Updated the HEAD build version to 20090813
[moodle.git] / lib / outputlib.php
CommitLineData
571fa828 1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
f8065dd2 18/**
19 * This constant is used for html attributes which need to have an empty
20 * value and still be output by the renderers (e.g. alt="");
21 *
22 * @constant @EMPTY@
23 */
24define('HTML_ATTR_EMPTY', '@EMPTY@');
571fa828 25
d9c8f425 26require_once($CFG->libdir.'/outputcomponents.php');
27require_once($CFG->libdir.'/outputactions.php');
28require_once($CFG->libdir.'/outputfactories.php');
29require_once($CFG->libdir.'/outputpixfinders.php');
30require_once($CFG->libdir.'/outputrenderers.php');
31
571fa828 32/**
33 * Functions for generating the HTML that Moodle should output.
34 *
35 * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML
36 * for an overview.
37 *
38 * @package moodlecore
39 * @copyright 2009 Tim Hunt
b7009474 40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
571fa828 41 */
42
571fa828 43/**
fdeb7fa1 44 * This class represents the configuration variables of a Moodle theme.
45 *
46 * All the variables with access: public below (with a few exceptions that are marked)
47 * are the properties you can set in your theme's config.php file.
48 *
49 * There are also some methods and protected variables that are part of the inner
50 * workings of Moodle's themes system. If you are just editing a theme's config.php
fa1afe32 51 * file, you can just ignore those, and the following information for developers.
ebebf55c 52 *
53 * Normally, to create an instance of this class, you should use the
54 * {@link theme_config::load()} factory method to load a themes config.php file.
fa1afe32 55 * However, normally you don't need to bother, because moodle_page (that is, $PAGE)
fdeb7fa1 56 * will create one for you, accessible as $PAGE->theme.
571fa828 57 *
58 * @copyright 2009 Tim Hunt
59 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
60 * @since Moodle 2.0
61 */
ebebf55c 62class theme_config {
63 /**
fdeb7fa1 64 * @var string the name of this theme. Set automatically when this theme is
65 * loaded. Please do not try to set this in your theme's config.php file.
ebebf55c 66 */
fdeb7fa1 67 public $name;
571fa828 68
fdeb7fa1 69 /**
70 * @var string the folder where this themes files are stored. This is set
71 * automatically when the theme is loaded to $CFG->themedir . '/' . $this->name.
72 * Please do not try to set this in your theme's config.php file.
73 */
74 public $dir;
8954245a 75
fdeb7fa1 76 /**
77 * @var array The names of all the stylesheets from this theme that you would
78 * like included, in order. Give the names of the files without .css.
79 */
80 public $sheets = array('styles_layout', 'styles_fonts', 'styles_color');
b7009474 81
fdeb7fa1 82 /**
83 * You can base your theme on another theme by linking to the other theme as
84 * a parent. This lets you use the CSS from the other theme
d4a03c00 85 * (see {@link $parentsheets}), or layout templates (see {@link $layouts}).
fdeb7fa1 86 * That makes it easy to create a new theme that is similar to another one
87 * but with a few changes. In this theme's CSS you only need to override
88 * those rules you want to change.
89 */
90 public $parent = null;
8954245a 91
fdeb7fa1 92 /**
93 * @var boolean|array Whether and which stylesheets from the parent theme
94 * to use in this theme. (Ignored if parent is null)
95 *
fa1afe32 96 * Possible values are:
fdeb7fa1 97 * false - no parent theme CSS.
98 * true - include all the normal parent theme CSS. Currently this means
99 * array('styles_layout', 'styles_fonts', 'styles_color').
100 * array - include just the listed stylesheets. Give the files names
101 * without the .css, as in the above example.
102 */
103 public $parentsheets = false;
571fa828 104
fdeb7fa1 105 /**
106 * @var boolean|array Whether and which stylesheets from the standard theme
107 * to use in this theme.
108 *
109 * The advantages of using the standard stylesheets in your theme is that
110 * they give you a good basic layout, and when the Moodle core code is
111 * updated with new features, the standard theme CSS will be updated to match
112 * the changes in the code. Therefore, your theme is less likely to break
113 * when you upgrade Moodle.
114 *
fa1afe32 115 * Possible values are:
fdeb7fa1 116 * false - no standard theme CSS.
117 * true - include all the main standard theme CSS. Currently this means
118 * array('styles_layout', 'styles_fonts', 'styles_color').
119 * array - include just the listed stylesheets. Give the files names
120 * without the .css, as in the above example.
121 */
122 public $standardsheets = true;
571fa828 123
fdeb7fa1 124 /**
125 * @var array use the CSS fragments from these types of plugins.
126 *
127 * All the plugins of the given types will be searched for a file called
128 * styles.php and, if found, these will be included with the CSS for this theme.
129 *
130 * This allows modules to provide some basic CSS so they work out of the box.
131 * You are strongly advised to leave this enabled, otherwise you will have to
132 * provide styling in your theme for every installed block, activity, course
133 * format, ... in your Moodle site.
134 *
135 * This setting is an array of plugin types, as in the {@link get_plugin_types()}
136 * function. The default value has been chosen to be the same as Moodle 1.9.
137 * This is not necessarily the best choice.
138 *
139 * The plugin CSS is included first, before any theme CSS. To be precise,
fa1afe32 140 * if $standardsheets is true, the plugin CSS is included with the
fdeb7fa1 141 * standard theme's CSS, otherwise if $parentsheets is true, the plugin CSS
142 * will be included with the parent theme's CSS, otherwise the plugin CSS
143 * will be include with this theme's CSS.
144 */
145 public $pluginsheets = array('mod', 'block', 'format', 'gradereport');
571fa828 146
fdeb7fa1 147 /**
148 * @var boolean When this is true then Moodle will try to include a file
149 * meta.php from this theme into the <head></head> part of the page.
150 */
ebebf55c 151 public $metainclude = false;
571fa828 152
fdeb7fa1 153 /**
154 * @var boolean When this is true, and when this theme has a parent, then
155 * Moodle will try to include a file meta.php from the parent theme into the
156 * <head></head> part of the page.
157 */
ebebf55c 158 public $parentmetainclude = false;
571fa828 159
fdeb7fa1 160 /**
161 * @var boolean When this is true then Moodle will try to include the file
162 * meta.php from the standard theme into the <head></head> part of the page.
163 */
164 public $standardmetainclude = true;
571fa828 165
fdeb7fa1 166 /**
167 * If true, then this theme must have a "pix" subdirectory that contains
168 * copies of all files from the moodle/pix directory, plus a "pix/mod"
169 * directory containing all the icons for all the activity modules.
170 *
171 * @var boolean
172 */
173 public $custompix = false;
571fa828 174
fdeb7fa1 175 /**
176 * Which template to use for each general type of page.
177 *
d4a03c00 178 * This is an array of arrays. The keys of the outer array are the different
179 * types of page. Pages in Moodle are categorised into one of a short list of
180 * types like 'normal', 'home', 'popup', 'form', .... The most reliable way
181 * to get a complete list is to look at
182 * {@link http://cvs.moodle.org/moodle/theme/standard/config.php?view=markup the standard theme config.php file}.
183 * That file also has a good example of how to set this setting.
fdeb7fa1 184 *
fa1afe32 185 * If Moodle encounters a general type of page that is not listed in your theme,
d4a03c00 186 * then it will use the first layout. Therefore, should probably put 'normal'
187 * first in this array.
fdeb7fa1 188 *
d4a03c00 189 * For each page type, the value in the outer array is an array that describes
190 * how you want that type of page to look. For example
191 * <pre>
192 * $THEME->layouts = array(
193 * // Most pages. Put this first, so if we encounter an unknown page type, this is used.
194 * 'normal' => array(
195 * 'layout' => 'parent:layout.php',
196 * 'regions' => array('side-pre', 'side-post'),
197 * 'defaultregion' => 'side-post'
198 * ),
199 * // The site home page.
200 * 'home' => array(
201 * 'layout' => 'layout-home.php',
202 * 'regions' => array('side-pre', 'side-post'),
203 * 'defaultregion' => 'side-post'
204 * ),
205 * // ...
206 * );
207 * </pre>
fdeb7fa1 208 *
d4a03c00 209 * 'layout' is the layout template to use for this type of page. You can
210 * specify this in one of three ways:
fdeb7fa1 211 * <ol>
d4a03c00 212 * <li><b>filename</b> for example 'layout-home.php' as above. Use that file from this theme.</li>
213 * <li><b>parent:filename</b> for example 'parent:layout.php' as above. Use the
fdeb7fa1 214 * specified file from the parent theme. (Obviously, you can only do this
215 * if this theme has a parent!)</li>
216 * <li><b>standard:filename</b> for example 'standard:layout-popup.php'. Use
217 * the specified file from the standard theme.</li>
218 * </ol>
fa1afe32 219 * To promote consistency, you are encouraged to call your layout files
d4a03c00 220 * layout.php or layout-something.php.
fdeb7fa1 221 *
d4a03c00 222 * 'regions' This lists the regions on the page where blocks may appear. For
223 * each region you list here, your layout file must include a call to
224 * <pre>
225 * echo $OUTPUT->blocks_for_region($regionname);
226 * </pre>
227 * or equivalent so that the blocks are actually visible.
fdeb7fa1 228 *
d4a03c00 229 * 'defaultregion' If the list of regions is non-empty, then you must pick
230 * one of the one of them as 'default'. This has two meanings. First, this is
231 * where new blocks are added. Second, if there are any blocks associated with
fa1afe32 232 * the page, but in non-existent regions, they appear here. (Imaging, for example,
d4a03c00 233 * that someone added blocks using a different theme that used different region
234 * names, and then switched to this theme.)
fdeb7fa1 235 *
236 * @var array
237 */
d4a03c00 238 public $layouts = array();
571fa828 239
74623e0a 240 /*
40427883 241 * Time in seconds to cache the CSS style sheets for the chosen theme
74623e0a 242 *
40427883 243 * @var integer
244 */
245 public $csslifetime = 1800;
246
fdeb7fa1 247 /**
248 * With this you can control the colours of the big MP3 player
249 * that is used for MP3 resources.
250 *
251 * @var string
252 */
ebebf55c 253 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';
571fa828 254
fdeb7fa1 255 /**
256 * With this you can control the colours of the small MP3 player
257 * that is used elsewhere
258 *.
259 * @var string
260 */
ebebf55c 261 public $filter_mediaplugin_colors = 'bgColour=000000&btnColour=ffffff&btnBorderColour=cccccc&iconColour=000000&iconOverColour=00cc00&trackColour=cccccc&handleColour=ffffff&loaderColour=ffffff&waitForPlay=yes';
262
fdeb7fa1 263 /**
264 *$THEME->rarrow = '&#x25BA;' //OR '&rarr;';
265 *$THEME->larrow = '&#x25C4;' //OR '&larr;';
266 *$CFG->block_search_button = link_arrow_right(get_string('search'), $url='', $accesshide=true);
267 *
268 * Accessibility: Right and left arrow-like characters are
269 * used in the breadcrumb trail, course navigation menu
270 * (previous/next activity), calendar, and search forum block.
271 *
272 * If the theme does not set characters, appropriate defaults
273 * are set by (lib/weblib.php:check_theme_arrows). The suggestions
274 * above are 'silent' in a screen-reader like JAWS. Please DO NOT
275 * use &lt; &gt; &raquo; - these are confusing for blind users.
276 */
ebebf55c 277
fdeb7fa1 278 /**
279 * Name of the renderer factory class to use.
280 *
281 * This is an advanced feature. Moodle output is generated by 'renderers',
282 * you can customise the HTML that is output by writing custom renderers,
283 * and then you need to specify 'renderer factory' so that Moodle can find
284 * your renderers.
285 *
286 * There are some renderer factories supplied with Moodle. Please follow these
287 * links to see what they do.
288 * <ul>
289 * <li>{@link standard_renderer_factory} - the default.</li>
290 * <li>{@link theme_overridden_renderer_factory} - use this if you want to write
291 * your own custom renderers in a renderers.php file in this theme (or the parent theme).</li>
292 * <li>{@link template_renderer_factory} - highly experimental! Do not use (yet).</li>
293 * </ul>
294 *
295 * @var string name of a class implementing the {@link renderer_factory} interface.
296 */
ebebf55c 297 public $rendererfactory = 'standard_renderer_factory';
ebebf55c 298
fdeb7fa1 299 /**
300 * Name of the icon finder class to use.
301 *
302 * This is an advanced feature. controls how Moodle converts from the icon
303 * names used in the code to URLs to embed in the HTML. You should not ever
304 * need to change this.
305 *
306 * @var string name of a class implementing the {@link icon_finder} interface.
307 */
ebebf55c 308 public $iconfinder = 'pix_icon_finder';
571fa828 309
310 /**
fdeb7fa1 311 * Function to do custom CSS processing.
312 *
313 * This is an advanced feature. If you want to do custom processing on the
314 * CSS before it is output (for example, to replace certain variable names
315 * with particular values) you can give the name of a function here.
ebebf55c 316 *
fa1afe32 317 * There are two functions available that you may wish to use (defined in lib/outputlib.php):
fdeb7fa1 318 * <ul>
319 * <li>{@link output_css_replacing_constants}</li>
320 * <li>{@link output_css_for_css_edit}</li>
321 * </ul>
322 *
323 * If you wish to write your own function, look at those two as examples,
324 * and it should be clear what you have to do.
ebebf55c 325 *
326 * @var string the name of a function.
571fa828 327 */
ebebf55c 328 public $customcssoutputfunction = null;
571fa828 329
fdeb7fa1 330 /**
331 * You can use this to control the cutoff point for strings
332 * in the navmenus (list of activities in popup menu etc)
333 * Default is 50 characters wide.
334 */
335 public $navmenuwidth = 50;
336
337 /**
338 * By setting this to true, then you will have access to a
339 * new variable in your header.html and footer.html called
340 * $navmenulist ... this contains a simple XHTML menu of
341 * all activities in the current course, mostly useful for
342 * creating popup navigation menus and so on.
343 */
344 public $makenavmenulist = false;
345
346 /**
347 * @var renderer_factory Instance of the renderer_factory implementation
348 * we are using. Implementation detail.
349 */
350 protected $rf = null;
351
352 /**
353 * @var renderer_factory Instance of the icon_finder implementation we are
354 * using. Implementation detail.
355 */
356 protected $if = null;
357
571fa828 358 /**
ebebf55c 359 * Load the config.php file for a particular theme, and return an instance
360 * of this class. (That is, this is a factory method.)
361 *
362 * @param string $themename the name of the theme.
363 * @return theme_config an instance of this class.
571fa828 364 */
ebebf55c 365 public static function load($themename) {
366 global $CFG;
571fa828 367
fa1afe32 368 // We have to use the variable name $THEME (upper case) because that
ebebf55c 369 // is what is used in theme config.php files.
370
371 // Set some other standard properties of the theme.
372 $THEME = new theme_config;
373 $THEME->name = $themename;
374 $THEME->dir = $CFG->themedir . '/' . $themename;
375
376 // Load up the theme config
377 $configfile = $THEME->dir . '/config.php';
378 if (!is_readable($configfile)) {
379 throw new coding_exception('Cannot use theme ' . $themename .
380 '. The file ' . $configfile . ' does not exist or is not readable.');
571fa828 381 }
ebebf55c 382 include($configfile);
383
384 $THEME->update_legacy_information();
385
386 return $THEME;
571fa828 387 }
388
34a2777c 389 /**
ebebf55c 390 * Get the renderer for a part of Moodle for this theme.
391 * @param string $module the name of part of moodle. E.g. 'core', 'quiz', 'qtype_multichoice'.
392 * @param moodle_page $page the page we are rendering
897b5c82 393 * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
fa1afe32 394 * @return moodle_renderer_base the requested renderer.
34a2777c 395 */
897b5c82 396 public function get_renderer($module, $page, $subtype=null) {
ebebf55c 397 if (is_null($this->rf)) {
398 if (CLI_SCRIPT) {
399 $classname = 'cli_renderer_factory';
400 } else {
401 $classname = $this->rendererfactory;
402 }
403 $this->rf = new $classname($this);
404 }
405
897b5c82 406 return $this->rf->get_renderer($module, $page, $subtype);
34a2777c 407 }
408
571fa828 409 /**
ebebf55c 410 * Get the renderer for a part of Moodle for this theme.
411 * @return moodle_renderer_base the requested renderer.
571fa828 412 */
ebebf55c 413 protected function get_icon_finder() {
414 if (is_null($this->if)) {
415 $classname = $this->iconfinder;
416 $this->if = new $classname($this);
571fa828 417 }
ebebf55c 418 return $this->if;
571fa828 419 }
420
421 /**
fa1afe32 422 * Return the URL for an icon identified as in pre-Moodle 2.0 code.
ebebf55c 423 *
424 * Suppose you have old code like $url = "$CFG->pixpath/i/course.gif";
425 * then old_icon_url('i/course'); will return the equivalent URL that is correct now.
426 *
fa1afe32 427 * @param string $iconname the name of the icon.
ebebf55c 428 * @return string the URL for that icon.
571fa828 429 */
ebebf55c 430 public function old_icon_url($iconname) {
4096752d 431 return $this->get_icon_finder()->old_icon_url($iconname);
571fa828 432 }
433
434 /**
fa1afe32 435 * Return the URL for an icon identified as in pre-Moodle 2.0 code.
ebebf55c 436 *
437 * Suppose you have old code like $url = "$CFG->modpixpath/$mod/icon.gif";
438 * then mod_icon_url('icon', $mod); will return the equivalent URL that is correct now.
439 *
fa1afe32 440 * @param string $iconname the name of the icon.
441 * @param string $module the module the icon belongs to.
ebebf55c 442 * @return string the URL for that icon.
571fa828 443 */
ebebf55c 444 public function mod_icon_url($iconname, $module) {
4096752d 445 return $this->get_icon_finder()->mod_icon_url($iconname, $module);
571fa828 446 }
34a2777c 447
ebebf55c 448 /**
449 * Get the list of stylesheet URLs that need to go in the header for this theme.
450 * @return array of URLs.
451 */
452 public function get_stylesheet_urls() {
34a2777c 453 global $CFG;
454
fdeb7fa1 455 // We need to tell the CSS that is being included (for example the standard
456 // theme CSS) which theme it is being included for. Prepare the necessary param.
457 $param = '?for=' . $this->name;
34a2777c 458
ebebf55c 459 // Stylesheets, in order (standard, parent, this - some of which may be the same).
460 $stylesheets = array();
461 if ($this->name != 'standard' && $this->standardsheets) {
fdeb7fa1 462 $stylesheets[] = $CFG->httpsthemewww . '/standard/styles.php' . $param;
ebebf55c 463 }
464 if (!empty($this->parent)) {
fdeb7fa1 465 $stylesheets[] = $CFG->httpsthemewww . '/' . $this->parent . '/styles.php' . $param;
34a2777c 466 }
fdeb7fa1 467 $stylesheets[] = $CFG->httpsthemewww . '/' . $this->name . '/styles.php' . $param;
34a2777c 468
fdeb7fa1 469 // Additional styles for right-to-left languages, if applicable.
ebebf55c 470 if (right_to_left()) {
471 $stylesheets[] = $CFG->httpsthemewww . '/standard/rtl.css';
34a2777c 472
ebebf55c 473 if (!empty($this->parent) && file_exists($CFG->themedir . '/' . $this->parent . '/rtl.css')) {
474 $stylesheets[] = $CFG->httpsthemewww . '/' . $this->parent . '/rtl.css';
475 }
34a2777c 476
ebebf55c 477 if (file_exists($this->dir . '/rtl.css')) {
478 $stylesheets[] = $CFG->httpsthemewww . '/' . $this->name . '/rtl.css';
479 }
34a2777c 480 }
17a6649b 481
fdeb7fa1 482 // If the theme wants pluginsheets, get them included in the first (most
483 // general) stylesheet we are including. That is, process them with the
484 // standard CSS if we are using that, else with the parent CSS, else with
485 // our own CSS.
486 if (!empty($this->pluginsheets)) {
487 $stylesheets[0] .= '&amp;pluginsheets=1';
488 }
489
ebebf55c 490 return $stylesheets;
491 }
17a6649b 492
ebebf55c 493 /**
fdeb7fa1 494 * Get the meta tags from one theme to got in the <head> of the HTML.
fa1afe32 495 * @param string $themename the name of the theme to get meta tags from.
496 * @param string $page that page whose <head> is being output.
fdeb7fa1 497 * @return string HTML code.
498 */
499 protected function get_theme_meta_tags($themename, $page) {
500 global $CFG;
501 // At least one theme's meta.php expects to have $PAGE visible.
502 $PAGE = $page;
503 $filename = $CFG->themedir . '/' . $themename . '/meta.php';
504 if (file_exists($filename)) {
505 ob_start();
506 include_once($filename);
f77fcb5a 507 $metatags = ob_get_contents();
fdeb7fa1 508 ob_end_clean();
509 }
510 return $metatags;
511 }
512
513 /**
514 * Get all the meta tags (from this theme, standard, parent) that this theme
515 * wants in the <head> of the HTML.
516 *
fa1afe32 517 * @param string $page that page whose <head> is being output.
fdeb7fa1 518 * @return string HTML code.
519 */
520 public function get_meta_tags($page) {
521 $metatags = '';
522 if ($this->standardmetainclude) {
523 $metatags .= $this->get_theme_meta_tags('standard', $page);
524 }
525 if ($this->parent && $this->parentmetainclude) {
526 $metatags .= $this->get_theme_meta_tags($this->parent, $page);
527 }
528 if ($this->metainclude) {
529 $metatags .= $this->get_theme_meta_tags($this->name, $page);
530 }
531 return $metatags;
532 }
533
d4a03c00 534 /**
535 * Get the information from {@link $layouts} for this type of page.
536 * @param string $generaltype the general type of the page.
537 * @return array the appropriate part of {@link $layouts}.
538 */
539 protected function layout_info_for_page($generaltype) {
540 if (array_key_exists($generaltype, $this->layouts)) {
541 return $this->layouts[$generaltype];
542 } else {
543 return reset($this->layouts);
544 }
545 }
546
fdeb7fa1 547 /**
548 * Given the settings of this theme, and the page generaltype, return the
549 * full path of the page layout template to use.
550 *
551 * Used by {@link moodle_core_renderer::header()}. If an appropriate new-style
552 * template cannot be found, returns false to signal that the old-style
553 * header.html and footer.html files should be used.
554 *
d4a03c00 555 * @param string $generaltype the general type of the page.
fdeb7fa1 556 * @return string Full path to the template to use, or false if a new-style
557 * template cannot be found.
558 */
d4a03c00 559 public function template_for_page($generaltype) {
fdeb7fa1 560 global $CFG;
561
562 // Legacy fallback.
d4a03c00 563 if (empty($this->layouts)) {
fdeb7fa1 564 return false;
565 }
566
d4a03c00 567 $layoutinfo = $this->layout_info_for_page($generaltype);
568 $templatefile = $layoutinfo['layout'];
fdeb7fa1 569
570 // Parse the name that was found.
d4a03c00 571 if (strpos($templatefile, 'standard:') === 0) {
fdeb7fa1 572 $templatepath = $CFG->themedir . '/standard/' . substr($templatefile, 9);
d4a03c00 573 } else if (strpos($templatefile, 'parent:') === 0) {
fdeb7fa1 574 if (empty($this->parent)) {
575 throw new coding_exception('This theme (' . $this->name .
576 ') does not have a parent. You cannot specify a layout template like ' .
577 $templatefile);
578 }
579 $templatepath = $CFG->themedir . '/' . $this->parent . '/' . substr($templatefile, 7);
580 } else {
581 $templatepath = $this->dir . '/' . $templatefile;
582 }
583
fa1afe32 584 // Check the template exists.
fdeb7fa1 585 if (!is_readable($templatepath)) {
586 throw new coding_exception('The template ' . $templatefile . ' (' . $templatepath .
587 ') for page type ' . $generaltype . ' cannot be found in this theme (' .
588 $this->name . ')');
589 }
590
591 return $templatepath;
592 }
593
d4a03c00 594 /**
595 * Inform a block_manager about the block regions this theme wants on this
596 * type of page.
597 * @param string $generaltype the general type of the page.
598 * @param block_manager $blockmanager the block_manger to set up.
fa1afe32 599 * @return void
d4a03c00 600 */
601 public function setup_blocks($generaltype, $blockmanager) {
602 // Legacy fallback.
603 if (empty($this->layouts)) {
604 if (!in_array($generaltype, array('form', 'popup', 'maintenance'))) {
605 $blockmanager->add_regions(array(BLOCK_POS_LEFT, BLOCK_POS_RIGHT));
606 $blockmanager->set_default_region(BLOCK_POS_RIGHT);
607 }
608 return;
609 }
610
611 $layoutinfo = $this->layout_info_for_page($generaltype);
612 if (!empty($layoutinfo['regions'])) {
613 $blockmanager->add_regions($layoutinfo['regions']);
614 $blockmanager->set_default_region($layoutinfo['defaultregion']);
615 }
616 }
617
1d13c75c 618 /**
619 * Get the list of all block regions known to this theme in all templates.
620 * @return array internal region name => human readable name.
621 */
622 public function get_all_block_regions() {
623 // Legacy fallback.
624 if (empty($this->layouts)) {
625 return array(
626 'side-pre' => get_string('region-side-pre', 'theme_standard'),
627 'side-post' => get_string('region-side-post', 'theme_standard'),
628 );
629 }
630
631 $regions = array();
632 foreach ($this->layouts as $layoutinfo) {
633 $ownertheme = $this->name;
634 if (strpos($layoutinfo['layout'], 'standard:') === 0) {
635 $ownertheme = 'standard';
636 } else if (strpos($layoutinfo['layout'], 'parent:') === 0) {
637 $ownertheme = $this->parent;
638 }
639
640 foreach ($layoutinfo['regions'] as $region) {
641 $regions[$region] = get_string('region-' . $region, 'theme_' . $ownertheme);
642 }
643 }
644 return $regions;
645 }
646
fdeb7fa1 647 /**
648 * Helper method used by {@link update_legacy_information()}. Update one entry
649 * in the $this->pluginsheets array, based on the legacy $property propery.
fa1afe32 650 * @param string $plugintype e.g. 'mod'.
651 * @param string $property e.g. 'modsheets'.
652 * @return void
fdeb7fa1 653 */
654 protected function update_legacy_plugin_sheets($plugintype, $property) {
d4a03c00 655 // In Moodle 1.9, modsheets etc. were ignored if standardsheets was false.
656 if (!empty($this->standardsheets) && property_exists($this, $property)) {
fdeb7fa1 657 debugging('$THEME->' . $property . ' is deprecated. Please use the new $THEME->pluginsheets instead.', DEBUG_DEVELOPER);
658 if (!empty($this->$property) && !in_array($plugintype, $this->pluginsheets)) {
659 $this->pluginsheets[] = $plugintype;
660 } else if (empty($this->$property) && in_array($plugintype, $this->pluginsheets)) {
661 unset($this->pluginsheets[array_search($plugintype, $this->pluginsheets)]);
662 }
663 }
664 }
665
666 /**
667 * This method looks a the settings that have been loaded, to see whether
ebebf55c 668 * any legacy things are being used, and outputs warning and tries to update
669 * things to use equivalent newer settings.
fa1afe32 670 * @return void
ebebf55c 671 */
672 protected function update_legacy_information() {
673 global $CFG;
fdeb7fa1 674
675 $this->update_legacy_plugin_sheets('mod', 'modsheets');
676 $this->update_legacy_plugin_sheets('block', 'blocksheets');
677 $this->update_legacy_plugin_sheets('format', 'formatsheets');
678 $this->update_legacy_plugin_sheets('gradereport', 'gradereportsheets');
679
fdeb7fa1 680 if (!empty($this->langsheets)) {
681 debugging('$THEME->langsheets is no longer supported. No languages were ' .
682 'using it for anything, and it did not seem to serve any purpose.', DEBUG_DEVELOPER);
683 }
684
ebebf55c 685 if (!empty($this->customcorners)) {
686 // $THEME->customcorners is deprecated but we provide support for it via the
687 // custom_corners_renderer_factory class in lib/deprecatedlib.php
688 debugging('$THEME->customcorners is deprecated. Please use the new $THEME->rendererfactory ' .
689 'to control HTML generation. Please use $this->rendererfactory = \'custom_corners_renderer_factory\'; ' .
690 'in your config.php file instead.', DEBUG_DEVELOPER);
691 $this->rendererfactory = 'custom_corners_renderer_factory';
34a2777c 692 }
693
ebebf55c 694 if (!empty($this->cssconstants)) {
695 debugging('$THEME->cssconstants is deprecated. Please use ' .
696 '$THEME->customcssoutputfunction = \'output_css_replacing_constants\'; ' .
697 'in your config.php file instead.', DEBUG_DEVELOPER);
698 $this->customcssoutputfunction = 'output_css_replacing_constants';
34a2777c 699 }
ebebf55c 700
701 if (!empty($this->CSSEdit)) {
702 debugging('$THEME->CSSEdit is deprecated. Please use ' .
703 '$THEME->customcssoutputfunction = \'output_css_for_css_edit\'; ' .
704 'in your config.php file instead.', DEBUG_DEVELOPER);
705 $this->customcssoutputfunction = 'output_css_for_css_edit';
34a2777c 706 }
707
ae96b517 708 if (!empty($CFG->smartpix)) {
ebebf55c 709 $this->iconfinder = 'smartpix_icon_finder';
710 } else if ($this->custompix) {
711 $this->iconfinder = 'theme_icon_finder';
712 }
34a2777c 713 }
714
ebebf55c 715 /**
716 * Set the variable $CFG->pixpath and $CFG->modpixpath to be the right
d436d197 717 * ones for this theme. These should no longer be used, but legacy code
718 * might still rely on them.
fa1afe32 719 * @return void
ebebf55c 720 */
d436d197 721 public function setup_legacy_pix_paths() {
ebebf55c 722 global $CFG;
723 if (!empty($CFG->smartpix)) {
724 if ($CFG->slasharguments) {
725 // Use this method if possible for better caching
726 $extra = '';
727 } else {
728 $extra = '?file=';
729 }
730 $CFG->pixpath = $CFG->httpswwwroot . '/pix/smartpix.php' . $extra . '/' . $this->name;
731 $CFG->modpixpath = $CFG->httpswwwroot . '/pix/smartpix.php' . $extra . '/' . $this->name . '/mod';
34a2777c 732
ebebf55c 733 } else if (empty($THEME->custompix)) {
734 $CFG->pixpath = $CFG->httpswwwroot . '/pix';
735 $CFG->modpixpath = $CFG->httpswwwroot . '/mod';
736
737 } else {
738 $CFG->pixpath = $CFG->httpsthemewww . '/' . $this->name . '/pix';
739 $CFG->modpixpath = $CFG->httpsthemewww . '/' . $this->name . '/pix/mod';
34a2777c 740 }
34a2777c 741 }
ebebf55c 742}
34a2777c 743
ebebf55c 744/**
d9c8f425 745 * This class keeps track of which HTML tags are currently open.
746 *
747 * This makes it much easier to always generate well formed XHTML output, even
748 * if execution terminates abruptly. Any time you output some opening HTML
749 * without the matching closing HTML, you should push the necessary close tags
750 * onto the stack.
ebebf55c 751 *
752 * @copyright 2009 Tim Hunt
753 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
754 * @since Moodle 2.0
755 */
d9c8f425 756class xhtml_container_stack {
757 /** @var array stores the list of open containers. */
758 protected $opencontainers = array();
fa1afe32 759 /**
d9c8f425 760 * @var array in developer debug mode, stores a stack trace of all opens and
761 * closes, so we can output helpful error messages when there is a mismatch.
fa1afe32 762 */
d9c8f425 763 protected $log = array();
fa1afe32 764 /**
d9c8f425 765 * Store whether we are developer debug mode. We need this in several places
766 * including in the destructor where we may no thave access to $CFG.
767 * @var boolean
fa1afe32 768 */
d9c8f425 769 protected $isdebugging;
34a2777c 770
d9c8f425 771 public function __construct() {
772 $this->isdebugging = debugging('', DEBUG_DEVELOPER);
ebebf55c 773 }
34a2777c 774
fa1afe32 775 /**
d9c8f425 776 * Push the close HTML for a recently opened container onto the stack.
777 * @param string $type The type of container. This is checked when {@link pop()}
778 * is called and must match, otherwise a developer debug warning is output.
779 * @param string $closehtml The HTML required to close the container.
780 * @return void
fa1afe32 781 */
d9c8f425 782 public function push($type, $closehtml) {
783 $container = new stdClass;
784 $container->type = $type;
785 $container->closehtml = $closehtml;
786 if ($this->isdebugging) {
787 $this->log('Open', $type);
3aaa27f4 788 }
d9c8f425 789 array_push($this->opencontainers, $container);
ebebf55c 790 }
34a2777c 791
fa1afe32 792 /**
d9c8f425 793 * Pop the HTML for the next closing container from the stack. The $type
794 * must match the type passed when the container was opened, otherwise a
795 * warning will be output.
796 * @param string $type The type of container.
797 * @return string the HTML required to close the container.
fa1afe32 798 */
d9c8f425 799 public function pop($type) {
800 if (empty($this->opencontainers)) {
801 debugging('<p>There are no more open containers. This suggests there is a nesting problem.</p>' .
802 $this->output_log(), DEBUG_DEVELOPER);
803 return;
3aaa27f4 804 }
ebebf55c 805
d9c8f425 806 $container = array_pop($this->opencontainers);
807 if ($container->type != $type) {
808 debugging('<p>The type of container to be closed (' . $container->type .
809 ') does not match the type of the next open container (' . $type .
810 '). This suggests there is a nesting problem.</p>' .
811 $this->output_log(), DEBUG_DEVELOPER);
ebebf55c 812 }
d9c8f425 813 if ($this->isdebugging) {
814 $this->log('Close', $type);
e8775320 815 }
d9c8f425 816 return $container->closehtml;
ebebf55c 817 }
e8775320 818
fa1afe32 819 /**
d9c8f425 820 * Close all but the last open container. This is useful in places like error
821 * handling, where you want to close all the open containers (apart from <body>)
822 * before outputting the error message.
823 * @param bool $shouldbenone assert that the stack should be empty now - causes a
824 * developer debug warning if it isn't.
825 * @return string the HTML required to close any open containers inside <body>.
fa1afe32 826 */
d9c8f425 827 public function pop_all_but_last($shouldbenone = false) {
828 if ($shouldbenone && count($this->opencontainers) != 1) {
829 debugging('<p>Some HTML tags were opened in the body of the page but not closed.</p>' .
830 $this->output_log(), DEBUG_DEVELOPER);
831 }
832 $output = '';
833 while (count($this->opencontainers) > 1) {
834 $container = array_pop($this->opencontainers);
835 $output .= $container->closehtml;
e8775320 836 }
d9c8f425 837 return $output;
e8775320 838 }
34a2777c 839
ebebf55c 840 /**
d9c8f425 841 * You can call this function if you want to throw away an instance of this
842 * class without properly emptying the stack (for example, in a unit test).
843 * Calling this method stops the destruct method from outputting a developer
844 * debug warning. After calling this method, the instance can no longer be used.
845 * @return void
ebebf55c 846 */
d9c8f425 847 public function discard() {
848 $this->opencontainers = null;
ebebf55c 849 }
d9c8f425 850
ebebf55c 851 /**
d9c8f425 852 * Emergency fallback. If we get to the end of processing and not all
853 * containers have been closed, output the rest with a developer debug warning.
854 * @return void
ebebf55c 855 */
d9c8f425 856 public function __destruct() {
857 if (empty($this->opencontainers)) {
858 return;
897b5c82 859 }
d9c8f425 860
861 // It seems you cannot rely on $CFG, and hence the debugging function here,
862 // becuase $CFG may be destroyed before this object is.
863 if ($this->isdebugging) {
864 echo '<div class="notifytiny"><p>Some containers were left open. This suggests there is a nesting problem.</p>' .
865 $this->output_log() . '</div>';
34a2777c 866 }
d9c8f425 867 echo $this->pop_all_but_last();
868 $container = array_pop($this->opencontainers);
869 echo $container->closehtml;
34a2777c 870 }
34a2777c 871
fa1afe32 872 /**
d9c8f425 873 * Adds an entry to the log.
874 * @param string $action The name of the action
875 * @param string $type The type of action
876 * @return void
fa1afe32 877 */
d9c8f425 878 protected function log($action, $type) {
879 $this->log[] = '<li>' . $action . ' ' . $type . ' at:' .
880 format_backtrace(debug_backtrace()) . '</li>';
ebebf55c 881 }
34a2777c 882
fa1afe32 883 /**
d9c8f425 884 * Outputs the log's contents as a HTML list.
885 * @return string HTML list of the log
fa1afe32 886 */
d9c8f425 887 protected function output_log() {
888 return '<ul>' . implode("\n", $this->log) . '</ul>';
34a2777c 889 }
890}
891
b7009474 892
893/**
894 * Output CSS while replacing constants/variables. See MDL-6798 for details
895 *
896 * Information from Urs Hunkler:
897 *
898 * This is an adaptation of Shaun Inman's "CSS Server-side Constants" for Moodle.
899 * http://www.shauninman.com/post/heap/2005/08/09/css_constants
900 *
901 * To use, specify $THEME->customcssoutputfunction = 'output_css_replacing_constants';
902 * in your theme's config.php file.
903 *
904 * The constant definitions are written into a separate CSS file named like
905 * constants.css and loaded first in config.php. You can use constants for any
906 * CSS properties. The constant definition looks like:
907 * <code>
908 * \@server constants {
909 * fontColor: #3a2830;
910 * aLink: #116699;
911 * aVisited: #AA2200;
912 * aHover: #779911;
913 * pageBackground: #FFFFFF;
914 * backgroundColor: #EEEEEE;
915 * backgroundSideblockHeader: #a8a4e9;
916 * fontcolorSideblockHeader: #222222;
917 * color1: #98818b;
918 * color2: #bd807b;
919 * color3: #f9d1d7;
920 * color4: #e8d4d8;
921 * }
922 * </code>
923 *
924 * The lines in the CSS files using CSS constants look like:
925 * <code>
926 * body {
927 * font-size: 100%;
928 * background-color: pageBackground;
929 * color: fontColor;
930 * font-family: 'Bitstream Vera Serif', georgia, times, serif;
931 * margin: 0;
932 * padding: 0;
933 * }
934 * div#page {
935 * margin: 0 10px;
936 * padding-top: 5px;
937 * border-top-width: 10px;
938 * border-top-style: solid;
939 * border-top-color: color3;
940 * }
941 * div.clearer {
942 * clear: both;
943 * }
944 * a:link {
945 * color: aLink;
fdeb7fa1 946 * }
b7009474 947 * </code>
948 *
fa1afe32 949 * @param array $files an array of the CSS fields that need to be output.
fdeb7fa1 950 * @param array $toreplace for convenience. If you are going to output the names
951 * of the css files, for debugging purposes, then you should output
fa1afe32 952 * str_replace($toreplace, '', $file); because it looks prettier.
953 * @return void
b7009474 954 */
fdeb7fa1 955function output_css_replacing_constants($files, $toreplace) {
b7009474 956 // Get all the CSS.
b7009474 957 ob_start();
958 foreach ($files as $file) {
959 $shortname = str_replace($toreplace, '', $file);
960 echo '/******* ' . $shortname . " start *******/\n\n";
961 @include_once($file);
962 echo '/******* ' . $shortname . " end *******/\n\n";
963 }
964 $css = ob_get_contents();
965 ob_end_clean();
966
967 if (preg_match_all("/@server\s+(?:variables|constants)\s*\{\s*([^\}]+)\s*\}\s*/i", $css, $matches)) {
968 $variables = array();
969 foreach ($matches[0] as $key => $server) {
970 $css = str_replace($server, '', $css);
971 preg_match_all("/([^:\}\s]+)\s*:\s*([^;\}]+);/", $matches[1][$key], $vars);
972 foreach ($vars[1] as $var => $value) {
973 $variables[$value] = $vars[2][$var];
974 }
975 }
976 $css = str_replace(array_keys($variables), array_values($variables), $css);
977 }
978 echo $css;
979}
980
981/**
982 * This CSS output function will link to CSS files rather than including them
983 * inline.
984 *
985 * The single CSS files can then be edited and saved with interactive
986 * CSS editors like CSSEdit. Any files that have a .php extension are still included
987 * inline.
988 *
fa1afe32 989 * @param array $files an array of the CSS fields that need to be output.
fdeb7fa1 990 * @param array $toreplace for convenience. If you are going to output the names
991 * of the css files, for debugging purposes, then you should output
fa1afe32 992 * str_replace($toreplace, '', $file); because it looks prettier.
993 * @return void
b7009474 994 */
fdeb7fa1 995function output_css_for_css_edit($files, $toreplace) {
b7009474 996 foreach ($files as $file) {
997 $shortname = str_replace($toreplace, '', $file);
998 echo '/* @group ' . $shortname . " */\n\n";
999 if (strpos($file, '.css') !== false) {
1000 echo '@import url("' . $file . '");'."\n\n";
1001 } else {
1002 @include_once($file);
1003 }
1004 echo "/* @end */\n\n";
1005 }
f77fcb5a 1006}