"MDL-13766, use OUTPUT to print box"
[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
18
19/**
20 * Functions for generating the HTML that Moodle should output.
21 *
22 * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML
23 * for an overview.
24 *
25 * @package moodlecore
26 * @copyright 2009 Tim Hunt
b7009474 27 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
571fa828 28 */
29
30
31/**
32 * A renderer factory is just responsible for creating an appropriate renderer
33 * for any given part of Moodle.
34 *
35 * Which renderer factory to use is chose by the current theme, and an instance
36 * if created automatically when the theme is set up.
37 *
ebebf55c 38 * A renderer factory must also have a constructor that takes a theme_config object.
39 * (See {@link renderer_factory_base::__construct} for an example.)
571fa828 40 *
41 * @copyright 2009 Tim Hunt
42 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
43 * @since Moodle 2.0
44 */
45interface renderer_factory {
46 /**
47 * Return the renderer for a particular part of Moodle.
48 *
49 * The renderer interfaces are defined by classes called moodle_..._renderer
50 * where ... is the name of the module, which, will be defined in this file
51 * for core parts of Moodle, and in a file called renderer.php for plugins.
52 *
53 * There is no separate interface definintion for renderers. Instead we
54 * take advantage of PHP being a dynamic languages. The renderer returned
55 * does not need to be a subclass of the moodle_..._renderer base class, it
56 * just needs to impmenent the same interface. This is sometimes called
57 * 'Duck typing'. For a tricky example, see {@link template_renderer} below.
58 * renderer ob
59 *
ebebf55c 60 * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
61 * @param moodle_page $page the page the renderer is outputting content for.
897b5c82 62 * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
571fa828 63 * @return object an object implementing the requested renderer interface.
64 */
897b5c82 65 public function get_renderer($component, $page, $subtype=null);
571fa828 66}
67
68
69/**
ebebf55c 70 * An icon finder is responsible for working out the correct URL for an icon.
571fa828 71 *
ebebf55c 72 * A icon finder must also have a constructor that takes a theme object.
73 * (See {@link standard_icon_finder::__construct} for an example.)
571fa828 74 *
ebebf55c 75 * Note that we are planning to change the Moodle icon naming convention before
76 * the Moodle 2.0 relase. Therefore, this API will probably change.
571fa828 77 *
78 * @copyright 2009 Tim Hunt
79 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
80 * @since Moodle 2.0
81 */
ebebf55c 82interface icon_finder {
571fa828 83 /**
ebebf55c 84 * Return the URL for an icon indentifed as in pre-Moodle 2.0 code.
85 *
86 * Suppose you have old code like $url = "$CFG->pixpath/i/course.gif";
87 * then old_icon_url('i/course'); will return the equivalent URL that is correct now.
88 *
89 * @param $iconname the name of the icon.
90 * @return string the URL for that icon.
571fa828 91 */
ebebf55c 92 public function old_icon_url($iconname);
93
571fa828 94 /**
ebebf55c 95 * Return the URL for an icon indentifed as in pre-Moodle 2.0 code.
571fa828 96 *
ebebf55c 97 * Suppose you have old code like $url = "$CFG->modpixpath/$mod/icon.gif";
98 * then mod_icon_url('icon', $mod); will return the equivalent URL that is correct now.
571fa828 99 *
ebebf55c 100 * @param $iconname the name of the icon.
101 * @param $module the module the icon belongs to.
102 * @return string the URL for that icon.
571fa828 103 */
ebebf55c 104 public function mod_icon_url($iconname, $module);
571fa828 105}
106
107
108/**
fdeb7fa1 109 * This class represents the configuration variables of a Moodle theme.
110 *
111 * All the variables with access: public below (with a few exceptions that are marked)
112 * are the properties you can set in your theme's config.php file.
113 *
114 * There are also some methods and protected variables that are part of the inner
115 * workings of Moodle's themes system. If you are just editing a theme's config.php
116 * file, you cac just ingore those, and the following information for developers.
ebebf55c 117 *
118 * Normally, to create an instance of this class, you should use the
119 * {@link theme_config::load()} factory method to load a themes config.php file.
fdeb7fa1 120 * However, normally you don't need to bother, becuase moodle_page (that is, $PAGE)
121 * will create one for you, accessible as $PAGE->theme.
571fa828 122 *
123 * @copyright 2009 Tim Hunt
124 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
125 * @since Moodle 2.0
126 */
ebebf55c 127class theme_config {
128 /**
fdeb7fa1 129 * @var string the name of this theme. Set automatically when this theme is
130 * loaded. Please do not try to set this in your theme's config.php file.
ebebf55c 131 */
fdeb7fa1 132 public $name;
571fa828 133
fdeb7fa1 134 /**
135 * @var string the folder where this themes files are stored. This is set
136 * automatically when the theme is loaded to $CFG->themedir . '/' . $this->name.
137 * Please do not try to set this in your theme's config.php file.
138 */
139 public $dir;
8954245a 140
fdeb7fa1 141 /**
142 * @var array The names of all the stylesheets from this theme that you would
143 * like included, in order. Give the names of the files without .css.
144 */
145 public $sheets = array('styles_layout', 'styles_fonts', 'styles_color');
b7009474 146
fdeb7fa1 147 /**
148 * You can base your theme on another theme by linking to the other theme as
149 * a parent. This lets you use the CSS from the other theme
150 * (see {@link $parentsheets}), or layout templates (see {@link $layouttemplates}).
151 * That makes it easy to create a new theme that is similar to another one
152 * but with a few changes. In this theme's CSS you only need to override
153 * those rules you want to change.
154 */
155 public $parent = null;
8954245a 156
fdeb7fa1 157 /**
158 * @var boolean|array Whether and which stylesheets from the parent theme
159 * to use in this theme. (Ignored if parent is null)
160 *
161 * Possbile values are:
162 * false - no parent theme CSS.
163 * true - include all the normal parent theme CSS. Currently this means
164 * array('styles_layout', 'styles_fonts', 'styles_color').
165 * array - include just the listed stylesheets. Give the files names
166 * without the .css, as in the above example.
167 */
168 public $parentsheets = false;
571fa828 169
fdeb7fa1 170 /**
171 * @var boolean|array Whether and which stylesheets from the standard theme
172 * to use in this theme.
173 *
174 * The advantages of using the standard stylesheets in your theme is that
175 * they give you a good basic layout, and when the Moodle core code is
176 * updated with new features, the standard theme CSS will be updated to match
177 * the changes in the code. Therefore, your theme is less likely to break
178 * when you upgrade Moodle.
179 *
180 * Possbile values are:
181 * false - no standard theme CSS.
182 * true - include all the main standard theme CSS. Currently this means
183 * array('styles_layout', 'styles_fonts', 'styles_color').
184 * array - include just the listed stylesheets. Give the files names
185 * without the .css, as in the above example.
186 */
187 public $standardsheets = true;
571fa828 188
fdeb7fa1 189 /**
190 * @var array use the CSS fragments from these types of plugins.
191 *
192 * All the plugins of the given types will be searched for a file called
193 * styles.php and, if found, these will be included with the CSS for this theme.
194 *
195 * This allows modules to provide some basic CSS so they work out of the box.
196 * You are strongly advised to leave this enabled, otherwise you will have to
197 * provide styling in your theme for every installed block, activity, course
198 * format, ... in your Moodle site.
199 *
200 * This setting is an array of plugin types, as in the {@link get_plugin_types()}
201 * function. The default value has been chosen to be the same as Moodle 1.9.
202 * This is not necessarily the best choice.
203 *
204 * The plugin CSS is included first, before any theme CSS. To be precise,
205 * if $standardsheets is true, the the plugin CSS is included with the
206 * standard theme's CSS, otherwise if $parentsheets is true, the plugin CSS
207 * will be included with the parent theme's CSS, otherwise the plugin CSS
208 * will be include with this theme's CSS.
209 */
210 public $pluginsheets = array('mod', 'block', 'format', 'gradereport');
571fa828 211
fdeb7fa1 212 /**
213 * @var boolean When this is true then Moodle will try to include a file
214 * meta.php from this theme into the <head></head> part of the page.
215 */
ebebf55c 216 public $metainclude = false;
571fa828 217
fdeb7fa1 218 /**
219 * @var boolean When this is true, and when this theme has a parent, then
220 * Moodle will try to include a file meta.php from the parent theme into the
221 * <head></head> part of the page.
222 */
ebebf55c 223 public $parentmetainclude = false;
571fa828 224
fdeb7fa1 225 /**
226 * @var boolean When this is true then Moodle will try to include the file
227 * meta.php from the standard theme into the <head></head> part of the page.
228 */
229 public $standardmetainclude = true;
571fa828 230
fdeb7fa1 231 /**
232 * If true, then this theme must have a "pix" subdirectory that contains
233 * copies of all files from the moodle/pix directory, plus a "pix/mod"
234 * directory containing all the icons for all the activity modules.
235 *
236 * @var boolean
237 */
238 public $custompix = false;
571fa828 239
fdeb7fa1 240 /**
241 * Which template to use for each general type of page.
242 *
243 * The array should have keys that are the page types, and values that are
244 * the template names, as described below.
245 *
246 * There are a few recognised page tyes like 'normal', 'popup', 'home'.
247 * If the current page if of a type not listed here, then the first listed
248 * template is used. Therefore you should probably list the 'normal' template
249 * first.
250 *
251 * To promote conisitency, you are encouraged to call your templates
252 * layout.php or layout-something.php.
253 *
254 * The name of the template can take one of three forms:
255 * <ol>
256 * <li><b>filename</b> for example 'layout.php'. Use that file from this theme.</li>
257 * <li><b>parent:filename</b> for example 'parent:layout-popup.php'. Use the
258 * specified file from the parent theme. (Obviously, you can only do this
259 * if this theme has a parent!)</li>
260 * <li><b>standard:filename</b> for example 'standard:layout-popup.php'. Use
261 * the specified file from the standard theme.</li>
262 * </ol>
263 *
264 * @var array
265 */
266 public $layouttemplates = array();
571fa828 267
fdeb7fa1 268 /**
269 * Names of the regions where blocks may appear on the page.
270 *
271 * For each region you list in $THEME->blockregions you must call
272 * blocks_print_group with that region id somewhere in your layout template.
273 *
274 * @var array
275 */
276 public $blockregions = array('side-pre', 'side-post');
571fa828 277
fdeb7fa1 278 /**
279 * The blocks region where new blocks will be added.
280 *
281 * Also, where any blocks in unrecognised regions will be shown.
282 * (Suppose someone added a block when another theme was selected).
283 *
284 * @var string
285 */
286 public $defaultblockregion = 'side-post';
571fa828 287
fdeb7fa1 288 /**
289 * With this you can control the colours of the big MP3 player
290 * that is used for MP3 resources.
291 *
292 * @var string
293 */
ebebf55c 294 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 295
fdeb7fa1 296 /**
297 * With this you can control the colours of the small MP3 player
298 * that is used elsewhere
299 *.
300 * @var string
301 */
ebebf55c 302 public $filter_mediaplugin_colors = 'bgColour=000000&btnColour=ffffff&btnBorderColour=cccccc&iconColour=000000&iconOverColour=00cc00&trackColour=cccccc&handleColour=ffffff&loaderColour=ffffff&waitForPlay=yes';
303
fdeb7fa1 304 /**
305 *$THEME->rarrow = '&#x25BA;' //OR '&rarr;';
306 *$THEME->larrow = '&#x25C4;' //OR '&larr;';
307 *$CFG->block_search_button = link_arrow_right(get_string('search'), $url='', $accesshide=true);
308 *
309 * Accessibility: Right and left arrow-like characters are
310 * used in the breadcrumb trail, course navigation menu
311 * (previous/next activity), calendar, and search forum block.
312 *
313 * If the theme does not set characters, appropriate defaults
314 * are set by (lib/weblib.php:check_theme_arrows). The suggestions
315 * above are 'silent' in a screen-reader like JAWS. Please DO NOT
316 * use &lt; &gt; &raquo; - these are confusing for blind users.
317 */
ebebf55c 318
fdeb7fa1 319 /**
320 * Name of the renderer factory class to use.
321 *
322 * This is an advanced feature. Moodle output is generated by 'renderers',
323 * you can customise the HTML that is output by writing custom renderers,
324 * and then you need to specify 'renderer factory' so that Moodle can find
325 * your renderers.
326 *
327 * There are some renderer factories supplied with Moodle. Please follow these
328 * links to see what they do.
329 * <ul>
330 * <li>{@link standard_renderer_factory} - the default.</li>
331 * <li>{@link theme_overridden_renderer_factory} - use this if you want to write
332 * your own custom renderers in a renderers.php file in this theme (or the parent theme).</li>
333 * <li>{@link template_renderer_factory} - highly experimental! Do not use (yet).</li>
334 * </ul>
335 *
336 * @var string name of a class implementing the {@link renderer_factory} interface.
337 */
ebebf55c 338 public $rendererfactory = 'standard_renderer_factory';
ebebf55c 339
fdeb7fa1 340 /**
341 * Name of the icon finder class to use.
342 *
343 * This is an advanced feature. controls how Moodle converts from the icon
344 * names used in the code to URLs to embed in the HTML. You should not ever
345 * need to change this.
346 *
347 * @var string name of a class implementing the {@link icon_finder} interface.
348 */
ebebf55c 349 public $iconfinder = 'pix_icon_finder';
571fa828 350
351 /**
fdeb7fa1 352 * Function to do custom CSS processing.
353 *
354 * This is an advanced feature. If you want to do custom processing on the
355 * CSS before it is output (for example, to replace certain variable names
356 * with particular values) you can give the name of a function here.
ebebf55c 357 *
358 * There are two functions avaiable that you may wish to use (defined in lib/outputlib.php):
fdeb7fa1 359 * <ul>
360 * <li>{@link output_css_replacing_constants}</li>
361 * <li>{@link output_css_for_css_edit}</li>
362 * </ul>
363 *
364 * If you wish to write your own function, look at those two as examples,
365 * and it should be clear what you have to do.
ebebf55c 366 *
367 * @var string the name of a function.
571fa828 368 */
ebebf55c 369 public $customcssoutputfunction = null;
571fa828 370
fdeb7fa1 371 /**
372 * You can use this to control the cutoff point for strings
373 * in the navmenus (list of activities in popup menu etc)
374 * Default is 50 characters wide.
375 */
376 public $navmenuwidth = 50;
377
378 /**
379 * By setting this to true, then you will have access to a
380 * new variable in your header.html and footer.html called
381 * $navmenulist ... this contains a simple XHTML menu of
382 * all activities in the current course, mostly useful for
383 * creating popup navigation menus and so on.
384 */
385 public $makenavmenulist = false;
386
387 /**
388 * @var renderer_factory Instance of the renderer_factory implementation
389 * we are using. Implementation detail.
390 */
391 protected $rf = null;
392
393 /**
394 * @var renderer_factory Instance of the icon_finder implementation we are
395 * using. Implementation detail.
396 */
397 protected $if = null;
398
571fa828 399 /**
ebebf55c 400 * Load the config.php file for a particular theme, and return an instance
401 * of this class. (That is, this is a factory method.)
402 *
403 * @param string $themename the name of the theme.
404 * @return theme_config an instance of this class.
571fa828 405 */
ebebf55c 406 public static function load($themename) {
407 global $CFG;
571fa828 408
ebebf55c 409 // We have to use the variable name $THEME (upper case) becuase that
410 // is what is used in theme config.php files.
411
412 // Set some other standard properties of the theme.
413 $THEME = new theme_config;
414 $THEME->name = $themename;
415 $THEME->dir = $CFG->themedir . '/' . $themename;
416
417 // Load up the theme config
418 $configfile = $THEME->dir . '/config.php';
419 if (!is_readable($configfile)) {
420 throw new coding_exception('Cannot use theme ' . $themename .
421 '. The file ' . $configfile . ' does not exist or is not readable.');
571fa828 422 }
ebebf55c 423 include($configfile);
424
425 $THEME->update_legacy_information();
426
427 return $THEME;
571fa828 428 }
429
34a2777c 430 /**
ebebf55c 431 * Get the renderer for a part of Moodle for this theme.
432 * @param string $module the name of part of moodle. E.g. 'core', 'quiz', 'qtype_multichoice'.
433 * @param moodle_page $page the page we are rendering
434 * @return moodle_renderer_base the requested renderer.
897b5c82 435 * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
34a2777c 436 */
897b5c82 437 public function get_renderer($module, $page, $subtype=null) {
ebebf55c 438 if (is_null($this->rf)) {
439 if (CLI_SCRIPT) {
440 $classname = 'cli_renderer_factory';
441 } else {
442 $classname = $this->rendererfactory;
443 }
444 $this->rf = new $classname($this);
445 }
446
897b5c82 447 return $this->rf->get_renderer($module, $page, $subtype);
34a2777c 448 }
449
571fa828 450 /**
ebebf55c 451 * Get the renderer for a part of Moodle for this theme.
452 * @return moodle_renderer_base the requested renderer.
571fa828 453 */
ebebf55c 454 protected function get_icon_finder() {
455 if (is_null($this->if)) {
456 $classname = $this->iconfinder;
457 $this->if = new $classname($this);
571fa828 458 }
ebebf55c 459 return $this->if;
571fa828 460 }
461
462 /**
ebebf55c 463 * Return the URL for an icon indentifed as in pre-Moodle 2.0 code.
464 *
465 * Suppose you have old code like $url = "$CFG->pixpath/i/course.gif";
466 * then old_icon_url('i/course'); will return the equivalent URL that is correct now.
467 *
468 * @param $iconname the name of the icon.
469 * @return string the URL for that icon.
571fa828 470 */
ebebf55c 471 public function old_icon_url($iconname) {
4096752d 472 return $this->get_icon_finder()->old_icon_url($iconname);
571fa828 473 }
474
475 /**
ebebf55c 476 * Return the URL for an icon indentifed as in pre-Moodle 2.0 code.
477 *
478 * Suppose you have old code like $url = "$CFG->modpixpath/$mod/icon.gif";
479 * then mod_icon_url('icon', $mod); will return the equivalent URL that is correct now.
480 *
481 * @param $iconname the name of the icon.
482 * @param $module the module the icon belongs to.
483 * @return string the URL for that icon.
571fa828 484 */
ebebf55c 485 public function mod_icon_url($iconname, $module) {
4096752d 486 return $this->get_icon_finder()->mod_icon_url($iconname, $module);
571fa828 487 }
34a2777c 488
ebebf55c 489 /**
490 * Get the list of stylesheet URLs that need to go in the header for this theme.
491 * @return array of URLs.
492 */
493 public function get_stylesheet_urls() {
34a2777c 494 global $CFG;
495
fdeb7fa1 496 // We need to tell the CSS that is being included (for example the standard
497 // theme CSS) which theme it is being included for. Prepare the necessary param.
498 $param = '?for=' . $this->name;
34a2777c 499
ebebf55c 500 // Stylesheets, in order (standard, parent, this - some of which may be the same).
501 $stylesheets = array();
502 if ($this->name != 'standard' && $this->standardsheets) {
fdeb7fa1 503 $stylesheets[] = $CFG->httpsthemewww . '/standard/styles.php' . $param;
ebebf55c 504 }
505 if (!empty($this->parent)) {
fdeb7fa1 506 $stylesheets[] = $CFG->httpsthemewww . '/' . $this->parent . '/styles.php' . $param;
34a2777c 507 }
fdeb7fa1 508 $stylesheets[] = $CFG->httpsthemewww . '/' . $this->name . '/styles.php' . $param;
34a2777c 509
fdeb7fa1 510 // Additional styles for right-to-left languages, if applicable.
ebebf55c 511 if (right_to_left()) {
512 $stylesheets[] = $CFG->httpsthemewww . '/standard/rtl.css';
34a2777c 513
ebebf55c 514 if (!empty($this->parent) && file_exists($CFG->themedir . '/' . $this->parent . '/rtl.css')) {
515 $stylesheets[] = $CFG->httpsthemewww . '/' . $this->parent . '/rtl.css';
516 }
34a2777c 517
ebebf55c 518 if (file_exists($this->dir . '/rtl.css')) {
519 $stylesheets[] = $CFG->httpsthemewww . '/' . $this->name . '/rtl.css';
520 }
34a2777c 521 }
17a6649b 522
fdeb7fa1 523 // If the theme wants pluginsheets, get them included in the first (most
524 // general) stylesheet we are including. That is, process them with the
525 // standard CSS if we are using that, else with the parent CSS, else with
526 // our own CSS.
527 if (!empty($this->pluginsheets)) {
528 $stylesheets[0] .= '&amp;pluginsheets=1';
529 }
530
ebebf55c 531 return $stylesheets;
532 }
17a6649b 533
ebebf55c 534 /**
fdeb7fa1 535 * Get the meta tags from one theme to got in the <head> of the HTML.
536 * @param $themename the name of the theme to get meta tags from.
537 * @param $page that page whose <head> is being output.
538 * @return string HTML code.
539 */
540 protected function get_theme_meta_tags($themename, $page) {
541 global $CFG;
542 // At least one theme's meta.php expects to have $PAGE visible.
543 $PAGE = $page;
544 $filename = $CFG->themedir . '/' . $themename . '/meta.php';
545 if (file_exists($filename)) {
546 ob_start();
547 include_once($filename);
f77fcb5a 548 $metatags = ob_get_contents();
fdeb7fa1 549 ob_end_clean();
550 }
551 return $metatags;
552 }
553
554 /**
555 * Get all the meta tags (from this theme, standard, parent) that this theme
556 * wants in the <head> of the HTML.
557 *
558 * @param $page that page whose <head> is being output.
559 * @return string HTML code.
560 */
561 public function get_meta_tags($page) {
562 $metatags = '';
563 if ($this->standardmetainclude) {
564 $metatags .= $this->get_theme_meta_tags('standard', $page);
565 }
566 if ($this->parent && $this->parentmetainclude) {
567 $metatags .= $this->get_theme_meta_tags($this->parent, $page);
568 }
569 if ($this->metainclude) {
570 $metatags .= $this->get_theme_meta_tags($this->name, $page);
571 }
572 return $metatags;
573 }
574
575 /**
576 * Given the settings of this theme, and the page generaltype, return the
577 * full path of the page layout template to use.
578 *
579 * Used by {@link moodle_core_renderer::header()}. If an appropriate new-style
580 * template cannot be found, returns false to signal that the old-style
581 * header.html and footer.html files should be used.
582 *
583 * @return string Full path to the template to use, or false if a new-style
584 * template cannot be found.
585 */
586 public function find_page_template($generaltype) {
587 global $CFG;
588
589 // Legacy fallback.
590 if (empty($this->layouttemplates)) {
591 return false;
592 }
593
594 // Look up the page type in the config array.
595 if (array_key_exists($generaltype, $this->layouttemplates)) {
596 $templatefile = $this->layouttemplates[$generaltype];
597 } else {
598 $templatefile = reset($this->layouttemplates);
599 }
600
601 // Parse the name that was found.
602 if (strpos('standard:', $templatefile) === 0) {
603 $templatepath = $CFG->themedir . '/standard/' . substr($templatefile, 9);
604 } else if (strpos('parent:', $templatefile) === 0) {
605 if (empty($this->parent)) {
606 throw new coding_exception('This theme (' . $this->name .
607 ') does not have a parent. You cannot specify a layout template like ' .
608 $templatefile);
609 }
610 $templatepath = $CFG->themedir . '/' . $this->parent . '/' . substr($templatefile, 7);
611 } else {
612 $templatepath = $this->dir . '/' . $templatefile;
613 }
614
615 // Check the the template exists.
616 if (!is_readable($templatepath)) {
617 throw new coding_exception('The template ' . $templatefile . ' (' . $templatepath .
618 ') for page type ' . $generaltype . ' cannot be found in this theme (' .
619 $this->name . ')');
620 }
621
622 return $templatepath;
623 }
624
625 /**
626 * Helper method used by {@link update_legacy_information()}. Update one entry
627 * in the $this->pluginsheets array, based on the legacy $property propery.
628 * @param $plugintype e.g. 'mod'.
629 * @param $property e.g. 'modsheets'.
630 */
631 protected function update_legacy_plugin_sheets($plugintype, $property) {
632 if (property_exists($this, $property)) {
633 debugging('$THEME->' . $property . ' is deprecated. Please use the new $THEME->pluginsheets instead.', DEBUG_DEVELOPER);
634 if (!empty($this->$property) && !in_array($plugintype, $this->pluginsheets)) {
635 $this->pluginsheets[] = $plugintype;
636 } else if (empty($this->$property) && in_array($plugintype, $this->pluginsheets)) {
637 unset($this->pluginsheets[array_search($plugintype, $this->pluginsheets)]);
638 }
639 }
640 }
641
642 /**
643 * This method looks a the settings that have been loaded, to see whether
ebebf55c 644 * any legacy things are being used, and outputs warning and tries to update
645 * things to use equivalent newer settings.
646 */
647 protected function update_legacy_information() {
648 global $CFG;
fdeb7fa1 649
650 $this->update_legacy_plugin_sheets('mod', 'modsheets');
651 $this->update_legacy_plugin_sheets('block', 'blocksheets');
652 $this->update_legacy_plugin_sheets('format', 'formatsheets');
653 $this->update_legacy_plugin_sheets('gradereport', 'gradereportsheets');
654
655 if (empty($this->standardsheets)) {
656 // In Moodle 1.9, these settings were dependant on each other. They
657 // are now independant, at least at the time when the CSS is served,
658 // to this is necessary to maintain backwards compatibility. Hmm.
659 // What if you don't want this?
660 $this->pluginsheets = array();
661 }
662
663 if (!empty($this->langsheets)) {
664 debugging('$THEME->langsheets is no longer supported. No languages were ' .
665 'using it for anything, and it did not seem to serve any purpose.', DEBUG_DEVELOPER);
666 }
667
ebebf55c 668 if (!empty($this->customcorners)) {
669 // $THEME->customcorners is deprecated but we provide support for it via the
670 // custom_corners_renderer_factory class in lib/deprecatedlib.php
671 debugging('$THEME->customcorners is deprecated. Please use the new $THEME->rendererfactory ' .
672 'to control HTML generation. Please use $this->rendererfactory = \'custom_corners_renderer_factory\'; ' .
673 'in your config.php file instead.', DEBUG_DEVELOPER);
674 $this->rendererfactory = 'custom_corners_renderer_factory';
34a2777c 675 }
676
ebebf55c 677 if (!empty($this->cssconstants)) {
678 debugging('$THEME->cssconstants is deprecated. Please use ' .
679 '$THEME->customcssoutputfunction = \'output_css_replacing_constants\'; ' .
680 'in your config.php file instead.', DEBUG_DEVELOPER);
681 $this->customcssoutputfunction = 'output_css_replacing_constants';
34a2777c 682 }
ebebf55c 683
684 if (!empty($this->CSSEdit)) {
685 debugging('$THEME->CSSEdit is deprecated. Please use ' .
686 '$THEME->customcssoutputfunction = \'output_css_for_css_edit\'; ' .
687 'in your config.php file instead.', DEBUG_DEVELOPER);
688 $this->customcssoutputfunction = 'output_css_for_css_edit';
34a2777c 689 }
690
ae96b517 691 if (!empty($CFG->smartpix)) {
ebebf55c 692 $this->iconfinder = 'smartpix_icon_finder';
693 } else if ($this->custompix) {
694 $this->iconfinder = 'theme_icon_finder';
695 }
34a2777c 696 }
697
ebebf55c 698 /**
699 * Set the variable $CFG->pixpath and $CFG->modpixpath to be the right
d436d197 700 * ones for this theme. These should no longer be used, but legacy code
701 * might still rely on them.
ebebf55c 702 */
d436d197 703 public function setup_legacy_pix_paths() {
ebebf55c 704 global $CFG;
705 if (!empty($CFG->smartpix)) {
706 if ($CFG->slasharguments) {
707 // Use this method if possible for better caching
708 $extra = '';
709 } else {
710 $extra = '?file=';
711 }
712 $CFG->pixpath = $CFG->httpswwwroot . '/pix/smartpix.php' . $extra . '/' . $this->name;
713 $CFG->modpixpath = $CFG->httpswwwroot . '/pix/smartpix.php' . $extra . '/' . $this->name . '/mod';
34a2777c 714
ebebf55c 715 } else if (empty($THEME->custompix)) {
716 $CFG->pixpath = $CFG->httpswwwroot . '/pix';
717 $CFG->modpixpath = $CFG->httpswwwroot . '/mod';
718
719 } else {
720 $CFG->pixpath = $CFG->httpsthemewww . '/' . $this->name . '/pix';
721 $CFG->modpixpath = $CFG->httpsthemewww . '/' . $this->name . '/pix/mod';
34a2777c 722 }
34a2777c 723 }
ebebf55c 724}
34a2777c 725
ebebf55c 726
727/**
728 * This icon finder implements the old scheme that was used when themes that had
729 * $THEME->custompix = false.
730 *
731 * @copyright 2009 Tim Hunt
732 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
733 * @since Moodle 2.0
734 */
735class pix_icon_finder implements icon_finder {
736 /**
737 * Constructor
738 * @param theme_config $theme the theme we are finding icons for (which is irrelevant).
739 */
740 public function __construct($theme) {
34a2777c 741 }
742
ebebf55c 743 /* Implement interface method. */
744 public function old_icon_url($iconname) {
745 global $CFG;
3aaa27f4 746 if (file_exists($CFG->dirroot . '/pix/' . $iconname . '.png')) {
747 return $CFG->httpswwwroot . '/pix/' . $iconname . '.png';
748 } else {
749 return $CFG->httpswwwroot . '/pix/' . $iconname . '.gif';
750 }
34a2777c 751 }
752
ebebf55c 753 /* Implement interface method. */
754 public function mod_icon_url($iconname, $module) {
755 global $CFG;
3aaa27f4 756 if (file_exists($CFG->dirroot . '/mod/' . $module . '/' . $iconname . '.png')) {
757 return $CFG->httpswwwroot . '/mod/' . $module . '/' . $iconname . '.png';
758 } else {
759 return $CFG->httpswwwroot . '/mod/' . $module . '/' . $iconname . '.gif';
760 }
ebebf55c 761 }
762}
34a2777c 763
34a2777c 764
ebebf55c 765/**
766 * This icon finder implements the old scheme that was used for themes that had
767 * $THEME->custompix = true.
768 *
769 * @copyright 2009 Tim Hunt
770 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
771 * @since Moodle 2.0
772 */
773class theme_icon_finder implements icon_finder {
774 protected $themename;
775 /**
776 * Constructor
777 * @param theme_config $theme the theme we are finding icons for.
778 */
779 public function __construct($theme) {
780 $this->themename = $theme->name;
781 }
34a2777c 782
ebebf55c 783 /* Implement interface method. */
784 public function old_icon_url($iconname) {
785 global $CFG;
3aaa27f4 786 if (file_exists($CFG->themedir . '/' . $this->themename . '/pix/' . $iconname . '.png')) {
787 return $CFG->httpsthemewww . '/' . $this->themename . '/pix/' . $iconname . '.png';
788 } else {
789 return $CFG->httpsthemewww . '/' . $this->themename . '/pix/' . $iconname . '.gif';
790 }
ebebf55c 791 }
34a2777c 792
ebebf55c 793 /* Implement interface method. */
794 public function mod_icon_url($iconname, $module) {
795 global $CFG;
3aaa27f4 796 if (file_exists($CFG->themedir . '/' . $this->themename . '/pix/mod/' . $module . '/' . $iconname . '.png')) {
797 return $CFG->httpsthemewww . '/' . $this->themename . '/pix/mod/' . $module . '/' . $iconname . '.png';
798 } else {
799 return $CFG->httpsthemewww . '/' . $this->themename . '/pix/mod/' . $module . '/' . $iconname . '.gif';
800 }
34a2777c 801 }
ebebf55c 802}
803
804
805/**
806 * This icon finder implements the algorithm in pix/smartpix.php.
807 *
808 * @copyright 2009 Tim Hunt
809 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
810 * @since Moodle 2.0
811 */
812class smartpix_icon_finder extends pix_icon_finder {
813 protected $places = array();
34a2777c 814
afa2dcad 815 /**
ebebf55c 816 * Constructor
817 * @param theme_config $theme the theme we are finding icons for.
afa2dcad 818 */
ebebf55c 819 public function __construct($theme) {
e8775320 820 global $CFG;
ebebf55c 821 $this->places[$CFG->themedir . '/' . $theme->name . '/pix/'] =
822 $CFG->httpsthemewww . '/' . $theme->name . '/pix/';
823 if (!empty($theme->parent)) {
824 $this->places[$CFG->themedir . '/' . $theme->parent . '/pix/'] =
825 $CFG->httpsthemewww . '/' . $theme->parent . '/pix/';
826 }
827 }
e8775320 828
ebebf55c 829 /* Implement interface method. */
830 public function old_icon_url($iconname) {
831 foreach ($this->places as $dirroot => $urlroot) {
3aaa27f4 832 if (file_exists($dirroot . $iconname . '.png')) {
833 return $dirroot . $iconname . '.png';
834 } else if (file_exists($dirroot . $iconname . '.gif')) {
835 return $dirroot . $iconname . '.gif';
e8775320 836 }
837 }
ebebf55c 838 return parent::old_icon_url($iconname);
839 }
e8775320 840
ebebf55c 841 /* Implement interface method. */
842 public function mod_icon_url($iconname, $module) {
843 foreach ($this->places as $dirroot => $urlroot) {
3aaa27f4 844 if (file_exists($dirroot . 'mod/' . $iconname . '.png')) {
845 return $dirroot . 'mod/' . $iconname . '.png';
846 } else if (file_exists($dirroot . 'mod/' . $iconname . '.gif')) {
847 return $dirroot . 'mod/' . $iconname . '.gif';
ebebf55c 848 }
e8775320 849 }
3aaa27f4 850 return parent::old_icon_url($iconname, $module);
e8775320 851 }
ebebf55c 852}
e8775320 853
34a2777c 854
ebebf55c 855/**
856 * This is a base class to help you implement the renderer_factory interface.
857 *
858 * It keeps a cache of renderers that have been constructed, so you only need
859 * to construct each one once in you subclass.
860 *
861 * It also has a method to get the name of, and include the renderer.php with
862 * the definition of, the standard renderer class for a given module.
863 *
864 * @copyright 2009 Tim Hunt
865 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
866 * @since Moodle 2.0
867 */
868abstract class renderer_factory_base implements renderer_factory {
869 /** @var theme_config the theme we belong to. */
870 protected $theme;
34a2777c 871
ebebf55c 872 /**
873 * Constructor.
874 * @param theme_config $theme the theme we belong to.
875 */
876 public function __construct($theme) {
877 $this->theme = $theme;
878 }
879 /**
880 * For a given module name, return the name of the standard renderer class
881 * that defines the renderer interface for that module.
882 *
883 * Also, if it exists, include the renderer.php file for that module, so
884 * the class definition of the default renderer has been loaded.
885 *
886 * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
897b5c82 887 * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
ebebf55c 888 * @return string the name of the standard renderer class for that module.
889 */
897b5c82 890 protected function standard_renderer_class_for_module($component, $subtype=null) {
ebebf55c 891 if ($component != 'core') {
892 $pluginrenderer = get_component_directory($component) . '/renderer.php';
893 if (file_exists($pluginrenderer)) {
894 include_once($pluginrenderer);
895 }
34a2777c 896 }
897b5c82 897 if (is_null($subtype)) {
898 $class = 'moodle_' . $component . '_renderer';
899 } else {
900 $class = 'moodle_' . $component . '_' . $subtype . '_renderer';
901 }
ebebf55c 902 if (!class_exists($class)) {
903 throw new coding_exception('Request for an unknown renderer class ' . $class);
34a2777c 904 }
ebebf55c 905 return $class;
34a2777c 906 }
ebebf55c 907}
34a2777c 908
34a2777c 909
ebebf55c 910/**
911 * This is the default renderer factory for Moodle. It simply returns an instance
912 * of the appropriate standard renderer class.
913 *
914 * @copyright 2009 Tim Hunt
915 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
916 * @since Moodle 2.0
917 */
918class standard_renderer_factory extends renderer_factory_base {
919 /* Implement the subclass method. */
897b5c82 920 public function get_renderer($module, $page, $subtype=null) {
ebebf55c 921 if ($module == 'core') {
922 return new moodle_core_renderer($page);
923 } else {
897b5c82 924 $class = $this->standard_renderer_class_for_module($module, $subtype);
ebebf55c 925 return new $class($page, $this->get_renderer('core', $page));
34a2777c 926 }
ebebf55c 927 }
928}
34a2777c 929
34a2777c 930
ebebf55c 931/**
932 * This is a slight variation on the standard_renderer_factory used by CLI scripts.
933 *
934 * @copyright 2009 Tim Hunt
935 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
936 * @since Moodle 2.0
937 */
938class cli_renderer_factory extends standard_renderer_factory {
939 /* Implement the subclass method. */
897b5c82 940 public function get_renderer($module, $page, $subtype=null) {
ebebf55c 941 if ($module == 'core') {
942 return new cli_core_renderer($page);
943 } else {
897b5c82 944 parent::get_renderer($module, $page, $subtype);
ebebf55c 945 }
34a2777c 946 }
ebebf55c 947}
34a2777c 948
34a2777c 949
ebebf55c 950/**
951 * This is renderer factory allows themes to override the standard renderers using
952 * php code.
953 *
954 * It will load any code from theme/mytheme/renderers.php and
955 * theme/parenttheme/renderers.php, if then exist. Then whenever you ask for
956 * a renderer for 'component', it will create a mytheme_component_renderer or a
957 * parenttheme_component_renderer, instead of a moodle_component_renderer,
958 * if either of those classes exist.
959 *
960 * This generates the slightly different HTML that the custom_corners theme expects.
961 *
962 * @copyright 2009 Tim Hunt
963 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
964 * @since Moodle 2.0
965 */
966class theme_overridden_renderer_factory extends standard_renderer_factory {
967 protected $prefixes = array();
968
969 /**
970 * Constructor.
971 * @param object $theme the theme we are rendering for.
972 * @param moodle_page $page the page we are doing output for.
973 */
974 public function __construct($theme) {
975 global $CFG;
976 parent::__construct($theme);
977
978 // Initialise $this->prefixes.
979 $renderersfile = $theme->dir . '/renderers.php';
980 if (is_readable($renderersfile)) {
981 include_once($renderersfile);
982 $this->prefixes[] = $theme->name . '_';
983 }
984 if (!empty($theme->parent)) {
985 $renderersfile = $CFG->themedir .'/'. $theme->parent . '/renderers.php';
986 if (is_readable($renderersfile)) {
987 include_once($renderersfile);
988 $this->prefixes[] = $theme->parent . '_';
989 }
990 }
34a2777c 991 }
992
ebebf55c 993 /* Implement the subclass method. */
897b5c82 994 public function get_renderer($module, $page, $subtype=null) {
ebebf55c 995 foreach ($this->prefixes as $prefix) {
897b5c82 996 if (is_null($subtype)) {
997 $classname = $prefix . $module . '_renderer';
998 } else {
999 $classname = $prefix . $module . '_' . $subtype . '_renderer';
1000 }
ebebf55c 1001 if (class_exists($classname)) {
1002 if ($module == 'core') {
1003 return new $classname($page);
1004 } else {
1005 return new $classname($page, $this->get_renderer('core', $page));
1006 }
1007 }
1008 }
897b5c82 1009 return parent::get_renderer($module, $page, $subtype);
ebebf55c 1010 }
1011}
34a2777c 1012
34a2777c 1013
ebebf55c 1014/**
1015 * This is renderer factory that allows you to create templated themes.
1016 *
1017 * This should be considered an experimental proof of concept. In particular,
1018 * the performance is probably not very good. Do not try to use in on a busy site
1019 * without doing careful load testing first!
1020 *
1021 * This renderer factory returns instances of {@link template_renderer} class
1022 * which which implement the corresponding renderer interface in terms of
1023 * templates. To use this your theme must have a templates folder inside it.
1024 * Then suppose the method moodle_core_renderer::greeting($name = 'world');
1025 * exists. Then, a call to $OUTPUT->greeting() will cause the template
1026 * /theme/yourtheme/templates/core/greeting.php to be rendered, with the variable
1027 * $name available. The greeting.php template might contain
1028 *
1029 * <pre>
1030 * <h1>Hello <?php echo $name ?>!</h1>
1031 * </pre>
1032 *
1033 * @copyright 2009 Tim Hunt
1034 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1035 * @since Moodle 2.0
1036 */
1037class template_renderer_factory extends renderer_factory_base {
1038 /**
1039 * An array of paths of where to search for templates. Normally this theme,
1040 * the parent theme then the standardtemplate theme. (If some of these do
1041 * not exist, or are the same as each other, then the list will be shorter.
1042 */
1043 protected $searchpaths = array();
34a2777c 1044
ebebf55c 1045 /**
1046 * Constructor.
1047 * @param object $theme the theme we are rendering for.
1048 * @param moodle_page $page the page we are doing output for.
1049 */
1050 public function __construct($theme) {
1051 global $CFG;
1052 parent::__construct($theme);
34a2777c 1053
ebebf55c 1054 // Initialise $this->searchpaths.
1055 if ($theme->name != 'standardtemplate') {
1056 $templatesdir = $theme->dir . '/templates';
1057 if (is_dir($templatesdir)) {
1058 $this->searchpaths[] = $templatesdir;
1059 }
34a2777c 1060 }
ebebf55c 1061 if (!empty($theme->parent)) {
1062 $templatesdir = $CFG->themedir .'/'. $theme->parent . '/templates';
1063 if (is_dir($templatesdir)) {
1064 $this->searchpaths[] = $templatesdir;
1065 }
1066 }
1067 $this->searchpaths[] = $CFG->themedir .'/standardtemplate/templates';
1068 }
34a2777c 1069
ebebf55c 1070 /* Implement the subclass method. */
897b5c82 1071 public function get_renderer($module, $page, $subtype=null) {
ebebf55c 1072 // Refine the list of search paths for this module.
1073 $searchpaths = array();
1074 foreach ($this->searchpaths as $rootpath) {
1075 $path = $rootpath . '/' . $module;
897b5c82 1076 if (!is_null($subtype)) {
1077 $path .= '/' . $subtype;
1078 }
ebebf55c 1079 if (is_dir($path)) {
1080 $searchpaths[] = $path;
1081 }
1082 }
34a2777c 1083
ebebf55c 1084 // Create a template_renderer that copies the API of the standard renderer.
897b5c82 1085 $copiedclass = $this->standard_renderer_class_for_module($module, $subtype);
ebebf55c 1086 return new template_renderer($copiedclass, $searchpaths, $page);
1087 }
1088}
34a2777c 1089
34a2777c 1090
ebebf55c 1091/**
1092 * Simple base class for Moodle renderers.
1093 *
1094 * Tracks the xhtml_container_stack to use, which is passed in in the constructor.
1095 *
1096 * Also has methods to facilitate generating HTML output.
1097 *
1098 * @copyright 2009 Tim Hunt
1099 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1100 * @since Moodle 2.0
1101 */
1102class moodle_renderer_base {
1103 /** @var xhtml_container_stack the xhtml_container_stack to use. */
1104 protected $opencontainers;
1105 /** @var moodle_page the page we are rendering for. */
1106 protected $page;
34a2777c 1107
ebebf55c 1108 /**
1109 * Constructor
1110 * @param $opencontainers the xhtml_container_stack to use.
1111 * @param moodle_page $page the page we are doing output for.
1112 */
1113 public function __construct($page) {
1114 $this->opencontainers = $page->opencontainers;
1115 $this->page = $page;
34a2777c 1116 }
1117
ebebf55c 1118 /**
1119 * Have we started output yet?
1120 * @return boolean true if the header has been printed.
1121 */
1122 public function has_started() {
1123 return $this->page->state >= moodle_page::STATE_IN_BODY;
1124 }
1125
1126 protected function output_tag($tagname, $attributes, $contents) {
1127 return $this->output_start_tag($tagname, $attributes) . $contents .
1128 $this->output_end_tag($tagname);
1129 }
1130 protected function output_start_tag($tagname, $attributes) {
1131 return '<' . $tagname . $this->output_attributes($attributes) . '>';
1132 }
1133 protected function output_end_tag($tagname) {
1134 return '</' . $tagname . '>';
1135 }
1136 protected function output_empty_tag($tagname, $attributes) {
1137 return '<' . $tagname . $this->output_attributes($attributes) . ' />';
1138 }
1139
1140 protected function output_attribute($name, $value) {
1141 $value = trim($value);
1142 if ($value || is_numeric($value)) { // We want 0 to be output.
1143 return ' ' . $name . '="' . $value . '"';
1144 }
1145 }
1146 protected function output_attributes($attributes) {
1147 if (empty($attributes)) {
1148 $attributes = array();
1149 }
34a2777c 1150 $output = '';
ebebf55c 1151 foreach ($attributes as $name => $value) {
1152 $output .= $this->output_attribute($name, $value);
34a2777c 1153 }
ebebf55c 1154 return $output;
1155 }
1156 public static function prepare_classes($classes) {
1157 if (is_array($classes)) {
1158 return implode(' ', array_unique($classes));
1159 }
1160 return $classes;
1161 }
34a2777c 1162
ebebf55c 1163 /**
1164 * Return the URL for an icon indentifed as in pre-Moodle 2.0 code.
1165 *
1166 * Suppose you have old code like $url = "$CFG->pixpath/i/course.gif";
1167 * then old_icon_url('i/course'); will return the equivalent URL that is correct now.
1168 *
1169 * @param $iconname the name of the icon.
1170 * @return string the URL for that icon.
1171 */
1172 public function old_icon_url($iconname) {
1173 return $this->page->theme->old_icon_url($iconname);
1174 }
34a2777c 1175
ebebf55c 1176 /**
1177 * Return the URL for an icon indentifed as in pre-Moodle 2.0 code.
1178 *
1179 * Suppose you have old code like $url = "$CFG->modpixpath/$mod/icon.gif";
1180 * then mod_icon_url('icon', $mod); will return the equivalent URL that is correct now.
1181 *
1182 * @param $iconname the name of the icon.
1183 * @param $module the module the icon belongs to.
1184 * @return string the URL for that icon.
1185 */
1186 public function mod_icon_url($iconname, $module) {
1187 return $this->page->theme->mod_icon_url($iconname, $module);
1188 }
1189}
34a2777c 1190
34a2777c 1191
ebebf55c 1192/**
1193 * This is the templated renderer which copies the API of another class, replacing
1194 * all methods calls with instantiation of a template.
1195 *
1196 * When the method method_name is called, this class will search for a template
1197 * called method_name.php in the folders in $searchpaths, taking the first one
1198 * that it finds. Then it will set up variables for each of the arguments of that
1199 * method, and render the template. This is implemented in the {@link __call()}
1200 * PHP magic method.
1201 *
1202 * Methods like print_box_start and print_box_end are handles specially, and
1203 * implemented in terms of the print_box.php method.
1204 *
1205 * @copyright 2009 Tim Hunt
1206 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1207 * @since Moodle 2.0
1208 */
1209class template_renderer extends moodle_renderer_base {
1210 /** @var ReflectionClass information about the class whose API we are copying. */
1211 protected $copiedclass;
1212 /** @var array of places to search for templates. */
1213 protected $searchpaths;
1214 protected $rendererfactory;
34a2777c 1215
ebebf55c 1216 /**
1217 * Magic word used when breaking apart container templates to implement
1218 * _start and _end methods.
1219 */
1220 const contentstoken = '-@#-Contents-go-here-#@-';
1221
1222 /**
1223 * Constructor
1224 * @param string $copiedclass the name of a class whose API we should be copying.
1225 * @param $searchpaths a list of folders to search for templates in.
1226 * @param $opencontainers the xhtml_container_stack to use.
1227 * @param moodle_page $page the page we are doing output for.
1228 */
1229 public function __construct($copiedclass, $searchpaths, $page) {
1230 parent::__construct($page);
1231 $this->copiedclass = new ReflectionClass($copiedclass);
1232 $this->searchpaths = $searchpaths;
1233 }
1234
1235 /* PHP magic method implementation. */
1236 public function __call($method, $arguments) {
1237 if (substr($method, -6) == '_start') {
1238 return $this->process_start(substr($method, 0, -6), $arguments);
1239 } else if (substr($method, -4) == '_end') {
1240 return $this->process_end(substr($method, 0, -4), $arguments);
1241 } else {
1242 return $this->process_template($method, $arguments);
1243 }
34a2777c 1244 }
1245
a5cb8d69 1246 /**
ebebf55c 1247 * Render the template for a given method of the renderer class we are copying,
1248 * using the arguments passed.
1249 * @param string $method the method that was called.
1250 * @param array $arguments the arguments that were passed to it.
a5cb8d69 1251 * @return string the HTML to be output.
1252 */
ebebf55c 1253 protected function process_template($method, $arguments) {
1254 if (!$this->copiedclass->hasMethod($method) ||
1255 !$this->copiedclass->getMethod($method)->isPublic()) {
1256 throw new coding_exception('Unknown method ' . $method);
a5cb8d69 1257 }
1258
ebebf55c 1259 // Find the template file for this method.
1260 $template = $this->find_template($method);
a5cb8d69 1261
ebebf55c 1262 // Use the reflection API to find out what variable names the arguments
1263 // should be stored in, and fill in any missing ones with the defaults.
1264 $namedarguments = array();
1265 $expectedparams = $this->copiedclass->getMethod($method)->getParameters();
1266 foreach ($expectedparams as $param) {
1267 $paramname = $param->getName();
1268 if (!empty($arguments)) {
1269 $namedarguments[$paramname] = array_shift($arguments);
1270 } else if ($param->isDefaultValueAvailable()) {
1271 $namedarguments[$paramname] = $param->getDefaultValue();
1272 } else {
1273 throw new coding_exception('Missing required argument ' . $paramname);
a5cb8d69 1274 }
a5cb8d69 1275 }
1276
ebebf55c 1277 // Actually render the template.
1278 return $this->render_template($template, $namedarguments);
1279 }
a5cb8d69 1280
ebebf55c 1281 /**
1282 * Actually do the work of rendering the template.
1283 * @param $_template the full path to the template file.
1284 * @param $_namedarguments an array variable name => value, the variables
1285 * that should be available to the template.
1286 * @return string the HTML to be output.
1287 */
1288 protected function render_template($_template, $_namedarguments) {
1289 // Note, we intentionally break the coding guidelines with regards to
1290 // local variable names used in this function, so that they do not clash
1291 // with the names of any variables being passed to the template.
a5cb8d69 1292
ebebf55c 1293 global $CFG, $SITE, $THEME, $USER;
1294 // The next lines are a bit tricky. The point is, here we are in a method
1295 // of a renderer class, and this object may, or may not, be the the same as
1296 // the global $OUTPUT object. When rendering the template, we want to use
1297 // this object. However, people writing Moodle code expect the current
1298 // rederer to be called $OUTPUT, not $this, so define a variable called
1299 // $OUTPUT pointing at $this. The same comment applies to $PAGE and $COURSE.
1300 $OUTPUT = $this;
1301 $PAGE = $this->page;
1302 $COURSE = $this->page->course;
a5cb8d69 1303
ebebf55c 1304 // And the parameters from the function call.
1305 extract($_namedarguments);
a5cb8d69 1306
ebebf55c 1307 // Include the template, capturing the output.
1308 ob_start();
1309 include($_template);
1310 $_result = ob_get_contents();
1311 ob_end_clean();
a5cb8d69 1312
ebebf55c 1313 return $_result;
a5cb8d69 1314 }
1315
ebebf55c 1316 /**
1317 * Searches the folders in {@link $searchpaths} to try to find a template for
1318 * this method name. Throws an exception if one cannot be found.
1319 * @param string $method the method name.
1320 * @return string the full path of the template to use.
1321 */
1322 protected function find_template($method) {
1323 foreach ($this->searchpaths as $path) {
1324 $filename = $path . '/' . $method . '.php';
1325 if (file_exists($filename)) {
1326 return $filename;
1327 }
1328 }
1329 throw new coding_exception('Cannot find template for ' . $this->copiedclass->getName() . '::' . $method);
8954245a 1330 }
571fa828 1331
ebebf55c 1332 /**
1333 * Handle methods like print_box_start by using the print_box template,
1334 * splitting the result, pusing the end onto the stack, then returning the start.
1335 * @param string $method the method that was called, with _start stripped off.
1336 * @param array $arguments the arguments that were passed to it.
1337 * @return string the HTML to be output.
1338 */
1339 protected function process_start($template, $arguments) {
1340 array_unshift($arguments, self::contentstoken);
1341 $html = $this->process_template($template, $arguments);
1342 list($start, $end) = explode(self::contentstoken, $html, 2);
1343 $this->opencontainers->push($template, $end);
1344 return $start;
8954245a 1345 }
1346
ebebf55c 1347 /**
1348 * Handle methods like print_box_end, we just need to pop the end HTML from
1349 * the stack.
1350 * @param string $method the method that was called, with _end stripped off.
1351 * @param array $arguments not used. Assumed to be irrelevant.
1352 * @return string the HTML to be output.
1353 */
1354 protected function process_end($template, $arguments) {
1355 return $this->opencontainers->pop($template);
8954245a 1356 }
1357
ebebf55c 1358 /**
1359 * @return array the list of paths where this class searches for templates.
1360 */
1361 public function get_search_paths() {
1362 return $this->searchpaths;
8954245a 1363 }
1364
1365 /**
ebebf55c 1366 * @return string the name of the class whose API we are copying.
8954245a 1367 */
ebebf55c 1368 public function get_copied_class() {
1369 return $this->copiedclass->getName();
1370 }
1371}
8954245a 1372
8954245a 1373
ebebf55c 1374/**
1375 * This class keeps track of which HTML tags are currently open.
1376 *
1377 * This makes it much easier to always generate well formed XHTML output, even
1378 * if execution terminates abruptly. Any time you output some opening HTML
1379 * without the matching closing HTML, you should push the neccessary close tags
1380 * onto the stack.
1381 *
1382 * @copyright 2009 Tim Hunt
1383 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1384 * @since Moodle 2.0
1385 */
1386class xhtml_container_stack {
1387 /** @var array stores the list of open containers. */
1388 protected $opencontainers = array();
4af1e3b0 1389 /**
1390 * @var array in developer debug mode, stores a stack trace of all opens and
1391 * closes, so we can output helpful error messages when there is a mismatch.
1392 */
1393 protected $log = array();
8954245a 1394
ebebf55c 1395 /**
1396 * Push the close HTML for a recently opened container onto the stack.
1397 * @param string $type The type of container. This is checked when {@link pop()}
1398 * is called and must match, otherwise a developer debug warning is output.
1399 * @param string $closehtml The HTML required to close the container.
1400 */
1401 public function push($type, $closehtml) {
1402 $container = new stdClass;
1403 $container->type = $type;
1404 $container->closehtml = $closehtml;
4af1e3b0 1405 if (debugging('', DEBUG_DEVELOPER)) {
1406 $this->log('Open', $type);
1407 }
ebebf55c 1408 array_push($this->opencontainers, $container);
1409 }
8954245a 1410
ebebf55c 1411 /**
1412 * Pop the HTML for the next closing container from the stack. The $type
1413 * must match the type passed when the container was opened, otherwise a
1414 * warning will be output.
1415 * @param string $type The type of container.
1416 * @return string the HTML requried to close the container.
1417 */
1418 public function pop($type) {
1419 if (empty($this->opencontainers)) {
4af1e3b0 1420 debugging('<p>There are no more open containers. This suggests there is a nesting problem.</p>' .
1421 $this->output_log(), DEBUG_DEVELOPER);
ebebf55c 1422 return;
8954245a 1423 }
1424
ebebf55c 1425 $container = array_pop($this->opencontainers);
1426 if ($container->type != $type) {
4af1e3b0 1427 debugging('<p>The type of container to be closed (' . $container->type .
ebebf55c 1428 ') does not match the type of the next open container (' . $type .
4af1e3b0 1429 '). This suggests there is a nesting problem.</p>' .
1430 $this->output_log(), DEBUG_DEVELOPER);
1431 }
1432 if (debugging('', DEBUG_DEVELOPER)) {
1433 $this->log('Close', $type);
8954245a 1434 }
ebebf55c 1435 return $container->closehtml;
8954245a 1436 }
1437
8954245a 1438 /**
ebebf55c 1439 * Close all but the last open container. This is useful in places like error
1440 * handling, where you want to close all the open containers (apart from <body>)
1441 * before outputting the error message.
4af1e3b0 1442 * @param $shouldbenone assert that the stack shold be empty now - causes a
1443 * developer debug warning if it isn't.
ebebf55c 1444 * @return string the HTML requried to close any open containers inside <body>.
8954245a 1445 */
4af1e3b0 1446 public function pop_all_but_last($shouldbenone = false) {
1447 if ($shouldbenone && count($this->opencontainers) != 1) {
1448 debugging('<p>Some HTML tags were opened in the body of the page but not closed.</p>' .
1449 $this->output_log(), DEBUG_DEVELOPER);
1450 }
ebebf55c 1451 $output = '';
1452 while (count($this->opencontainers) > 1) {
1453 $container = array_pop($this->opencontainers);
1454 $output .= $container->closehtml;
8954245a 1455 }
ebebf55c 1456 return $output;
8954245a 1457 }
34a2777c 1458
1459 /**
ebebf55c 1460 * You can call this function if you want to throw away an instance of this
1461 * class without properly emptying the stack (for example, in a unit test).
1462 * Calling this method stops the destruct method from outputting a developer
1463 * debug warning. After calling this method, the instance can no longer be used.
34a2777c 1464 */
ebebf55c 1465 public function discard() {
1466 $this->opencontainers = null;
1467 }
34a2777c 1468
ebebf55c 1469 /**
1470 * Emergency fallback. If we get to the end of processing and not all
1471 * containers have been closed, output the rest with a developer debug warning.
1472 */
1473 public function __destruct() {
1474 if (empty($this->opencontainers)) {
1475 return;
34a2777c 1476 }
1477
4af1e3b0 1478 debugging('<p>Some containers were left open. This suggests there is a nesting problem.</p>' .
1479 $this->output_log(), DEBUG_DEVELOPER);
ebebf55c 1480 echo $this->pop_all_but_last();
1481 $container = array_pop($this->opencontainers);
1482 echo $container->closehtml;
1483 }
4af1e3b0 1484
1485 protected function log($action, $type) {
1486 $this->log[] = '<li>' . $action . ' ' . $type . ' at:' .
fdeb7fa1 1487 format_backtrace(debug_backtrace()) . '</li>';
4af1e3b0 1488 }
1489
1490 protected function output_log() {
1491 return '<ul>' . implode("\n", $this->log) . '</ul>';
1492 }
ebebf55c 1493}
34a2777c 1494
34a2777c 1495
ebebf55c 1496/**
1497 * The standard implementation of the moodle_core_renderer interface.
1498 *
1499 * @copyright 2009 Tim Hunt
1500 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1501 * @since Moodle 2.0
1502 */
1503class moodle_core_renderer extends moodle_renderer_base {
fdeb7fa1 1504 /** @var string used in {@link header()}. */
ebebf55c 1505 const PERFORMANCE_INFO_TOKEN = '%%PERFORMANCEINFO%%';
fdeb7fa1 1506 /** @var string used in {@link header()}. */
ebebf55c 1507 const END_HTML_TOKEN = '%%ENDHTML%%';
fdeb7fa1 1508 /** @var string used in {@link header()}. */
ebebf55c 1509 const MAIN_CONTENT_TOKEN = '[MAIN CONTENT GOES HERE]';
fdeb7fa1 1510 /** @var string used to pass information from {@link doctype()} to {@link standard_head_html()}. */
ebebf55c 1511 protected $contenttype;
fdeb7fa1 1512 /** @var string used by {@link redirect_message()} method to communicate with {@link header()}. */
ebebf55c 1513 protected $metarefreshtag = '';
1514
fdeb7fa1 1515 /**
1516 * Get the DOCTYPE declaration that should be used with this page. Designed to
1517 * be called in theme layout.php files.
1518 * @return string the DOCTYPE declaration (and any XML prologue) that should be used.
1519 */
ebebf55c 1520 public function doctype() {
1521 global $CFG;
1522
1523 $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n";
1524 $this->contenttype = 'text/html; charset=utf-8';
1525
1526 if (empty($CFG->xmlstrictheaders)) {
1527 return $doctype;
34a2777c 1528 }
1529
ebebf55c 1530 // We want to serve the page with an XML content type, to force well-formedness errors to be reported.
1531 $prolog = '<?xml version="1.0" encoding="utf-8"?>' . "\n";
1532 if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'application/xhtml+xml') !== false) {
1533 // Firefox and other browsers that can cope natively with XHTML.
1534 $this->contenttype = 'application/xhtml+xml; charset=utf-8';
34a2777c 1535
ebebf55c 1536 } else if (preg_match('/MSIE.*Windows NT/', $_SERVER['HTTP_USER_AGENT'])) {
1537 // IE can't cope with application/xhtml+xml, but it will cope if we send application/xml with an XSL stylesheet.
1538 $this->contenttype = 'application/xml; charset=utf-8';
1539 $prolog .= '<?xml-stylesheet type="text/xsl" href="' . $CFG->httpswwwroot . '/lib/xhtml.xsl"?>' . "\n";
34a2777c 1540
ebebf55c 1541 } else {
1542 $prolog = '';
1543 }
1544
1545 return $prolog . $doctype;
34a2777c 1546 }
1547
fdeb7fa1 1548 /**
1549 * The attributes that should be added to the <html> tag. Designed to
1550 * be called in theme layout.php files.
1551 * @return string HTML fragment.
1552 */
ebebf55c 1553 public function htmlattributes() {
1554 return get_html_lang(true) . ' xmlns="http://www.w3.org/1999/xhtml"';
34a2777c 1555 }
1556
fdeb7fa1 1557 /**
1558 * The standard tags (meta tags, links to stylesheets and JavaScript, etc.)
1559 * that should be included in the <head> tag. Designed to be called in theme
1560 * layout.php files.
1561 * @return string HTML fragment.
1562 */
ebebf55c 1563 public function standard_head_html() {
fdeb7fa1 1564 global $CFG;
ebebf55c 1565 $output = '';
1566 $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . "\n";
1567 $output .= '<meta name="keywords" content="moodle, ' . $this->page->title . '" />' . "\n";
1568 if (!$this->page->cacheable) {
1569 $output .= '<meta http-equiv="pragma" content="no-cache" />' . "\n";
1570 $output .= '<meta http-equiv="expires" content="0" />' . "\n";
34a2777c 1571 }
ebebf55c 1572 // This is only set by the {@link redirect()} method
1573 $output .= $this->metarefreshtag;
1574
1575 // Check if a periodic refresh delay has been set and make sure we arn't
1576 // already meta refreshing
1577 if ($this->metarefreshtag=='' && $this->page->periodicrefreshdelay!==null) {
e11a8328 1578 $output .= '<meta http-equiv="refresh" content="'.$this->page->periodicrefreshdelay.';url='.$this->page->url->out().'" />';
ebebf55c 1579 }
1580
e11a8328 1581 $this->page->requires->js('lib/javascript-static.js')->in_head();
1582 $this->page->requires->js('lib/javascript-mod.php')->in_head();
1583 $this->page->requires->js('lib/overlib/overlib.js')->in_head();
1584 $this->page->requires->js('lib/overlib/overlib_cssstyle.js')->in_head();
1585 $this->page->requires->js('lib/cookies.js')->in_head();
e11a8328 1586 $this->page->requires->js_function_call('setTimeout', Array('fix_column_widths()', 20));
1587
1588 $focus = $this->page->focuscontrol;
1589 if (!empty($focus)) {
428acddb 1590 if(preg_match("#forms\['([a-zA-Z0-9]+)'\].elements\['([a-zA-Z0-9]+)'\]#", $focus, $matches)) {
1591 // This is a horrifically bad way to handle focus but it is passed in
1592 // through messy formslib::moodleform
1593 $this->page->requires->js_function_call('old_onload_focus', Array($matches[1], $matches[2]));
1594 } else if (strpos($focus, '.')!==false) {
e11a8328 1595 // Old style of focus, bad way to do it
428acddb 1596 debugging('This code is using the old style focus event, Please update this code to focus on an element id or the moodleform focus method.', DEBUG_DEVELOPER);
1597 $this->page->requires->js_function_call('old_onload_focus', explode('.', $focus, 2));
e11a8328 1598 } else {
1599 // Focus element with given id
1600 $this->page->requires->js_function_call('focuscontrol', Array($focus));
1601 }
1602 }
fdeb7fa1 1603
1604 // Add the meta tags from the themes if any were requested.
1605 $output .= $this->page->theme->get_meta_tags($this->page);
1606
1607 // Get any HTML from the page_requirements_manager.
ebebf55c 1608 $output .= $this->page->requires->get_head_code();
1609
1610 // List alternate versions.
1611 foreach ($this->page->alternateversions as $type => $alt) {
1612 $output .= $this->output_empty_tag('link', array('rel' => 'alternate',
1613 'type' => $type, 'title' => $alt->title, 'href' => $alt->url));
1614 }
1615
ebebf55c 1616 return $output;
34a2777c 1617 }
1618
fdeb7fa1 1619 /**
1620 * The standard tags (typically skip links) that should be output just inside
1621 * the start of the <body> tag. Designed to be called in theme layout.php files.
1622 * @return string HTML fragment.
1623 */
ebebf55c 1624 public function standard_top_of_body_html() {
1625 return $this->page->requires->get_top_of_body_code();
34a2777c 1626 }
1627
fdeb7fa1 1628 /**
1629 * The standard tags (typically performance information and validation links,
1630 * if we are indevelope debug mode) that should be output in the footer area
1631 * of the page. Designed to be called in theme layout.php files.
1632 * @return string HTML fragment.
1633 */
ebebf55c 1634 public function standard_footer_html() {
fdeb7fa1 1635 // This function is normally called from a layout.php file in {@link header()}
1636 // but some of the content won't be known until later, so we return a placeholder
1637 // for now. This will be replaced with the real content in {@link footer()}.
ebebf55c 1638 $output = self::PERFORMANCE_INFO_TOKEN;
1639 if (debugging()) {
1640 $output .= '<div class="validators"><ul>
1641 <li><a href="http://validator.w3.org/check?verbose=1&amp;ss=1&amp;uri=' . urlencode(qualified_me()) . '">Validate HTML</a></li>
1642 <li><a href="http://www.contentquality.com/mynewtester/cynthia.exe?rptmode=-1&amp;url1=' . urlencode(qualified_me()) . '">Section 508 Check</a></li>
1643 <li><a href="http://www.contentquality.com/mynewtester/cynthia.exe?rptmode=0&amp;warnp2n3e=1&amp;url1=' . urlencode(qualified_me()) . '">WCAG 1 (2,3) Check</a></li>
1644 </ul></div>';
34a2777c 1645 }
ebebf55c 1646 return $output;
34a2777c 1647 }
1648
fdeb7fa1 1649 /**
1650 * The standard tags (typically script tags that are not needed earlier) that
1651 * should be output after everything else, . Designed to be called in theme layout.php files.
1652 * @return string HTML fragment.
1653 */
ebebf55c 1654 public function standard_end_of_body_html() {
fdeb7fa1 1655 // This function is normally called from a layout.php file in {@link header()}
1656 // but some of the content won't be known until later, so we return a placeholder
1657 // for now. This will be replaced with the real content in {@link footer()}.
ebebf55c 1658 echo self::END_HTML_TOKEN;
34a2777c 1659 }
1660
fdeb7fa1 1661 /**
1662 * Return the standard string that says whether you are logged in (and switched
1663 * roles/logged in as another user).
1664 * @return string HTML fragment.
1665 */
ebebf55c 1666 public function login_info() {
1667 global $USER;
1668 return user_login_string($this->page->course, $USER);
34a2777c 1669 }
1670
fdeb7fa1 1671 /**
1672 * Return the 'back' link that normally appears in the footer.
1673 * @return string HTML fragment.
1674 */
ebebf55c 1675 public function home_link() {
1676 global $CFG, $SITE;
34a2777c 1677
ebebf55c 1678 if ($this->page->pagetype == 'site-index') {
1679 // Special case for site home page - please do not remove
1680 return '<div class="sitelink">' .
1681 '<a title="Moodle ' . $CFG->release . '" href="http://moodle.org/">' .
1682 '<img style="width:100px;height:30px" src="' . $CFG->httpswwwroot . '/pix/moodlelogo.gif" alt="moodlelogo" /></a></div>';
34a2777c 1683
ebebf55c 1684 } else if (!empty($CFG->target_release) && $CFG->target_release != $CFG->release) {
1685 // Special case for during install/upgrade.
1686 return '<div class="sitelink">'.
1687 '<a title="Moodle ' . $CFG->target_release . '" href="http://docs.moodle.org/en/Administrator_documentation" onclick="this.target=\'_blank\'">' .
1688 '<img style="width:100px;height:30px" src="' . $CFG->httpswwwroot . '/pix/moodlelogo.gif" alt="moodlelogo" /></a></div>';
34a2777c 1689
ebebf55c 1690 } else if ($this->page->course->id == $SITE->id || strpos($this->page->pagetype, 'course-view') === 0) {
1691 return '<div class="homelink"><a href="' . $CFG->wwwroot . '/">' .
1692 get_string('home') . '</a></div>';
1693
1694 } else {
1695 return '<div class="homelink"><a href="' . $CFG->wwwroot . '/course/view.php?id=' . $this->page->course->id . '">' .
1696 format_string($this->page->course->shortname) . '</a></div>';
1697 }
34a2777c 1698 }
a5cb8d69 1699
1700 /**
ebebf55c 1701 * Redirects the user by any means possible given the current state
1702 *
1703 * This function should not be called directly, it should always be called using
1704 * the redirect function in lib/weblib.php
1705 *
1706 * The redirect function should really only be called before page output has started
1707 * however it will allow itself to be called during the state STATE_IN_BODY
1708 *
1709 * @param string $encodedurl The URL to send to encoded if required
1710 * @param string $message The message to display to the user if any
1711 * @param int $delay The delay before redirecting a user, if $message has been
1712 * set this is a requirement and defaults to 3, set to 0 no delay
1713 * @param string $messageclass The css class to put on the message that is
1714 * being displayed to the user
ae96b517 1715 * @param boolean $debugdisableredirect this redirect has been disabled for
1716 * debugging purposes. Display a message that explains, and don't
1717 * trigger the redirect.
ebebf55c 1718 * @return string The HTML to display to the user before dying, may contain
1719 * meta refresh, javascript refresh, and may have set header redirects
a5cb8d69 1720 */
ae96b517 1721 public function redirect_message($encodedurl, $message, $delay, $debugdisableredirect) {
ebebf55c 1722 global $CFG;
1723 $url = str_replace('&amp;', '&', $encodedurl);
8954245a 1724
ebebf55c 1725 switch ($this->page->state) {
1726 case moodle_page::STATE_BEFORE_HEADER :
1727 // No output yet it is safe to delivery the full arsenol of redirect methods
ae96b517 1728 if (!$debugdisableredirect) {
1729 // Don't use exactly the same time here, it can cause problems when both redirects fire at the same time.
ebebf55c 1730 $this->metarefreshtag = '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />'."\n";
ae96b517 1731 $this->page->requires->js_function_call('document.location.replace', array($url))->after_delay($delay + 3);
ebebf55c 1732 }
1733 $output = $this->header();
ebebf55c 1734 break;
1735 case moodle_page::STATE_PRINTING_HEADER :
1736 // We should hopefully never get here
1737 throw new coding_exception('You cannot redirect while printing the page header');
1738 break;
1739 case moodle_page::STATE_IN_BODY :
1740 // We really shouldn't be here but we can deal with this
1741 debugging("You should really redirect before you start page output");
1742 if (!$disableredirect) {
ae96b517 1743 $this->page->requires->js_function_call('document.location.replace', array($url))->after_delay($delay);
ebebf55c 1744 }
1745 $output = $this->opencontainers->pop_all_but_last();
ebebf55c 1746 break;
1747 case moodle_page::STATE_DONE :
1748 // Too late to be calling redirect now
1749 throw new coding_exception('You cannot redirect after the entire page has been generated');
1750 break;
1751 }
ae96b517 1752 $output .= $this->notification($message, 'redirectmessage');
1753 $output .= '<a href="'. $encodedurl .'">'. get_string('continue') .'</a>';
1754 if ($debugdisableredirect) {
1755 $output .= '<p><strong>Error output, so disabling automatic redirect.</strong></p>';
1756 }
1757 $output .= $this->footer();
ebebf55c 1758 return $output;
1759 }
b7009474 1760
fdeb7fa1 1761 /**
1762 * Start output by sending the HTTP headers, and printing the HTML <head>
1763 * and the start of the <body>.
1764 *
1765 * To control what is printed, you should set properies on $PAGE. If you
1766 * are familiar with the old {@link print_header()} function from Moodle 1.9
1767 * you will find that there are properties on $PAGE that correspond to most
1768 * of the old paramters to could be passed to print_header.
1769 *
1770 * Not that, in due course, the remaining $navigation, $menu paramters here
1771 * will be replaced by more properties of $PAGE, but that is still to do.
1772 *
1773 * @param $navigation legacy, like the old paramter to print_header. Will be
1774 * removed when there is a $PAGE->... replacement.
1775 * @param $menu legacy, like the old paramter to print_header. Will be
1776 * removed when there is a $PAGE->... replacement.
1777 * @return string HTML that you must output this, preferably immediately.
1778 */
ebebf55c 1779 public function header($navigation = '', $menu='') {
fdeb7fa1 1780 // TODO remove $navigation and $menu arguments - replace with $PAGE->navigation
ebebf55c 1781 global $USER, $CFG;
b7009474 1782
ebebf55c 1783 output_starting_hook();
1784 $this->page->set_state(moodle_page::STATE_PRINTING_HEADER);
b7009474 1785
ebebf55c 1786 // Find the appropriate page template, based on $this->page->generaltype.
fdeb7fa1 1787 $templatefile = $this->page->theme->find_page_template($this->page->generaltype);
ebebf55c 1788 if ($templatefile) {
1789 // Render the template.
1790 $template = $this->render_page_template($templatefile, $menu, $navigation);
1791 } else {
1792 // New style template not found, fall back to using header.html and footer.html.
1793 $template = $this->handle_legacy_theme($navigation, $menu);
1794 }
b7009474 1795
ebebf55c 1796 // Slice the template output into header and footer.
1797 $cutpos = strpos($template, self::MAIN_CONTENT_TOKEN);
1798 if ($cutpos === false) {
1799 throw new coding_exception('Layout template ' . $templatefile .
1800 ' does not contain the string "' . self::MAIN_CONTENT_TOKEN . '".');
1801 }
1802 $header = substr($template, 0, $cutpos);
1803 $footer = substr($template, $cutpos + strlen(self::MAIN_CONTENT_TOKEN));
b7009474 1804
ebebf55c 1805 send_headers($this->contenttype, $this->page->cacheable);
1806 $this->opencontainers->push('header/footer', $footer);
1807 $this->page->set_state(moodle_page::STATE_IN_BODY);
1808 return $header . $this->skip_link_target();
1809 }
b7009474 1810
ebebf55c 1811 protected function render_page_template($templatefile, $menu, $navigation) {
1812 global $CFG, $SITE, $THEME, $USER;
1813 // The next lines are a bit tricky. The point is, here we are in a method
1814 // of a renderer class, and this object may, or may not, be the the same as
1815 // the global $OUTPUT object. When rendering the template, we want to use
1816 // this object. However, people writing Moodle code expect the current
1817 // rederer to be called $OUTPUT, not $this, so define a variable called
1818 // $OUTPUT pointing at $this. The same comment applies to $PAGE and $COURSE.
1819 $OUTPUT = $this;
1820 $PAGE = $this->page;
1821 $COURSE = $this->page->course;
b7009474 1822
ebebf55c 1823 ob_start();
1824 include($templatefile);
1825 $template = ob_get_contents();
1826 ob_end_clean();
1827 return $template;
1828 }
b7009474 1829
ebebf55c 1830 protected function handle_legacy_theme($navigation, $menu) {
1831 global $CFG, $SITE, $THEME, $USER;
1832 // Set a pretend global from the properties of this class.
1833 // See the comment in render_page_template for a fuller explanation.
1834 $COURSE = $this->page->course;
1835
1836 // Set up local variables that header.html expects.
1837 $direction = $this->htmlattributes();
1838 $title = $this->page->title;
1839 $heading = $this->page->heading;
1840 $focus = $this->page->focuscontrol;
1841 $button = $this->page->button;
1842 $pageid = $this->page->pagetype;
1843 $pageclass = $this->page->bodyclasses;
1844 $bodytags = ' class="' . $pageclass . '" id="' . $pageid . '"';
1845 $home = $this->page->generaltype == 'home';
1846
1847 $meta = $this->standard_head_html();
1848 // The next line is a nasty hack. having set $meta to standard_head_html, we have already
1849 // got the contents of include($CFG->javascript). However, legacy themes are going to
1850 // include($CFG->javascript) again. We want to make sure that when they do, nothing is output.
1851 $CFG->javascript = $CFG->libdir . '/emptyfile.php';
b7009474 1852
ebebf55c 1853 // Set up local variables that footer.html expects.
1854 $homelink = $this->home_link();
1855 $loggedinas = $this->login_info();
1856 $course = $this->page->course;
1857 $performanceinfo = self::PERFORMANCE_INFO_TOKEN;
b7009474 1858
ebebf55c 1859 if (!$menu && $navigation) {
1860 $menu = $loggedinas;
1861 }
b7009474 1862
ebebf55c 1863 ob_start();
1864 include($THEME->dir . '/header.html');
1865 $this->page->requires->get_top_of_body_code();
1866 echo self::MAIN_CONTENT_TOKEN;
b7009474 1867
ebebf55c 1868 $menu = str_replace('navmenu', 'navmenufooter', $menu);
1869 include($THEME->dir . '/footer.html');
b7009474 1870
ebebf55c 1871 $output = ob_get_contents();
1872 ob_end_clean();
b7009474 1873
ebebf55c 1874 $output = str_replace('</body>', self::END_HTML_TOKEN . '</body>', $output);
b7009474 1875
ebebf55c 1876 return $output;
1877 }
b7009474 1878
ebebf55c 1879 public function footer() {
4af1e3b0 1880 $output = $this->opencontainers->pop_all_but_last(true);
b7009474 1881
ebebf55c 1882 $footer = $this->opencontainers->pop('header/footer');
b7009474 1883
ebebf55c 1884 // Provide some performance info if required
1885 $performanceinfo = '';
1886 if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) {
1887 $perf = get_performance_info();
1888 if (defined('MDL_PERFTOLOG') && !function_exists('register_shutdown_function')) {
1889 error_log("PERF: " . $perf['txt']);
1890 }
1891 if (defined('MDL_PERFTOFOOT') || debugging() || $CFG->perfdebug > 7) {
1892 $performanceinfo = $perf['html'];
1893 }
1894 }
1895 $footer = str_replace(self::PERFORMANCE_INFO_TOKEN, $performanceinfo, $footer);
b7009474 1896
ebebf55c 1897 $footer = str_replace(self::END_HTML_TOKEN, $this->page->requires->get_end_code(), $footer);
b7009474 1898
ebebf55c 1899 $this->page->set_state(moodle_page::STATE_DONE);
b7009474 1900
ebebf55c 1901 return $output . $footer;
1902 }
b7009474 1903
ebebf55c 1904 /**
1905 * Prints a nice side block with an optional header.
1906 *
1907 * The content is described
1908 * by a {@link block_contents} object.
1909 *
1910 * @param block $content HTML for the content
1911 * @return string the HTML to be output.
1912 */
1913 function block($bc) {
1914 $bc = clone($bc);
1915 $bc->prepare();
b7009474 1916
ebebf55c 1917 $title = strip_tags($bc->title);
1918 if (empty($title)) {
1919 $output = '';
1920 $skipdest = '';
1921 } else {
1922 $output = $this->output_tag('a', array('href' => '#sb-' . $bc->skipid, 'class' => 'skip-block'),
1923 get_string('skipa', 'access', $title));
1924 $skipdest = $this->output_tag('span', array('id' => 'sb-' . $bc->skipid, 'class' => 'skip-block-to'), '');
1925 }
b7009474 1926
ebebf55c 1927 $bc->attributes['id'] = $bc->id;
1928 $bc->attributes['class'] = $bc->get_classes_string();
1929 $output .= $this->output_start_tag('div', $bc->attributes);
b7009474 1930
ebebf55c 1931 if ($bc->heading) {
1932 // Some callers pass in complete html for the heading, which may include
1933 // complicated things such as the 'hide block' button; some just pass in
1934 // text. If they only pass in plain text i.e. it doesn't include a
1935 // <div>, then we add in standard tags that make it look like a normal
1936 // page block including the h2 for accessibility
1937 if (strpos($bc->heading, '</div>') === false) {
1938 $bc->heading = $this->output_tag('div', array('class' => 'title'),
1939 $this->output_tag('h2', null, $bc->heading));
1940 }
b7009474 1941
ebebf55c 1942 $output .= $this->output_tag('div', array('class' => 'header'), $bc->heading);
1943 }
b7009474 1944
ebebf55c 1945 $output .= $this->output_start_tag('div', array('class' => 'content'));
b7009474 1946
ebebf55c 1947 if ($bc->content) {
1948 $output .= $bc->content;
b7009474 1949
ebebf55c 1950 } else if ($bc->list) {
1951 $row = 0;
1952 $items = array();
1953 foreach ($bc->list as $key => $string) {
1954 $item = $this->output_start_tag('li', array('class' => 'r' . $row));
1955 if ($bc->icons) {
1956 $item .= $this->output_tag('div', array('class' => 'icon column c0'), $bc->icons[$key]);
1957 }
1958 $item .= $this->output_tag('div', array('class' => 'column c1'), $string);
1959 $item .= $this->output_end_tag('li');
1960 $items[] = $item;
1961 $row = 1 - $row; // Flip even/odd.
1962 }
1963 $output .= $this->output_tag('ul', array('class' => 'list'), implode("\n", $items));
1964 }
b7009474 1965
ebebf55c 1966 if ($bc->footer) {
1967 $output .= $this->output_tag('div', array('class' => 'footer'), $bc->footer);
1968 }
b7009474 1969
ebebf55c 1970 $output .= $this->output_end_tag('div');
1971 $output .= $this->output_end_tag('div');
1972 $output .= $skipdest;
b7009474 1973
ebebf55c 1974 if (!empty($CFG->allowuserblockhiding) && isset($attributes['id'])) {
1975 $strshow = addslashes_js(get_string('showblocka', 'access', $title));
1976 $strhide = addslashes_js(get_string('hideblocka', 'access', $title));
1977 $output .= $this->page->requires->js_function_call('elementCookieHide', array(
1978 $bc->id, $strshow, $strhide))->asap();
1979 }
b7009474 1980
ebebf55c 1981 return $output;
1982 }
b7009474 1983
ebebf55c 1984 public function link_to_popup_window() {
b7009474 1985
ebebf55c 1986 }
b7009474 1987
ebebf55c 1988 public function button_to_popup_window() {
b7009474 1989
ebebf55c 1990 }
b7009474 1991
ebebf55c 1992 public function close_window_button($buttontext = null, $reloadopener = false) {
1993 if (empty($buttontext)) {
1994 $buttontext = get_string('closewindow');
1995 }
1996 // TODO
1997 }
b7009474 1998
ebebf55c 1999 public function close_window($delay = 0, $reloadopener = false) {
2000 // TODO
2001 }
b7009474 2002
ebebf55c 2003 /**
2004 * Output a <select> menu.
2005 *
2006 * You can either call this function with a single moodle_select_menu argument
2007 * or, with a list of parameters, in which case those parameters are sent to
2008 * the moodle_select_menu constructor.
2009 *
2010 * @param moodle_select_menu $selectmenu a moodle_select_menu that describes
2011 * the select menu you want output.
2012 * @return string the HTML for the <select>
2013 */
2014 public function select_menu($selectmenu) {
2015 $selectmenu = clone($selectmenu);
2016 $selectmenu->prepare();
b7009474 2017
ebebf55c 2018 if ($selectmenu->nothinglabel) {
2019 $selectmenu->options = array($selectmenu->nothingvalue => $selectmenu->nothinglabel) +
2020 $selectmenu->options;
2021 }
b7009474 2022
ebebf55c 2023 if (empty($selectmenu->id)) {
2024 $selectmenu->id = 'menu' . str_replace(array('[', ']'), '', $selectmenu->name);
2025 }
b7009474 2026
ebebf55c 2027 $attributes = array(
2028 'name' => $selectmenu->name,
2029 'id' => $selectmenu->id,
2030 'class' => $selectmenu->get_classes_string(),
2031 'onchange' => $selectmenu->script,
2032 );
2033 if ($selectmenu->disabled) {
2034 $attributes['disabled'] = 'disabled';
2035 }
2036 if ($selectmenu->tabindex) {
2037 $attributes['tabindex'] = $tabindex;
2038 }
b7009474 2039
ebebf55c 2040 if ($selectmenu->listbox) {
2041 if (is_integer($selectmenu->listbox)) {
2042 $size = $selectmenu->listbox;
2043 } else {
2044 $size = min($selectmenu->maxautosize, count($selectmenu->options));
2045 }
2046 $attributes['size'] = $size;
2047 if ($selectmenu->multiple) {
2048 $attributes['multiple'] = 'multiple';
2049 }
2050 }
b7009474 2051
ebebf55c 2052 $html = $this->output_start_tag('select', $attributes) . "\n";
2053 foreach ($selectmenu->options as $value => $label) {
2054 $attributes = array('value' => $value);
2055 if ((string)$value == (string)$selectmenu->selectedvalue ||
2056 (is_array($selectmenu->selectedvalue) && in_array($value, $selectmenu->selectedvalue))) {
2057 $attributes['selected'] = 'selected';
2058 }
2059 $html .= ' ' . $this->output_tag('option', $attributes, s($label)) . "\n";
2060 }
2061 $html .= $this->output_end_tag('select') . "\n";
b7009474 2062
ebebf55c 2063 return $html;
2064 }
b7009474 2065
ebebf55c 2066 // TODO choose_from_menu_nested
2067
2068 // TODO choose_from_radio
b7009474 2069
2070 /**
ebebf55c 2071 * Output an error message. By default wraps the error message in <span class="error">.
2072 * If the error message is blank, nothing is output.
2073 * @param $message the error message.
2074 * @return string the HTML to output.
b7009474 2075 */
ebebf55c 2076 public function error_text($message) {
2077 if (empty($message)) {
2078 return '';
2079 }
2080 return $this->output_tag('span', array('class' => 'error'), $message);
2081 }
b7009474 2082
2083 /**
ebebf55c 2084 * Do not call this function directly.
b7009474 2085 *
ebebf55c 2086 * To terminate the current script with a fatal error, call the {@link print_error}
2087 * function, or throw an exception. Doing either of those things will then call this
2088 * funciton to display the error, before terminating the exection.
2089 *
2090 * @param string $message
2091 * @param string $moreinfourl
2092 * @param string $link
2093 * @param array $backtrace
2094 * @param string $debuginfo
2095 * @param bool $showerrordebugwarning
2096 * @return string the HTML to output.
b7009474 2097 */
ebebf55c 2098 public function fatal_error($message, $moreinfourl, $link, $backtrace,
2099 $debuginfo = null, $showerrordebugwarning = false) {
b7009474 2100
ebebf55c 2101 $output = '';
b7009474 2102
ebebf55c 2103 if ($this->has_started()) {
2104 $output .= $this->opencontainers->pop_all_but_last();
2105 } else {
2106 // Header not yet printed
2107 @header('HTTP/1.0 404 Not Found');
2108 $this->page->set_title(get_string('error'));
2109 $output .= $this->header();
2110 }
b7009474 2111
ebebf55c 2112 $message = '<p class="errormessage">' . $message . '</p>'.
2113 '<p class="errorcode"><a href="' . $moreinfourl . '">' .
2114 get_string('moreinformation') . '</a></p>';
2115 $output .= $this->box($message, 'errorbox');
2116
2117 if (debugging('', DEBUG_DEVELOPER)) {
2118 if ($showerrordebugwarning) {
2119 $output .= $this->notification('error() is a deprecated function. ' .
2120 'Please call print_error() instead of error()', 'notifytiny');
2121 }
2122 if (!empty($debuginfo)) {
2123 $output .= $this->notification($debuginfo, 'notifytiny');
2124 }
2125 if (!empty($backtrace)) {
2126 $output .= $this->notification('Stack trace: ' .
2127 format_backtrace($backtrace), 'notifytiny');
2128 }
b7009474 2129 }
b7009474 2130
ebebf55c 2131 if (!empty($link)) {
2132 $output .= $this->continue_button($link);
2133 }
b7009474 2134
ebebf55c 2135 $output .= $this->footer();
2136
2137 // Padding to encourage IE to display our error page, rather than its own.
2138 $output .= str_repeat(' ', 512);
2139
2140 return $output;
b7009474 2141 }
2142
db8d89d8 2143 /**
ebebf55c 2144 * Output a notification (that is, a status message about something that has
2145 * just happened).
2146 *
2147 * @param string $message the message to print out
2148 * @param string $classes normally 'notifyproblem' or 'notifysuccess'.
2149 * @return string the HTML to output.
db8d89d8 2150 */
ebebf55c 2151 public function notification($message, $classes = 'notifyproblem') {
2152 return $this->output_tag('div', array('class' =>
2153 moodle_renderer_base::prepare_classes($classes)), clean_text($message));
db8d89d8 2154 }
2155
b7009474 2156 /**
ebebf55c 2157 * Print a continue button that goes to a particular URL.
2158 *
2159 * @param string|moodle_url $link The url the button goes to.
2160 * @return string the HTML to output.
b7009474 2161 */
ebebf55c 2162 public function continue_button($link) {
2163 if (!is_a($link, 'moodle_url')) {
2164 $link = new moodle_url($link);
b7009474 2165 }
ebebf55c 2166 return $this->output_tag('div', array('class' => 'continuebutton'),
2167 print_single_button($link->out(true), $link->params(), get_string('continue'), 'get', '', true));
b7009474 2168 }
2169
2170 /**
ebebf55c 2171 * Output the place a skip link goes to.
2172 * @param $id The target name from the corresponding $PAGE->requires->skip_link_to($target) call.
2173 * @return string the HTML to output.
b7009474 2174 */
ebebf55c 2175 public function skip_link_target($id = 'maincontent') {
2176 return $this->output_tag('span', array('id' => $id), '');
2177 }
b7009474 2178
ebebf55c 2179 public function heading($text, $level, $classes = 'main', $id = '') {
2180 $level = (integer) $level;
2181 if ($level < 1 or $level > 6) {
2182 throw new coding_exception('Heading level must be an integer between 1 and 6.');
b7009474 2183 }
ebebf55c 2184 return $this->output_tag('h' . $level,
2185 array('id' => $id, 'class' => moodle_renderer_base::prepare_classes($classes)), $text);
2186 }
b7009474 2187
ebebf55c 2188 public function box($contents, $classes = 'generalbox', $id = '') {
2189 return $this->box_start($classes, $id) . $contents . $this->box_end();
2190 }
b7009474 2191
ebebf55c 2192 public function box_start($classes = 'generalbox', $id = '') {
2193 $this->opencontainers->push('box', $this->output_end_tag('div'));
2194 return $this->output_start_tag('div', array('id' => $id,
2195 'class' => 'box ' . moodle_renderer_base::prepare_classes($classes)));
2196 }
b7009474 2197
ebebf55c 2198 public function box_end() {
2199 return $this->opencontainers->pop('box');
2200 }
b7009474 2201
ebebf55c 2202 public function container($contents, $classes = '', $id = '') {
2203 return $this->container_start($classes, $id) . $contents . $this->container_end();
2204 }
b7009474 2205
ebebf55c 2206 public function container_start($classes = '', $id = '') {
2207 $this->opencontainers->push('container', $this->output_end_tag('div'));
2208 return $this->output_start_tag('div', array('id' => $id,
2209 'class' => moodle_renderer_base::prepare_classes($classes)));
2210 }
2211
2212 public function container_end() {
2213 return $this->opencontainers->pop('container');
b7009474 2214 }
b7009474 2215}
2216
2217
8954245a 2218/**
2219 * Base class for classes representing HTML elements, like moodle_select_menu.
2220 *
2221 * Handles the id and class attribues.
2222 *
2223 * @copyright 2009 Tim Hunt
2224 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2225 * @since Moodle 2.0
2226 */
2227class moodle_html_component {
2228 /**
2229 * @var string value to use for the id attribute of this HTML tag.
2230 */
2231 public $id = '';
2232 /**
2233 * @var array class names to add to this HTML element.
2234 */
2235 public $classes = array();
2236
2237 /**
2238 * Ensure some class names are an array.
2239 * @param mixed $classes either an array of class names or a space-separated
2240 * string containing class names.
2241 * @return array the class names as an array.
2242 */
2243 public static function clean_clases($classes) {
2244 if (is_array($classes)) {
2245 return $classes;
2246 } else {
a5cb8d69 2247 return explode(' ', trim($classes));
8954245a 2248 }
2249 }
2250
2251 /**
2252 * Set the class name array.
2253 * @param mixed $classes either an array of class names or a space-separated
2254 * string containing class names.
2255 */
2256 public function set_classes($classes) {
2257 $this->classes = self::clean_clases($classes);
2258 }
2259
2260 /**
2261 * Add a class name to the class names array.
2262 * @param string $class the new class name to add.
2263 */
2264 public function add_class($class) {
2265 $this->classes[] = $class;
2266 }
2267
2268 /**
2269 * Add a whole lot of class names to the class names array.
2270 * @param mixed $classes either an array of class names or a space-separated
2271 * string containing class names.
2272 */
2273 public function add_classes($classes) {
2274 $this->classes += self::clean_clases($classes);
2275 }
2276
2277 /**
2278 * Get the class names as a string.
2279 * @return string the class names as a space-separated string. Ready to be put in the class="" attribute.
2280 */
2281 public function get_classes_string() {
2282 return implode(' ', $this->classes);
2283 }
2284
2285 /**
2286 * Perform any cleanup or final processing that should be done before an
2287 * instance of this class is output.
2288 */
2289 public function prepare() {
2290 $this->classes = array_unique(self::clean_clases($this->classes));
2291 }
2292}
2293
2294
2295/**
2296 * This class hold all the information required to describe a <select> menu that
34a2777c 2297 * will be printed by {@link moodle_core_renderer::select_menu()}. (Or by an overridden
8954245a 2298 * version of that method in a subclass.)
2299 *
2300 * All the fields that are not set by the constructor have sensible defaults, so
2301 * you only need to set the properties where you want non-default behaviour.
2302 *
2303 * @copyright 2009 Tim Hunt
2304 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2305 * @since Moodle 2.0
2306 */
2307class moodle_select_menu extends moodle_html_component {
2308 /**
2309 * @var array the choices to show in the menu. An array $value => $label.
2310 */
2311 public $options;
2312 /**
2313 * @var string the name of this form control. That is, the name of the GET/POST
2314 * variable that will be set if this select is submmitted as part of a form.
2315 */
2316 public $name;
2317 /**
2318 * @var string the option to select initially. Should match one
2319 * of the $options array keys. Default none.
2320 */
2321 public $selectedvalue;
2322 /**
2323 * @var string The label for the 'nothing is selected' option.
2324 * Defaults to get_string('choosedots').
2325 * Set this to '' if you do not want a 'nothing is selected' option.
2326 */
2327 public $nothinglabel = null;
2328 /**
2329 * @var string The value returned by the 'nothing is selected' option. Defaults to 0.
2330 */
2331 public $nothingvalue = 0;
2332 /**
2333 * @var boolean set this to true if you want the control to appear disabled.
2334 */
2335 public $disabled = false;
2336 /**
2337 * @var integer if non-zero, sets the tabindex attribute on the <select> element. Default 0.
2338 */
2339 public $tabindex = 0;
2340 /**
2341 * @var mixed Defaults to false, which means display the select as a dropdown menu.
2342 * If true, display this select as a list box whose size is chosen automatically.
2343 * If an integer, display as list box of that size.
2344 */
2345 public $listbox = false;
2346 /**
2347 * @var integer if you are using $listbox === true to get an automatically
2348 * sized list box, the size of the list box will be the number of options,
2349 * or this number, whichever is smaller.
2350 */
2351 public $maxautosize = 10;
2352 /**
2353 * @var boolean if true, allow multiple selection. Only used if $listbox is true.
2354 */
2355 public $multiple = false;
2356 /**
2357 * @deprecated
2358 * @var string JavaScript to add as an onchange attribute. Do not use this.
2359 * Use the YUI even library instead.
2360 */
2361 public $script = '';
2362
2363 /* @see lib/moodle_html_component#prepare() */
2364 public function prepare() {
2365 if (empty($this->id)) {
2366 $this->id = 'menu' . str_replace(array('[', ']'), '', $this->name);
2367 }
2368 if (empty($this->classes)) {
2369 $this->set_classes(array('menu' . str_replace(array('[', ']'), '', $this->name)));
2370 }
2371 $this->add_class('select');
2372 parent::prepare();
2373 }
2374
2375 /**
2376 * This is a shortcut for making a simple select menu. It lets you specify
2377 * the options, name and selected option in one line of code.
2378 * @param array $options used to initialise {@link $options}.
2379 * @param string $name used to initialise {@link $name}.
2380 * @param string $selected used to initialise {@link $selected}.
2381 * @return moodle_select_menu A moodle_select_menu object with the three common fields initialised.
2382 */
2383 public static function make($options, $name, $selected = '') {
2384 $menu = new moodle_select_menu();
2385 $menu->options = $options;
2386 $menu->name = $name;
2387 $menu->selectedvalue = $selected;
2388 return $menu;
2389 }
2390
2391 /**
2392 * This is a shortcut for making a yes/no select menu.
2393 * @param string $name used to initialise {@link $name}.
2394 * @param string $selected used to initialise {@link $selected}.
2395 * @return moodle_select_menu A menu initialised with yes/no options.
2396 */
2397 public static function make_yes_no($name, $selected) {
2398 return self::make(array(0 => get_string('no'), 1 => get_string('yes')), $name, $selected);
2399 }
571fa828 2400}
2401
2402
a5cb8d69 2403/**
2404 * This class hold all the information required to describe a Moodle block.
2405 *
2406 * That is, it holds all the different bits of HTML content that need to be put into the block.
2407 *
2408 * @copyright 2009 Tim Hunt
2409 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2410 * @since Moodle 2.0
2411 */
2412class block_contents extends moodle_html_component {
2413 protected static $idcounter = 1;
2414 /**
2415 * @param string $heading HTML for the heading. Can include full HTML or just
2416 * plain text - plain text will automatically be enclosed in the appropriate
2417 * heading tags.
2418 */
2419 public $heading = '';
2420 /**
2421 * @param string $title Plain text title, as embedded in the $heading.
2422 */
2423 public $title = '';
2424 /**
2425 * @param string $content HTML for the content
2426 */
2427 public $content = '';
2428 /**
2429 * @param array $list an alternative to $content, it you want a list of things with optional icons.
2430 */
2431 public $list = array();
2432 /**
2433 * @param array $icons optional icons for the things in $list.
2434 */
2435 public $icons = array();
2436 /**
2437 * @param string $footer Extra HTML content that gets output at the end, inside a &lt;div class="footer">
2438 */
2439 public $footer = '';
2440 /**
2441 * @param array $attributes an array of attribute => value pairs that are put on the
2442 * outer div of this block. {@link $id} and {@link $classes} attributes should be set separately.
2443 */
2444 public $attributes = array();
2445 /**
2446 * @param integer $skipid do not set this manually. It is set automatically be the {@link prepare()} method.
2447 */
2448 public $skipid;
2449
2450 /* @see lib/moodle_html_component#prepare() */
2451 public function prepare() {
2452 $this->skipid = self::$idcounter;
2453 self::$idcounter += 1;
2454 $this->add_class('sideblock');
2455 parent::prepare();
2456 }
2457}
2458
2459
34a2777c 2460/**
2461 * A renderer that generates output for commandlines scripts.
2462 *
2463 * The implementation of this renderer is probably incomplete.
2464 *
2465 * @copyright 2009 Tim Hunt
2466 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2467 * @since Moodle 2.0
2468 */
2469class cli_core_renderer extends moodle_core_renderer {
2470 public function header() {
2471 output_starting_hook();
2472 return $this->page->heading . "\n";
2473 }
2474
2475 public function heading($text, $level, $classes = 'main', $id = '') {
2476 $text .= "\n";
2477 switch ($level) {
2478 case 1:
2479 return '=>' . $text;
2480 case 2:
2481 return '-->' . $text;
2482 default:
2483 return $text;
2484 }
2485 }
2486
2487 public function fatal_error($errorcode, $module, $a, $link, $backtrace,
2488 $debuginfo = null, $showerrordebugwarning = false) {
2489 $output = "!!! $message !!!\n";
2490
2491 if (debugging('', DEBUG_DEVELOPER)) {
2492 if (!empty($debuginfo)) {
2493 $this->notification($debuginfo, 'notifytiny');
2494 }
2495 if (!empty($backtrace)) {
2496 $this->notification('Stack trace: ' . format_backtrace($backtrace, true), 'notifytiny');
2497 }
2498 }
2499 }
2500
2501 public function notification($message, $classes = 'notifyproblem') {
2502 $message = clean_text($message);
2503 if ($style === 'notifysuccess') {
2504 return "++ $message ++\n";
2505 }
2506 return "!! $message !!\n";
2507 }
2508}
2509
b7009474 2510
2511/**
2512 * Output CSS while replacing constants/variables. See MDL-6798 for details
2513 *
2514 * Information from Urs Hunkler:
2515 *
2516 * This is an adaptation of Shaun Inman's "CSS Server-side Constants" for Moodle.
2517 * http://www.shauninman.com/post/heap/2005/08/09/css_constants
2518 *
2519 * To use, specify $THEME->customcssoutputfunction = 'output_css_replacing_constants';
2520 * in your theme's config.php file.
2521 *
2522 * The constant definitions are written into a separate CSS file named like
2523 * constants.css and loaded first in config.php. You can use constants for any
2524 * CSS properties. The constant definition looks like:
2525 * <code>
2526 * \@server constants {
2527 * fontColor: #3a2830;
2528 * aLink: #116699;
2529 * aVisited: #AA2200;
2530 * aHover: #779911;
2531 * pageBackground: #FFFFFF;
2532 * backgroundColor: #EEEEEE;
2533 * backgroundSideblockHeader: #a8a4e9;
2534 * fontcolorSideblockHeader: #222222;
2535 * color1: #98818b;
2536 * color2: #bd807b;
2537 * color3: #f9d1d7;
2538 * color4: #e8d4d8;
2539 * }
2540 * </code>
2541 *
2542 * The lines in the CSS files using CSS constants look like:
2543 * <code>
2544 * body {
2545 * font-size: 100%;
2546 * background-color: pageBackground;
2547 * color: fontColor;
2548 * font-family: 'Bitstream Vera Serif', georgia, times, serif;
2549 * margin: 0;
2550 * padding: 0;
2551 * }
2552 * div#page {
2553 * margin: 0 10px;
2554 * padding-top: 5px;
2555 * border-top-width: 10px;
2556 * border-top-style: solid;
2557 * border-top-color: color3;
2558 * }
2559 * div.clearer {
2560 * clear: both;
2561 * }
2562 * a:link {
2563 * color: aLink;
fdeb7fa1 2564 * }
b7009474 2565 * </code>
2566 *
2567 * @param array $files an arry of the CSS fiels that need to be output.
fdeb7fa1 2568 * @param array $toreplace for convenience. If you are going to output the names
2569 * of the css files, for debugging purposes, then you should output
2570 * str_replace($toreplace, '', $file); becuase it looks prettier.
b7009474 2571 */
fdeb7fa1 2572function output_css_replacing_constants($files, $toreplace) {
b7009474 2573 // Get all the CSS.
b7009474 2574 ob_start();
2575 foreach ($files as $file) {
2576 $shortname = str_replace($toreplace, '', $file);
2577 echo '/******* ' . $shortname . " start *******/\n\n";
2578 @include_once($file);
2579 echo '/******* ' . $shortname . " end *******/\n\n";
2580 }
2581 $css = ob_get_contents();
2582 ob_end_clean();
2583
2584 if (preg_match_all("/@server\s+(?:variables|constants)\s*\{\s*([^\}]+)\s*\}\s*/i", $css, $matches)) {
2585 $variables = array();
2586 foreach ($matches[0] as $key => $server) {
2587 $css = str_replace($server, '', $css);
2588 preg_match_all("/([^:\}\s]+)\s*:\s*([^;\}]+);/", $matches[1][$key], $vars);
2589 foreach ($vars[1] as $var => $value) {
2590 $variables[$value] = $vars[2][$var];
2591 }
2592 }
2593 $css = str_replace(array_keys($variables), array_values($variables), $css);
2594 }
2595 echo $css;
2596}
2597
2598/**
2599 * This CSS output function will link to CSS files rather than including them
2600 * inline.
2601 *
2602 * The single CSS files can then be edited and saved with interactive
2603 * CSS editors like CSSEdit. Any files that have a .php extension are still included
2604 * inline.
2605 *
2606 * @param array $files an arry of the CSS fiels that need to be output.
fdeb7fa1 2607 * @param array $toreplace for convenience. If you are going to output the names
2608 * of the css files, for debugging purposes, then you should output
2609 * str_replace($toreplace, '', $file); becuase it looks prettier.
b7009474 2610 */
fdeb7fa1 2611function output_css_for_css_edit($files, $toreplace) {
b7009474 2612 foreach ($files as $file) {
2613 $shortname = str_replace($toreplace, '', $file);
2614 echo '/* @group ' . $shortname . " */\n\n";
2615 if (strpos($file, '.css') !== false) {
2616 echo '@import url("' . $file . '");'."\n\n";
2617 } else {
2618 @include_once($file);
2619 }
2620 echo "/* @end */\n\n";
2621 }
f77fcb5a 2622}