. /** * This constant is used for html attributes which need to have an empty * value and still be output by the renderers (e.g. alt=""); * * @constant @EMPTY@ */ define('HTML_ATTR_EMPTY', '@EMPTY@'); require_once($CFG->libdir.'/outputcomponents.php'); require_once($CFG->libdir.'/outputactions.php'); require_once($CFG->libdir.'/outputfactories.php'); require_once($CFG->libdir.'/outputrenderers.php'); /** * Functions for generating the HTML that Moodle should output. * * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML * for an overview. * * @package moodlecore * @copyright 2009 Tim Hunt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ /** * This class represents the configuration variables of a Moodle theme. * * All the variables with access: public below (with a few exceptions that are marked) * are the properties you can set in your theme's config.php file. * * There are also some methods and protected variables that are part of the inner * workings of Moodle's themes system. If you are just editing a theme's config.php * file, you can just ignore those, and the following information for developers. * * 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. * However, normally you don't need to bother, because moodle_page (that is, $PAGE) * will create one for you, accessible as $PAGE->theme. * * @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 string the name of this theme. Set automatically when this theme is * loaded. Please do not try to set this in your theme's config.php file. */ public $name; /** * @var string the folder where this themes files are stored. This is set * automatically when the theme is loaded to $CFG->themedir . '/' . $this->name. * Please do not try to set this in your theme's config.php file. */ public $dir; /** * @var array The names of all the stylesheets from this theme that you would * like included, in order. Give the names of the files without .css. */ public $sheets = array('styles_layout', 'styles_fonts', 'styles_color'); /** * You can base your theme on another theme by linking to the other theme as * a parent. This lets you use the CSS from the other theme * (see {@link $parentsheets}), or layout templates (see {@link $layouts}). * That makes it easy to create a new theme that is similar to another one * but with a few changes. In this theme's CSS you only need to override * those rules you want to change. */ public $parent = null; /** * @var boolean|array Whether and which stylesheets from the parent theme * to use in this theme. (Ignored if parent is null) * * Possible values are: * false - no parent theme CSS. * true - include all the normal parent theme CSS. Currently this means * array('styles_layout', 'styles_fonts', 'styles_color'). * array - include just the listed stylesheets. Give the files names * without the .css, as in the above example. */ public $parentsheets = false; /** * @var boolean|array Whether and which stylesheets from the standard theme * to use in this theme. * * The advantages of using the standard stylesheets in your theme is that * they give you a good basic layout, and when the Moodle core code is * updated with new features, the standard theme CSS will be updated to match * the changes in the code. Therefore, your theme is less likely to break * when you upgrade Moodle. * * Possible values are: * false - no standard theme CSS. * true - include all the main standard theme CSS. Currently this means * array('styles_layout', 'styles_fonts', 'styles_color'). * array - include just the listed stylesheets. Give the files names * without the .css, as in the above example. */ public $standardsheets = true; /** * @var array use the CSS fragments from these types of plugins. * * All the plugins of the given types will be searched for a file called * styles.php and, if found, these will be included with the CSS for this theme. * * This allows modules to provide some basic CSS so they work out of the box. * You are strongly advised to leave this enabled, otherwise you will have to * provide styling in your theme for every installed block, activity, course * format, ... in your Moodle site. * * This setting is an array of plugin types, as in the {@link get_plugin_types()} * function. The default value has been chosen to be the same as Moodle 1.9. * This is not necessarily the best choice. * * The plugin CSS is included first, before any theme CSS. To be precise, * if $standardsheets is true, the plugin CSS is included with the * standard theme's CSS, otherwise if $parentsheets is true, the plugin CSS * will be included with the parent theme's CSS, otherwise the plugin CSS * will be include with this theme's CSS. */ public $pluginsheets = array('mod', 'block', 'format', 'gradereport'); /** * @var boolean When this is true then Moodle will try to include a file * meta.php from this theme into the part of the page. */ public $metainclude = false; /** * @var boolean When this is true, and when this theme has a parent, then * Moodle will try to include a file meta.php from the parent theme into the * part of the page. */ public $parentmetainclude = false; /** * @var boolean When this is true then Moodle will try to include the file * meta.php from the standard theme into the part of the page. */ public $standardmetainclude = true; /** * 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. * * @var boolean */ public $custompix = false; /** * Which template to use for each general type of page. * * This is an array of arrays. The keys of the outer array are the different * types of page. Pages in Moodle are categorised into one of a short list of * types like 'normal', 'home', 'popup', 'form', .... The most reliable way * to get a complete list is to look at * {@link http://cvs.moodle.org/moodle/theme/standard/config.php?view=markup the standard theme config.php file}. * That file also has a good example of how to set this setting. * * If Moodle encounters a general type of page that is not listed in your theme, * then it will use the first layout. Therefore, should probably put 'normal' * first in this array. * * For each page type, the value in the outer array is an array that describes * how you want that type of page to look. For example *
     *   $THEME->layouts = array(
     *       // Most pages. Put this first, so if we encounter an unknown page type, this is used.
     *       'normal' => array(
     *           'layout' => 'parent:layout.php',
     *           'regions' => array('side-pre', 'side-post'),
     *           'defaultregion' => 'side-post'
     *       ),
     *       // The site home page.
     *       'home' => array(
     *           'layout' => 'layout-home.php',
     *           'regions' => array('side-pre', 'side-post'),
     *           'defaultregion' => 'side-post'
     *       ),
     *       // ...
     *   );
     * 
* * 'layout' is the layout template to use for this type of page. You can * specify this in one of three ways: *
    *
  1. filename for example 'layout-home.php' as above. Use that file from this theme.
  2. *
  3. parent:filename for example 'parent:layout.php' as above. Use the * specified file from the parent theme. (Obviously, you can only do this * if this theme has a parent!)
  4. *
  5. standard:filename for example 'standard:layout-popup.php'. Use * the specified file from the standard theme.
  6. *
* To promote consistency, you are encouraged to call your layout files * layout.php or layout-something.php. * * 'regions' This lists the regions on the page where blocks may appear. For * each region you list here, your layout file must include a call to *
     *   echo $OUTPUT->blocks_for_region($regionname);
     * 
* or equivalent so that the blocks are actually visible. * * 'defaultregion' If the list of regions is non-empty, then you must pick * one of the one of them as 'default'. This has two meanings. First, this is * where new blocks are added. Second, if there are any blocks associated with * the page, but in non-existent regions, they appear here. (Imaging, for example, * that someone added blocks using a different theme that used different region * names, and then switched to this theme.) * * @var array */ public $layouts = array(); /* * Time in seconds to cache the CSS style sheets for the chosen theme * * @var integer */ public $csslifetime = 1800; /** * With this you can control the colours of the big MP3 player * that is used for MP3 resources. * * @var string */ 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 small MP3 player * that is used elsewhere *. * @var string */ public $filter_mediaplugin_colors = 'bgColour=000000&btnColour=ffffff&btnBorderColour=cccccc&iconColour=000000&iconOverColour=00cc00&trackColour=cccccc&handleColour=ffffff&loaderColour=ffffff&waitForPlay=yes'; /** *$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. */ /** * Name of the renderer factory class to use. * * This is an advanced feature. Moodle output is generated by 'renderers', * you can customise the HTML that is output by writing custom renderers, * and then you need to specify 'renderer factory' so that Moodle can find * your renderers. * * There are some renderer factories supplied with Moodle. Please follow these * links to see what they do. * * * @var string name of a class implementing the {@link renderer_factory} interface. */ public $rendererfactory = 'standard_renderer_factory'; /** * Name of the icon finder class to use. * * This is an advanced feature. controls how Moodle converts from the icon * names used in the code to URLs to embed in the HTML. You should not ever * need to change this. * * @var string name of a class implementing the {@link icon_finder} interface. */ public $iconfinder = 'pix_icon_finder'; /** * Function to do custom CSS processing. * * This is an advanced feature. 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 available that you may wish to use (defined in lib/outputlib.php): * * * If you wish to write your own function, look at those two as examples, * and it should be clear what you have to do. * * @var string the name of a function. */ public $customcssoutputfunction = null; /** * 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 $navmenuwidth = 50; /** * 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 $makenavmenulist = false; /** * @var renderer_factory Instance of the renderer_factory implementation * we are using. Implementation detail. */ protected $rf = null; /** * @var renderer_factory Instance of the icon_finder implementation we are * using. Implementation detail. */ protected $if = 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; // We have to use the variable name $THEME (upper case) because 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; } /** * Get the renderer for a part of Moodle for this theme. * @param string $module the name of part of moodle. E.g. 'core', 'quiz', 'qtype_multichoice'. * @param moodle_page $page the page we are rendering * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news' * @return moodle_renderer_base the requested renderer. */ public function get_renderer($module, $page, $subtype=null) { if (is_null($this->rf)) { if (CLI_SCRIPT) { $classname = 'cli_renderer_factory'; } else { $classname = $this->rendererfactory; } $this->rf = new $classname($this); } return $this->rf->get_renderer($module, $page, $subtype); } /** * Get the renderer for a part of Moodle for this theme. * @return moodle_renderer_base the requested renderer. */ protected function get_icon_finder() { if (is_null($this->if)) { $classname = $this->iconfinder; $this->if = new $classname($this); } return $this->if; } /** * Return the URL for an icon identified as in pre-Moodle 2.0 code. * * Suppose you have old code like $url = "$CFG->pixpath/i/course.gif"; * then old_icon_url('i/course'); will return the equivalent URL that is correct now. * * @param string $iconname the name of the icon. * @return string the URL for that icon. */ public function old_icon_url($iconname) { return $this->get_icon_finder()->old_icon_url($iconname); } /** * Return the URL for an icon identified as in pre-Moodle 2.0 code. * * Suppose you have old code like $url = "$CFG->modpixpath/$mod/icon.gif"; * then mod_icon_url('icon', $mod); will return the equivalent URL that is correct now. * * @param string $iconname the name of the icon. * @param string $module the module the icon belongs to. * @return string the URL for that icon. */ public function mod_icon_url($iconname, $module) { return $this->get_icon_finder()->mod_icon_url($iconname, $module); } /** * 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; // We need to tell the CSS that is being included (for example the standard // theme CSS) which theme it is being included for. Prepare the necessary param. $param = '?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' . $param; } if (!empty($this->parent)) { $stylesheets[] = $CFG->httpsthemewww . '/' . $this->parent . '/styles.php' . $param; } $stylesheets[] = $CFG->httpsthemewww . '/' . $this->name . '/styles.php' . $param; // Additional styles for right-to-left languages, if applicable. 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'; } } // If the theme wants pluginsheets, get them included in the first (most // general) stylesheet we are including. That is, process them with the // standard CSS if we are using that, else with the parent CSS, else with // our own CSS. if (!empty($this->pluginsheets)) { $stylesheets[0] .= '&pluginsheets=1'; } return $stylesheets; } /** * Get the meta tags from one theme to got in the of the HTML. * @param string $themename the name of the theme to get meta tags from. * @param string $page that page whose is being output. * @return string HTML code. */ protected function get_theme_meta_tags($themename, $page) { global $CFG; // At least one theme's meta.php expects to have $PAGE visible. $PAGE = $page; $filename = $CFG->themedir . '/' . $themename . '/meta.php'; if (file_exists($filename)) { ob_start(); include_once($filename); $metatags = ob_get_contents(); ob_end_clean(); } return $metatags; } /** * Get all the meta tags (from this theme, standard, parent) that this theme * wants in the of the HTML. * * @param string $page that page whose is being output. * @return string HTML code. */ public function get_meta_tags($page) { $metatags = ''; if ($this->standardmetainclude) { $metatags .= $this->get_theme_meta_tags('standard', $page); } if ($this->parent && $this->parentmetainclude) { $metatags .= $this->get_theme_meta_tags($this->parent, $page); } if ($this->metainclude) { $metatags .= $this->get_theme_meta_tags($this->name, $page); } return $metatags; } /** * Get the information from {@link $layouts} for this type of page. * @param string $generaltype the general type of the page. * @return array the appropriate part of {@link $layouts}. */ protected function layout_info_for_page($generaltype) { if (array_key_exists($generaltype, $this->layouts)) { return $this->layouts[$generaltype]; } else { return reset($this->layouts); } } /** * Given the settings of this theme, and the page generaltype, return the * full path of the page layout template to use. * * Used by {@link moodle_core_renderer::header()}. If an appropriate new-style * template cannot be found, returns false to signal that the old-style * header.html and footer.html files should be used. * * @param string $generaltype the general type of the page. * @return string Full path to the template to use, or false if a new-style * template cannot be found. */ public function template_for_page($generaltype) { global $CFG; // Legacy fallback. if (empty($this->layouts)) { return false; } $layoutinfo = $this->layout_info_for_page($generaltype); $templatefile = $layoutinfo['layout']; // Parse the name that was found. if (strpos($templatefile, 'standard:') === 0) { $templatepath = $CFG->themedir . '/standard/' . substr($templatefile, 9); } else if (strpos($templatefile, 'parent:') === 0) { if (empty($this->parent)) { throw new coding_exception('This theme (' . $this->name . ') does not have a parent. You cannot specify a layout template like ' . $templatefile); } $templatepath = $CFG->themedir . '/' . $this->parent . '/' . substr($templatefile, 7); } else { $templatepath = $this->dir . '/' . $templatefile; } // Check the template exists. if (!is_readable($templatepath)) { throw new coding_exception('The template ' . $templatefile . ' (' . $templatepath . ') for page type ' . $generaltype . ' cannot be found in this theme (' . $this->name . ')'); } return $templatepath; } /** * Inform a block_manager about the block regions this theme wants on this * type of page. * @param string $generaltype the general type of the page. * @param block_manager $blockmanager the block_manger to set up. * @return void */ public function setup_blocks($generaltype, $blockmanager) { // Legacy fallback. if (empty($this->layouts)) { if (!in_array($generaltype, array('form', 'popup', 'maintenance'))) { $blockmanager->add_regions(array(BLOCK_POS_LEFT, BLOCK_POS_RIGHT)); $blockmanager->set_default_region(BLOCK_POS_RIGHT); } return; } $layoutinfo = $this->layout_info_for_page($generaltype); if (!empty($layoutinfo['regions'])) { $blockmanager->add_regions($layoutinfo['regions']); $blockmanager->set_default_region($layoutinfo['defaultregion']); } } /** * Get the list of all block regions known to this theme in all templates. * @return array internal region name => human readable name. */ public function get_all_block_regions() { // Legacy fallback. if (empty($this->layouts)) { return array( 'side-pre' => get_string('region-side-pre', 'theme_standard'), 'side-post' => get_string('region-side-post', 'theme_standard'), ); } $regions = array(); foreach ($this->layouts as $layoutinfo) { $ownertheme = $this->name; if (strpos($layoutinfo['layout'], 'standard:') === 0) { $ownertheme = 'standard'; } else if (strpos($layoutinfo['layout'], 'parent:') === 0) { $ownertheme = $this->parent; } foreach ($layoutinfo['regions'] as $region) { $regions[$region] = get_string('region-' . $region, 'theme_' . $ownertheme); } } return $regions; } /** * Helper method used by {@link update_legacy_information()}. Update one entry * in the $this->pluginsheets array, based on the legacy $property propery. * @param string $plugintype e.g. 'mod'. * @param string $property e.g. 'modsheets'. * @return void */ protected function update_legacy_plugin_sheets($plugintype, $property) { // In Moodle 1.9, modsheets etc. were ignored if standardsheets was false. if (!empty($this->standardsheets) && property_exists($this, $property)) { debugging('$THEME->' . $property . ' is deprecated. Please use the new $THEME->pluginsheets instead.', DEBUG_DEVELOPER); if (!empty($this->$property) && !in_array($plugintype, $this->pluginsheets)) { $this->pluginsheets[] = $plugintype; } else if (empty($this->$property) && in_array($plugintype, $this->pluginsheets)) { unset($this->pluginsheets[array_search($plugintype, $this->pluginsheets)]); } } } /** * This method 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. * @return void */ protected function update_legacy_information() { global $CFG; $this->update_legacy_plugin_sheets('mod', 'modsheets'); $this->update_legacy_plugin_sheets('block', 'blocksheets'); $this->update_legacy_plugin_sheets('format', 'formatsheets'); $this->update_legacy_plugin_sheets('gradereport', 'gradereportsheets'); if (!empty($this->langsheets)) { debugging('$THEME->langsheets is no longer supported. No languages were ' . 'using it for anything, and it did not seem to serve any purpose.', DEBUG_DEVELOPER); } 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'; } if (!empty($CFG->smartpix)) { $this->iconfinder = 'smartpix_icon_finder'; } else if ($this->custompix) { $this->iconfinder = 'theme_icon_finder'; } } /** * Set the variable $CFG->pixpath and $CFG->modpixpath to be the right * ones for this theme. These should no longer be used, but legacy code * might still rely on them. * @return void */ public function setup_legacy_pix_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'; } } } /** * This class keeps track of which HTML tags are currently open. * * This makes it much easier to always generate well formed XHTML output, even * if execution terminates abruptly. Any time you output some opening HTML * without the matching closing HTML, you should push the necessary close tags * onto the stack. * * @copyright 2009 Tim Hunt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 */ class xhtml_container_stack { /** @var array stores the list of open containers. */ protected $opencontainers = array(); /** * @var array in developer debug mode, stores a stack trace of all opens and * closes, so we can output helpful error messages when there is a mismatch. */ protected $log = array(); /** * Store whether we are developer debug mode. We need this in several places * including in the destructor where we may no thave access to $CFG. * @var boolean */ protected $isdebugging; public function __construct() { $this->isdebugging = debugging('', DEBUG_DEVELOPER); } /** * Push the close HTML for a recently opened container onto the stack. * @param string $type The type of container. This is checked when {@link pop()} * is called and must match, otherwise a developer debug warning is output. * @param string $closehtml The HTML required to close the container. * @return void */ public function push($type, $closehtml) { $container = new stdClass; $container->type = $type; $container->closehtml = $closehtml; if ($this->isdebugging) { $this->log('Open', $type); } array_push($this->opencontainers, $container); } /** * Pop the HTML for the next closing container from the stack. The $type * must match the type passed when the container was opened, otherwise a * warning will be output. * @param string $type The type of container. * @return string the HTML required to close the container. */ public function pop($type) { if (empty($this->opencontainers)) { debugging('

There are no more open containers. This suggests there is a nesting problem.

' . $this->output_log(), DEBUG_DEVELOPER); return; } $container = array_pop($this->opencontainers); if ($container->type != $type) { debugging('

The type of container to be closed (' . $container->type . ') does not match the type of the next open container (' . $type . '). This suggests there is a nesting problem.

' . $this->output_log(), DEBUG_DEVELOPER); } if ($this->isdebugging) { $this->log('Close', $type); } return $container->closehtml; } /** * Close all but the last open container. This is useful in places like error * handling, where you want to close all the open containers (apart from ) * before outputting the error message. * @param bool $shouldbenone assert that the stack should be empty now - causes a * developer debug warning if it isn't. * @return string the HTML required to close any open containers inside . */ public function pop_all_but_last($shouldbenone = false) { if ($shouldbenone && count($this->opencontainers) != 1) { debugging('

Some HTML tags were opened in the body of the page but not closed.

' . $this->output_log(), DEBUG_DEVELOPER); } $output = ''; while (count($this->opencontainers) > 1) { $container = array_pop($this->opencontainers); $output .= $container->closehtml; } return $output; } /** * You can call this function if you want to throw away an instance of this * class without properly emptying the stack (for example, in a unit test). * Calling this method stops the destruct method from outputting a developer * debug warning. After calling this method, the instance can no longer be used. * @return void */ public function discard() { $this->opencontainers = null; } /** * Emergency fallback. If we get to the end of processing and not all * containers have been closed, output the rest with a developer debug warning. * @return void */ public function __destruct() { if (empty($this->opencontainers)) { return; } // It seems you cannot rely on $CFG, and hence the debugging function here, // becuase $CFG may be destroyed before this object is. if ($this->isdebugging) { echo '

Some containers were left open. This suggests there is a nesting problem.

' . $this->output_log() . '
'; } echo $this->pop_all_but_last(); $container = array_pop($this->opencontainers); echo $container->closehtml; } /** * Adds an entry to the log. * @param string $action The name of the action * @param string $type The type of action * @return void */ protected function log($action, $type) { $this->log[] = '
  • ' . $action . ' ' . $type . ' at:' . format_backtrace(debug_backtrace()) . '
  • '; } /** * Outputs the log's contents as a HTML list. * @return string HTML list of the log */ protected function output_log() { return ''; } } /** * An icon finder is responsible for working out the correct URL for an icon. * * A icon finder must also have a constructor that takes a theme object. * (See {@link standard_icon_finder::__construct} for an example.) * * Note that we are planning to change the Moodle icon naming convention before * the Moodle 2.0 release. Therefore, this API will probably change. * * @copyright 2009 Tim Hunt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 */ interface icon_finder { /** * Return the URL for an icon identified as in pre-Moodle 2.0 code. * * Suppose you have old code like $url = "$CFG->pixpath/i/course.gif"; * then old_icon_url('i/course'); will return the equivalent URL that is correct now. * * @param string $iconname the name of the icon. * @return string the URL for that icon. */ public function old_icon_url($iconname); /** * Return the URL for an icon identified as in pre-Moodle 2.0 code. * * Suppose you have old code like $url = "$CFG->modpixpath/$mod/icon.gif"; * then mod_icon_url('icon', $mod); will return the equivalent URL that is correct now. * * @param string $iconname the name of the icon. * @param string $module the module the icon belongs to. * @return string the URL for that icon. */ public function mod_icon_url($iconname, $module); } /** * This icon finder implements the old scheme that was used when themes that had * $THEME->custompix = false. * * @copyright 2009 Tim Hunt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 */ class pix_icon_finder implements icon_finder { /** * Constructor * @param theme_config $theme the theme we are finding icons for (which is irrelevant). */ public function __construct($theme) { } /** * Implement interface method. * @param string $iconname the name of the icon. * @return string the URL for that icon. */ public function old_icon_url($iconname) { global $CFG; if (file_exists($CFG->dirroot . '/pix/' . $iconname . '.png')) { return $CFG->httpswwwroot . '/pix/' . $iconname . '.png'; } else { return $CFG->httpswwwroot . '/pix/' . $iconname . '.gif'; } } /** * Implement interface method. * @param string $iconname the name of the icon. * @param string $module the module the icon belongs to. * @return string the URL for that icon. */ public function mod_icon_url($iconname, $module) { global $CFG; if (file_exists($CFG->dirroot . '/mod/' . $module . '/' . $iconname . '.png')) { return $CFG->httpswwwroot . '/mod/' . $module . '/' . $iconname . '.png'; } else { return $CFG->httpswwwroot . '/mod/' . $module . '/' . $iconname . '.gif'; } } } /** * This icon finder implements the old scheme that was used for themes that had * $THEME->custompix = true. * * @copyright 2009 Tim Hunt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 */ class theme_icon_finder implements icon_finder { protected $themename; /** * Constructor * @param theme_config $theme the theme we are finding icons for. */ public function __construct($theme) { $this->themename = $theme->name; } /** * Implement interface method. * @param string $iconname the name of the icon. * @return string the URL for that icon. */ public function old_icon_url($iconname) { global $CFG; if (file_exists($CFG->themedir . '/' . $this->themename . '/pix/' . $iconname . '.png')) { return $CFG->httpsthemewww . '/' . $this->themename . '/pix/' . $iconname . '.png'; } else { return $CFG->httpsthemewww . '/' . $this->themename . '/pix/' . $iconname . '.gif'; } } /** * Implement interface method. * @param string $iconname the name of the icon. * @param string $module the module the icon belongs to. * @return string the URL for that icon. */ public function mod_icon_url($iconname, $module) { global $CFG; if (file_exists($CFG->themedir . '/' . $this->themename . '/pix/mod/' . $module . '/' . $iconname . '.png')) { return $CFG->httpsthemewww . '/' . $this->themename . '/pix/mod/' . $module . '/' . $iconname . '.png'; } else { return $CFG->httpsthemewww . '/' . $this->themename . '/pix/mod/' . $module . '/' . $iconname . '.gif'; } } } /** * This icon finder implements the algorithm in pix/smartpix.php. * * @copyright 2009 Tim Hunt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 */ class smartpix_icon_finder extends pix_icon_finder { protected $places = array(); /** * Constructor * @param theme_config $theme the theme we are finding icons for. */ public function __construct($theme) { global $CFG; $this->places[$CFG->themedir . '/' . $theme->name . '/pix/'] = $CFG->httpsthemewww . '/' . $theme->name . '/pix/'; if (!empty($theme->parent)) { $this->places[$CFG->themedir . '/' . $theme->parent . '/pix/'] = $CFG->httpsthemewww . '/' . $theme->parent . '/pix/'; } } /** * Implement interface method. * @param string $iconname the name of the icon. * @return string the URL for that icon. */ public function old_icon_url($iconname) { foreach ($this->places as $dirroot => $urlroot) { if (file_exists($dirroot . $iconname . '.png')) { return $dirroot . $iconname . '.png'; } else if (file_exists($dirroot . $iconname . '.gif')) { return $dirroot . $iconname . '.gif'; } } return parent::old_icon_url($iconname); } /** * Implement interface method. * @param string $iconname the name of the icon. * @param string $module the module the icon belongs to. * @return string the URL for that icon. */ public function mod_icon_url($iconname, $module) { foreach ($this->places as $dirroot => $urlroot) { if (file_exists($dirroot . 'mod/' . $iconname . '.png')) { return $dirroot . 'mod/' . $iconname . '.png'; } else if (file_exists($dirroot . 'mod/' . $iconname . '.gif')) { return $dirroot . 'mod/' . $iconname . '.gif'; } } return parent::old_icon_url($iconname, $module); } } /** * 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 array of the CSS fields that need to be output. * @param array $toreplace for convenience. If you are going to output the names * of the css files, for debugging purposes, then you should output * str_replace($toreplace, '', $file); because it looks prettier. * @return void */ function output_css_replacing_constants($files, $toreplace) { // Get all the CSS. 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 array of the CSS fields that need to be output. * @param array $toreplace for convenience. If you are going to output the names * of the css files, for debugging purposes, then you should output * str_replace($toreplace, '', $file); because it looks prettier. * @return void */ function output_css_for_css_edit($files, $toreplace) { 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"; } }