Removed redundant require_capability and require_login, causing havoc on new module...
[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 *
38 * A renderer factory must also have a constructor that takes a theme object and
39 * a moodle_page object. (See {@link renderer_factory_base::__construct} for an example.)
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 *
60 * @param $module the name of part of moodle. E.g. 'core', 'quiz', 'qtype_multichoice'.
61 * @return object an object implementing the requested renderer interface.
62 */
db8d89d8 63 public function get_renderer($module, $page);
571fa828 64}
65
66
67/**
68 * This is a base class to help you implement the renderer_factory interface.
69 *
70 * It keeps a cache of renderers that have been constructed, so you only need
71 * to construct each one once in you subclass.
72 *
73 * It also has a method to get the name of, and include the renderer.php with
74 * the definition of, the standard renderer class for a given module.
75 *
76 * @copyright 2009 Tim Hunt
77 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
78 * @since Moodle 2.0
79 */
80abstract class renderer_factory_base implements renderer_factory {
db8d89d8 81 /** @var theme_config the theme we belong to. */
571fa828 82 protected $theme;
83
571fa828 84 /**
85 * Constructor.
db8d89d8 86 * @param theme_config $theme the theme we belong to.
571fa828 87 */
db8d89d8 88 public function __construct($theme) {
571fa828 89 $this->theme = $theme;
571fa828 90 }
571fa828 91 /**
92 * For a given module name, return the name of the standard renderer class
93 * that defines the renderer interface for that module.
94 *
95 * Also, if it exists, include the renderer.php file for that module, so
96 * the class definition of the default renderer has been loaded.
97 *
98 * @param string $module the name of part of moodle. E.g. 'core', 'quiz', 'qtype_multichoice'.
99 * @return string the name of the standard renderer class for that module.
100 */
101 protected function standard_renderer_class_for_module($module) {
102 $pluginrenderer = get_plugin_dir($module) . '/renderer.php';
103 if (file_exists($pluginrenderer)) {
104 include_once($pluginrenderer);
105 }
106 $class = 'moodle_' . $module . '_renderer';
107 if (!class_exists($class)) {
108 throw new coding_exception('Request for an unknown renderer class ' . $class);
109 }
110 return $class;
111 }
112}
113
114
115/**
116 * This is the default renderer factory for Moodle. It simply returns an instance
117 * of the appropriate standard renderer class.
118 *
119 * @copyright 2009 Tim Hunt
120 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
121 * @since Moodle 2.0
122 */
123class standard_renderer_factory extends renderer_factory_base {
571fa828 124 /* Implement the subclass method. */
db8d89d8 125 public function get_renderer($module, $page) {
571fa828 126 if ($module == 'core') {
db8d89d8 127 return new moodle_core_renderer($page);
571fa828 128 } else {
129 $class = $this->standard_renderer_class_for_module($module);
db8d89d8 130 return new $class($page, $this->get_renderer('core', $page));
571fa828 131 }
132 }
133}
134
135
c84a2dbe 136/**
137 * This is a slight variation on the standard_renderer_factory used by CLI scripts.
138 *
139 * @copyright 2009 Tim Hunt
140 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
141 * @since Moodle 2.0
142 */
143class cli_renderer_factory extends standard_renderer_factory {
db8d89d8 144 /* Implement the subclass method. */
145 public function get_renderer($module, $page) {
146 if ($module == 'core') {
147 return new cli_core_renderer($page);
148 } else {
149 parent::get_renderer($module, $page);
150 }
571fa828 151 }
152}
153
154
155/**
156 * This is renderer factory allows themes to override the standard renderers using
157 * php code.
a5cb8d69 158 *
571fa828 159 * It will load any code from theme/mytheme/renderers.php and
160 * theme/parenttheme/renderers.php, if then exist. Then whenever you ask for
161 * a renderer for 'component', it will create a mytheme_component_renderer or a
162 * parenttheme_component_renderer, instead of a moodle_component_renderer,
163 * if either of those classes exist.
164 *
165 * This generates the slightly different HTML that the custom_corners theme expects.
166 *
167 * @copyright 2009 Tim Hunt
168 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
169 * @since Moodle 2.0
170 */
171class theme_overridden_renderer_factory extends standard_renderer_factory {
172 protected $prefixes = array();
173
174 /**
175 * Constructor.
176 * @param object $theme the theme we are rendering for.
177 * @param moodle_page $page the page we are doing output for.
178 */
db8d89d8 179 public function __construct($theme) {
571fa828 180 global $CFG;
db8d89d8 181 parent::__construct($theme);
571fa828 182
183 // Initialise $this->prefixes.
184 $renderersfile = $theme->dir . '/renderers.php';
185 if (is_readable($renderersfile)) {
186 include_once($renderersfile);
187 $this->prefixes[] = $theme->name . '_';
188 }
189 if (!empty($theme->parent)) {
190 $renderersfile = $CFG->themedir .'/'. $theme->parent . '/renderers.php';
191 if (is_readable($renderersfile)) {
192 include_once($renderersfile);
193 $this->prefixes[] = $theme->parent . '_';
194 }
195 }
196 }
197
198 /* Implement the subclass method. */
db8d89d8 199 public function get_renderer($module, $page) {
571fa828 200 foreach ($this->prefixes as $prefix) {
201 $classname = $prefix . $module . '_renderer';
202 if (class_exists($classname)) {
203 if ($module == 'core') {
db8d89d8 204 return new $classname($page);
571fa828 205 } else {
db8d89d8 206 return new $classname($page, $this->get_renderer('core', $page));
571fa828 207 }
208 }
209 }
db8d89d8 210 return parent::get_renderer($module, $page);
571fa828 211 }
212}
213
214
215/**
216 * This is renderer factory that allows you to create templated themes.
217 *
218 * This should be considered an experimental proof of concept. In particular,
219 * the performance is probably not very good. Do not try to use in on a busy site
220 * without doing careful load testing first!
221 *
222 * This renderer factory returns instances of {@link template_renderer} class
223 * which which implement the corresponding renderer interface in terms of
224 * templates. To use this your theme must have a templates folder inside it.
225 * Then suppose the method moodle_core_renderer::greeting($name = 'world');
226 * exists. Then, a call to $OUTPUT->greeting() will cause the template
227 * /theme/yourtheme/templates/core/greeting.php to be rendered, with the variable
228 * $name available. The greeting.php template might contain
a5cb8d69 229 *
571fa828 230 * <pre>
231 * <h1>Hello <?php echo $name ?>!</h1>
232 * </pre>
233 *
234 * @copyright 2009 Tim Hunt
235 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
236 * @since Moodle 2.0
237 */
238class template_renderer_factory extends renderer_factory_base {
239 /**
240 * An array of paths of where to search for templates. Normally this theme,
241 * the parent theme then the standardtemplate theme. (If some of these do
242 * not exist, or are the same as each other, then the list will be shorter.
243 */
244 protected $searchpaths = array();
245
246 /**
247 * Constructor.
248 * @param object $theme the theme we are rendering for.
249 * @param moodle_page $page the page we are doing output for.
250 */
db8d89d8 251 public function __construct($theme) {
571fa828 252 global $CFG;
db8d89d8 253 parent::__construct($theme);
571fa828 254
255 // Initialise $this->searchpaths.
256 if ($theme->name != 'standardtemplate') {
257 $templatesdir = $theme->dir . '/templates';
258 if (is_dir($templatesdir)) {
259 $this->searchpaths[] = $templatesdir;
260 }
261 }
262 if (!empty($theme->parent)) {
263 $templatesdir = $CFG->themedir .'/'. $theme->parent . '/templates';
264 if (is_dir($templatesdir)) {
265 $this->searchpaths[] = $templatesdir;
266 }
267 }
268 $this->searchpaths[] = $CFG->themedir .'/standardtemplate/templates';
269 }
270
271 /* Implement the subclass method. */
db8d89d8 272 public function get_renderer($module, $page) {
571fa828 273 // Refine the list of search paths for this module.
274 $searchpaths = array();
275 foreach ($this->searchpaths as $rootpath) {
276 $path = $rootpath . '/' . $module;
277 if (is_dir($path)) {
278 $searchpaths[] = $path;
279 }
280 }
281
282 // Create a template_renderer that copies the API of the standard renderer.
283 $copiedclass = $this->standard_renderer_class_for_module($module);
db8d89d8 284 return new template_renderer($copiedclass, $searchpaths, $page);
571fa828 285 }
286}
287
288
289/**
290 * Simple base class for Moodle renderers.
291 *
292 * Tracks the xhtml_container_stack to use, which is passed in in the constructor.
293 *
8954245a 294 * Also has methods to facilitate generating HTML output.
295 *
571fa828 296 * @copyright 2009 Tim Hunt
297 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
298 * @since Moodle 2.0
299 */
300class moodle_renderer_base {
301 /** @var xhtml_container_stack the xhtml_container_stack to use. */
34a2777c 302 protected $opencontainers;
303 /** @var moodle_page the page we are rendering for. */
304 protected $page;
571fa828 305
306 /**
307 * Constructor
34a2777c 308 * @param $opencontainers the xhtml_container_stack to use.
309 * @param moodle_page $page the page we are doing output for.
571fa828 310 */
db8d89d8 311 public function __construct($page) {
312 $this->opencontainers = $page->opencontainers;
34a2777c 313 $this->page = $page;
571fa828 314 }
8954245a 315
b7009474 316 /**
317 * Have we started output yet?
318 * @return boolean true if the header has been printed.
319 */
320 public function has_started() {
321 return $this->page->state >= moodle_page::STATE_IN_BODY;
322 }
323
8954245a 324 protected function output_tag($tagname, $attributes, $contents) {
325 return $this->output_start_tag($tagname, $attributes) . $contents .
326 $this->output_end_tag($tagname);
327 }
328 protected function output_start_tag($tagname, $attributes) {
329 return '<' . $tagname . $this->output_attributes($attributes) . '>';
330 }
331 protected function output_end_tag($tagname) {
332 return '</' . $tagname . '>';
333 }
334 protected function output_empty_tag($tagname, $attributes) {
335 return '<' . $tagname . $this->output_attributes($attributes) . ' />';
336 }
337
338 protected function output_attribute($name, $value) {
34a2777c 339 $value = trim($value);
8954245a 340 if ($value || is_numeric($value)) { // We want 0 to be output.
341 return ' ' . $name . '="' . $value . '"';
342 }
343 }
344 protected function output_attributes($attributes) {
a5cb8d69 345 if (empty($attributes)) {
346 $attributes = array();
347 }
8954245a 348 $output = '';
349 foreach ($attributes as $name => $value) {
350 $output .= $this->output_attribute($name, $value);
351 }
352 return $output;
353 }
34a2777c 354 public static function prepare_classes($classes) {
355 if (is_array($classes)) {
356 return implode(' ', array_unique($classes));
357 }
358 return $classes;
8954245a 359 }
571fa828 360}
361
362
363/**
364 * This is the templated renderer which copies the API of another class, replacing
365 * all methods calls with instantiation of a template.
366 *
367 * When the method method_name is called, this class will search for a template
368 * called method_name.php in the folders in $searchpaths, taking the first one
369 * that it finds. Then it will set up variables for each of the arguments of that
370 * method, and render the template. This is implemented in the {@link __call()}
371 * PHP magic method.
372 *
373 * Methods like print_box_start and print_box_end are handles specially, and
374 * implemented in terms of the print_box.php method.
375 *
376 * @copyright 2009 Tim Hunt
377 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
378 * @since Moodle 2.0
379 */
380class template_renderer extends moodle_renderer_base {
381 /** @var ReflectionClass information about the class whose API we are copying. */
382 protected $copiedclass;
383 /** @var array of places to search for templates. */
384 protected $searchpaths;
c84a2dbe 385 protected $rendererfactory;
571fa828 386
387 /**
388 * Magic word used when breaking apart container templates to implement
389 * _start and _end methods.
390 */
391 const contentstoken = '-@#-Contents-go-here-#@-';
392
393 /**
394 * Constructor
395 * @param string $copiedclass the name of a class whose API we should be copying.
396 * @param $searchpaths a list of folders to search for templates in.
34a2777c 397 * @param $opencontainers the xhtml_container_stack to use.
398 * @param moodle_page $page the page we are doing output for.
571fa828 399 */
db8d89d8 400 public function __construct($copiedclass, $searchpaths, $page) {
401 parent::__construct($page);
571fa828 402 $this->copiedclass = new ReflectionClass($copiedclass);
403 $this->searchpaths = $searchpaths;
404 }
405
406 /* PHP magic method implementation. */
407 public function __call($method, $arguments) {
408 if (substr($method, -6) == '_start') {
409 return $this->process_start(substr($method, 0, -6), $arguments);
410 } else if (substr($method, -4) == '_end') {
411 return $this->process_end(substr($method, 0, -4), $arguments);
412 } else {
413 return $this->process_template($method, $arguments);
414 }
415 }
416
417 /**
418 * Render the template for a given method of the renderer class we are copying,
419 * using the arguments passed.
420 * @param string $method the method that was called.
421 * @param array $arguments the arguments that were passed to it.
422 * @return string the HTML to be output.
423 */
424 protected function process_template($method, $arguments) {
425 if (!$this->copiedclass->hasMethod($method) ||
426 !$this->copiedclass->getMethod($method)->isPublic()) {
427 throw new coding_exception('Unknown method ' . $method);
428 }
429
430 // Find the template file for this method.
431 $template = $this->find_template($method);
432
433 // Use the reflection API to find out what variable names the arguments
434 // should be stored in, and fill in any missing ones with the defaults.
435 $namedarguments = array();
436 $expectedparams = $this->copiedclass->getMethod($method)->getParameters();
437 foreach ($expectedparams as $param) {
438 $paramname = $param->getName();
439 if (!empty($arguments)) {
440 $namedarguments[$paramname] = array_shift($arguments);
441 } else if ($param->isDefaultValueAvailable()) {
442 $namedarguments[$paramname] = $param->getDefaultValue();
443 } else {
444 throw new coding_exception('Missing required argument ' . $paramname);
445 }
446 }
447
448 // Actually render the template.
449 return $this->render_template($template, $namedarguments);
450 }
451
452 /**
453 * Actually do the work of rendering the template.
454 * @param $_template the full path to the template file.
455 * @param $_namedarguments an array variable name => value, the variables
456 * that should be available to the template.
457 * @return string the HTML to be output.
458 */
459 protected function render_template($_template, $_namedarguments) {
460 // Note, we intentionally break the coding guidelines with regards to
461 // local variable names used in this function, so that they do not clash
462 // with the names of any variables being passed to the template.
463
6e37fa50 464 global $CFG, $SITE, $THEME, $USER;
465 // The next lines are a bit tricky. The point is, here we are in a method
466 // of a renderer class, and this object may, or may not, be the the same as
467 // the global $OUTPUT object. When rendering the template, we want to use
468 // this object. However, people writing Moodle code expect the current
469 // rederer to be called $OUTPUT, not $this, so define a variable called
470 // $OUTPUT pointing at $this. The same comment applies to $PAGE and $COURSE.
34a2777c 471 $OUTPUT = $this;
6e37fa50 472 $PAGE = $this->page;
34a2777c 473 $COURSE = $this->page->course;
571fa828 474
475 // And the parameters from the function call.
476 extract($_namedarguments);
477
478 // Include the template, capturing the output.
479 ob_start();
480 include($_template);
481 $_result = ob_get_contents();
482 ob_end_clean();
483
484 return $_result;
485 }
486
487 /**
488 * Searches the folders in {@link $searchpaths} to try to find a template for
489 * this method name. Throws an exception if one cannot be found.
490 * @param string $method the method name.
491 * @return string the full path of the template to use.
492 */
493 protected function find_template($method) {
494 foreach ($this->searchpaths as $path) {
495 $filename = $path . '/' . $method . '.php';
496 if (file_exists($filename)) {
497 return $filename;
498 }
499 }
500 throw new coding_exception('Cannot find template for ' . $this->copiedclass->getName() . '::' . $method);
501 }
502
503 /**
504 * Handle methods like print_box_start by using the print_box template,
505 * splitting the result, pusing the end onto the stack, then returning the start.
506 * @param string $method the method that was called, with _start stripped off.
507 * @param array $arguments the arguments that were passed to it.
508 * @return string the HTML to be output.
509 */
510 protected function process_start($template, $arguments) {
511 array_unshift($arguments, self::contentstoken);
512 $html = $this->process_template($template, $arguments);
513 list($start, $end) = explode(self::contentstoken, $html, 2);
34a2777c 514 $this->opencontainers->push($template, $end);
571fa828 515 return $start;
516 }
517
518 /**
519 * Handle methods like print_box_end, we just need to pop the end HTML from
520 * the stack.
521 * @param string $method the method that was called, with _end stripped off.
522 * @param array $arguments not used. Assumed to be irrelevant.
523 * @return string the HTML to be output.
524 */
525 protected function process_end($template, $arguments) {
34a2777c 526 return $this->opencontainers->pop($template);
571fa828 527 }
528
529 /**
530 * @return array the list of paths where this class searches for templates.
531 */
532 public function get_search_paths() {
533 return $this->searchpaths;
534 }
535
536 /**
537 * @return string the name of the class whose API we are copying.
538 */
539 public function get_copied_class() {
540 return $this->copiedclass->getName();
541 }
542}
543
544
545/**
546 * This class keeps track of which HTML tags are currently open.
547 *
548 * This makes it much easier to always generate well formed XHTML output, even
549 * if execution terminates abruptly. Any time you output some opening HTML
550 * without the matching closing HTML, you should push the neccessary close tags
551 * onto the stack.
552 *
553 * @copyright 2009 Tim Hunt
554 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
555 * @since Moodle 2.0
556 */
557class xhtml_container_stack {
558 /** @var array stores the list of open containers. */
34a2777c 559 protected $opencontainers = array();
571fa828 560
561 /**
562 * Push the close HTML for a recently opened container onto the stack.
563 * @param string $type The type of container. This is checked when {@link pop()}
564 * is called and must match, otherwise a developer debug warning is output.
565 * @param string $closehtml The HTML required to close the container.
566 */
567 public function push($type, $closehtml) {
568 $container = new stdClass;
569 $container->type = $type;
570 $container->closehtml = $closehtml;
34a2777c 571 array_push($this->opencontainers, $container);
571fa828 572 }
573
574 /**
575 * Pop the HTML for the next closing container from the stack. The $type
576 * must match the type passed when the container was opened, otherwise a
577 * warning will be output.
578 * @param string $type The type of container.
579 * @return string the HTML requried to close the container.
580 */
581 public function pop($type) {
34a2777c 582 if (empty($this->opencontainers)) {
571fa828 583 debugging('There are no more open containers. This suggests there is a nesting problem.', DEBUG_DEVELOPER);
584 return;
585 }
586
34a2777c 587 $container = array_pop($this->opencontainers);
571fa828 588 if ($container->type != $type) {
589 debugging('The type of container to be closed (' . $container->type .
590 ') does not match the type of the next open container (' . $type .
591 '). This suggests there is a nesting problem.', DEBUG_DEVELOPER);
592 }
593 return $container->closehtml;
594 }
595
34a2777c 596 /**
597 * Return how many containers are currently open.
598 * @return integer how many containers are currently open.
599 */
600 public function count() {
601 return count($this->opencontainers);
602 }
603
571fa828 604 /**
605 * Close all but the last open container. This is useful in places like error
606 * handling, where you want to close all the open containers (apart from <body>)
607 * before outputting the error message.
608 * @return string the HTML requried to close any open containers inside <body>.
609 */
610 public function pop_all_but_last() {
611 $output = '';
34a2777c 612 while (count($this->opencontainers) > 1) {
613 $container = array_pop($this->opencontainers);
571fa828 614 $output .= $container->closehtml;
615 }
616 return $output;
617 }
618
619 /**
620 * You can call this function if you want to throw away an instance of this
621 * class without properly emptying the stack (for example, in a unit test).
622 * Calling this method stops the destruct method from outputting a developer
623 * debug warning. After calling this method, the instance can no longer be used.
624 */
625 public function discard() {
34a2777c 626 $this->opencontainers = null;
571fa828 627 }
628
629 /**
630 * Emergency fallback. If we get to the end of processing and not all
631 * containers have been closed, output the rest with a developer debug warning.
632 */
633 public function __destruct() {
34a2777c 634 if (empty($this->opencontainers)) {
571fa828 635 return;
636 }
637
638 debugging('Some containers were left open. This suggests there is a nesting problem.', DEBUG_DEVELOPER);
639 echo $this->pop_all_but_last();
34a2777c 640 $container = array_pop($this->opencontainers);
571fa828 641 echo $container->closehtml;
642 }
643}
644
645
646/**
647 * The standard implementation of the moodle_core_renderer interface.
648 *
649 * @copyright 2009 Tim Hunt
650 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
651 * @since Moodle 2.0
652 */
653class moodle_core_renderer extends moodle_renderer_base {
34a2777c 654 const PERFORMANCE_INFO_TOKEN = '%%PERFORMANCEINFO%%';
655 const END_HTML_TOKEN = '%%ENDHTML%%';
656 const MAIN_CONTENT_TOKEN = '[MAIN CONTENT GOES HERE]';
657 protected $contenttype;
e8775320 658 protected $metarefreshtag = '';
34a2777c 659
660 public function doctype() {
661 global $CFG;
662
663 $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n";
664 $this->contenttype = 'text/html; charset=utf-8';
665
666 if (empty($CFG->xmlstrictheaders)) {
667 return $doctype;
668 }
669
670 // We want to serve the page with an XML content type, to force well-formedness errors to be reported.
671 $prolog = '<?xml version="1.0" encoding="utf-8"?>' . "\n";
672 if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'application/xhtml+xml') !== false) {
673 // Firefox and other browsers that can cope natively with XHTML.
674 $this->contenttype = 'application/xhtml+xml; charset=utf-8';
675
676 } else if (preg_match('/MSIE.*Windows NT/', $_SERVER['HTTP_USER_AGENT'])) {
677 // IE can't cope with application/xhtml+xml, but it will cope if we send application/xml with an XSL stylesheet.
678 $this->contenttype = 'application/xml; charset=utf-8';
679 $prolog .= '<?xml-stylesheet type="text/xsl" href="' . $CFG->httpswwwroot . '/lib/xhtml.xsl"?>' . "\n";
680
681 } else {
682 $prolog = '';
683 }
684
685 return $prolog . $doctype;
686 }
687
688 public function htmlattributes() {
689 return get_html_lang(true) . ' xmlns="http://www.w3.org/1999/xhtml"';
690 }
691
692 public function standard_head_html() {
693 global $CFG, $THEME;
694 $output = '';
695 $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . "\n";
696 $output .= '<meta name="keywords" content="moodle, ' . $this->page->title . '" />' . "\n";
697 if (!$this->page->cacheable) {
698 $output .= '<meta http-equiv="pragma" content="no-cache" />' . "\n";
699 $output .= '<meta http-equiv="expires" content="0" />' . "\n";
700 }
e8775320 701 // This is only set by the {@link redirect()} method
702 $output .= $this->metarefreshtag;
17a6649b 703
704 // Check if a periodic refresh delay has been set and make sure we arn't
705 // already meta refreshing
706 if ($this->metarefreshtag=='' && $this->page->periodicrefreshdelay!==null) {
707 $metarefesh = '<meta http-equiv="refresh" content="%d;url=%s" />';
708 $output .= sprintf($metarefesh, $this->page->periodicrefreshdelay, $this->page->url->out());
709 }
710
b7009474 711 // TODO get rid of $CFG->javascript. We should be able to do everything
712 // with $PAGE->requires.
34a2777c 713 ob_start();
714 include($CFG->javascript);
715 $output .= ob_get_contents();
716 ob_end_clean();
717 $output .= $this->page->requires->get_head_code();
a5cb8d69 718
b7009474 719 // List alternate versions.
34a2777c 720 foreach ($this->page->alternateversions as $type => $alt) {
721 $output .= $this->output_empty_tag('link', array('rel' => 'alternate',
722 'type' => $type, 'title' => $alt->title, 'href' => $alt->url));
723 }
724
725 // Add the meta page from the themes if any were requested
b7009474 726 // TODO See if we can get rid of this.
34a2777c 727 $PAGE = $this->page;
728 $metapage = '';
729 if (!isset($THEME->standardmetainclude) || $THEME->standardmetainclude) {
730 ob_start();
731 include_once($CFG->dirroot.'/theme/standard/meta.php');
732 $output .= ob_get_contents();
733 ob_end_clean();
734 }
735 if ($THEME->parent && (!isset($THEME->parentmetainclude) || $THEME->parentmetainclude)) {
736 if (file_exists($CFG->dirroot.'/theme/'.$THEME->parent.'/meta.php')) {
737 ob_start();
738 include_once($CFG->dirroot.'/theme/'.$THEME->parent.'/meta.php');
739 $output .= ob_get_contents();
740 ob_end_clean();
741 }
742 }
743 if (!isset($THEME->metainclude) || $THEME->metainclude) {
744 if (file_exists($CFG->dirroot.'/theme/'.current_theme().'/meta.php')) {
745 ob_start();
746 include_once($CFG->dirroot.'/theme/'.current_theme().'/meta.php');
747 $output .= ob_get_contents();
748 ob_end_clean();
749 }
750 }
751
752 return $output;
753 }
754
755 public function standard_top_of_body_html() {
756 return $this->page->requires->get_top_of_body_code();
757 }
758
759 public function standard_footer_html() {
760 $output = self::PERFORMANCE_INFO_TOKEN;
761 if (debugging()) {
762 $output .= '<div class="validators"><ul>
763 <li><a href="http://validator.w3.org/check?verbose=1&amp;ss=1&amp;uri=' . urlencode(qualified_me()) . '">Validate HTML</a></li>
764 <li><a href="http://www.contentquality.com/mynewtester/cynthia.exe?rptmode=-1&amp;url1=' . urlencode(qualified_me()) . '">Section 508 Check</a></li>
765 <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>
766 </ul></div>';
767 }
768 return $output;
769 }
770
771 public function standard_end_of_body_html() {
772 echo self::END_HTML_TOKEN;
773 }
774
775 public function login_info() {
776 global $USER;
777 return user_login_string($this->page->course, $USER);
778 }
779
780 public function home_link() {
781 global $CFG, $SITE;
782
783 if ($this->page->pagetype == 'site-index') {
784 // Special case for site home page - please do not remove
785 return '<div class="sitelink">' .
786 '<a title="Moodle ' . $CFG->release . '" href="http://moodle.org/">' .
787 '<img style="width:100px;height:30px" src="' . $CFG->httpswwwroot . '/pix/moodlelogo.gif" alt="moodlelogo" /></a></div>';
788
789 } else if (!empty($CFG->target_release) && $CFG->target_release != $CFG->release) {
790 // Special case for during install/upgrade.
791 return '<div class="sitelink">'.
792 '<a title="Moodle ' . $CFG->target_release . '" href="http://docs.moodle.org/en/Administrator_documentation" onclick="this.target=\'_blank\'">' .
793 '<img style="width:100px;height:30px" src="' . $CFG->httpswwwroot . '/pix/moodlelogo.gif" alt="moodlelogo" /></a></div>';
794
795 } else if ($this->page->course->id == $SITE->id || strpos($this->page->pagetype, 'course-view') === 0) {
796 return '<div class="homelink"><a href="' . $CFG->wwwroot . '/">' .
797 get_string('home') . '</a></div>';
798
799 } else {
800 return '<div class="homelink"><a href="' . $CFG->wwwroot . '/course/view.php?id=' . $this->page->course->id . '">' .
801 format_string($this->page->course->shortname) . '</a></div>';
802 }
803 }
804
afa2dcad 805 /**
806 * Redirects the user by any means possible given the current state
807 *
808 * This function should not be called directly, it should always be called using
809 * the redirect function in lib/weblib.php
810 *
811 * The redirect function should really only be called before page output has started
812 * however it will allow itself to be called during the state STATE_IN_BODY
813 *
afa2dcad 814 * @param string $encodedurl The URL to send to encoded if required
815 * @param string $message The message to display to the user if any
816 * @param int $delay The delay before redirecting a user, if $message has been
817 * set this is a requirement and defaults to 3, set to 0 no delay
818 * @param string $messageclass The css class to put on the message that is
819 * being displayed to the user
820 * @return string The HTML to display to the user before dying, may contain
821 * meta refresh, javascript refresh, and may have set header redirects
822 */
e8775320 823 public function redirect($encodedurl, $message, $delay, $messageclass='notifyproblem') {
824 global $CFG;
825 $url = str_replace('&amp;', '&', $encodedurl);
826
827 $disableredirect = false;
828
829 if ($delay!=0) {
830 /// At developer debug level. Don't redirect if errors have been printed on screen.
831 /// Currenly only works in PHP 5.2+; we do not want strict PHP5 errors
832 $lasterror = error_get_last();
833 $error = defined('DEBUGGING_PRINTED') or (!empty($lasterror) && ($lasterror['type'] & DEBUG_DEVELOPER));
834 $errorprinted = debugging('', DEBUG_ALL) && $CFG->debugdisplay && $error;
835 if ($errorprinted) {
836 $disableredirect= true;
837 $message = "<strong>Error output, so disabling automatic redirect.</strong></p><p>" . $message;
838 }
839 }
840
841 switch ($this->page->state) {
842 case moodle_page::STATE_BEFORE_HEADER :
843 // No output yet it is safe to delivery the full arsenol of redirect methods
844 if (!$disableredirect) {
845 @header($_SERVER['SERVER_PROTOCOL'] . ' 303 See Other'); //302 might not work for POST requests, 303 is ignored by obsolete clients
846 @header('Location: '.$url);
847 $this->metarefreshtag = '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />'."\n";
848 $this->page->requires->js_function_call('document.location.replace', array($url))->after_delay($delay+3);
849 }
850 $output = $this->header();
851 $output .= $this->notification($message, $messageclass);
852 $output .= $this->footer();
853 break;
854 case moodle_page::STATE_PRINTING_HEADER :
855 // We should hopefully never get here
856 throw new coding_exception('You cannot redirect while printing the page header');
857 break;
858 case moodle_page::STATE_IN_BODY :
859 // We really shouldn't be here but we can deal with this
860 debugging("You should really redirect before you start page output");
861 if (!$disableredirect) {
862 $this->page->requires->js_function_call('document.location.replace', array($url))->after_delay($delay+3);
863 }
864 $output = $this->opencontainers->pop_all_but_last();
865 $output .= $this->notification($message, $messageclass);
866 $output .= $this->footer();
867 break;
868 case moodle_page::STATE_DONE :
869 // Too late to be calling redirect now
870 throw new coding_exception('You cannot redirect after the entire page has been generated');
871 break;
872 }
873 return $output;
874 }
875
34a2777c 876 // TODO remove $navigation and $menu arguments - replace with $PAGE->navigation
877 public function header($navigation = '', $menu='') {
878 global $USER, $CFG;
879
880 output_starting_hook();
881 $this->page->set_state(moodle_page::STATE_PRINTING_HEADER);
882
34a2777c 883 // Find the appropriate page template, based on $this->page->generaltype.
884 $templatefile = $this->find_page_template();
885 if ($templatefile) {
886 // Render the template.
887 $template = $this->render_page_template($templatefile, $menu, $navigation);
888 } else {
889 // New style template not found, fall back to using header.html and footer.html.
890 $template = $this->handle_legacy_theme($navigation, $menu);
891 }
892
893 // Slice the template output into header and footer.
894 $cutpos = strpos($template, self::MAIN_CONTENT_TOKEN);
895 if ($cutpos === false) {
896 throw new coding_exception('Layout template ' . $templatefile .
897 ' does not contain the string "' . self::MAIN_CONTENT_TOKEN . '".');
898 }
899 $header = substr($template, 0, $cutpos);
900 $footer = substr($template, $cutpos + strlen(self::MAIN_CONTENT_TOKEN));
901
902 send_headers($this->contenttype, $this->page->cacheable);
903 $this->opencontainers->push('header/footer', $footer);
904 $this->page->set_state(moodle_page::STATE_IN_BODY);
905 return $header . $this->skip_link_target();
906 }
907
908 protected function find_page_template() {
909 global $THEME;
910
911 // If this is a particular page type, look for a specific template.
912 $type = $this->page->generaltype;
913 if ($type != 'normal') {
914 $templatefile = $THEME->dir . '/layout-' . $type . '.php';
915 if (is_readable($templatefile)) {
916 return $templatefile;
917 }
918 }
919
920 // Otherwise look for the general template.
921 $templatefile = $THEME->dir . '/layout.php';
922 if (is_readable($templatefile)) {
923 return $templatefile;
924 }
925
926 return false;
927 }
928
929 protected function render_page_template($templatefile, $menu, $navigation) {
930 global $CFG, $SITE, $THEME, $USER;
6e37fa50 931 // The next lines are a bit tricky. The point is, here we are in a method
932 // of a renderer class, and this object may, or may not, be the the same as
933 // the global $OUTPUT object. When rendering the template, we want to use
934 // this object. However, people writing Moodle code expect the current
935 // rederer to be called $OUTPUT, not $this, so define a variable called
936 // $OUTPUT pointing at $this. The same comment applies to $PAGE and $COURSE.
34a2777c 937 $OUTPUT = $this;
938 $PAGE = $this->page;
939 $COURSE = $this->page->course;
940
941 ob_start();
942 include($templatefile);
943 $template = ob_get_contents();
944 ob_end_clean();
945 return $template;
946 }
947
948 protected function handle_legacy_theme($navigation, $menu) {
949 global $CFG, $SITE, $THEME, $USER;
950 // Set a pretend global from the properties of this class.
6e37fa50 951 // See the comment in render_page_template for a fuller explanation.
34a2777c 952 $COURSE = $this->page->course;
953
954 // Set up local variables that header.html expects.
955 $direction = $this->htmlattributes();
956 $title = $this->page->title;
957 $heading = $this->page->heading;
958 $focus = $this->page->focuscontrol;
959 $button = $this->page->button;
960 $pageid = $this->page->pagetype;
961 $pageclass = $this->page->bodyclasses;
962 $bodytags = ' class="' . $pageclass . '" id="' . $pageid . '"';
963 $home = $this->page->generaltype == 'home';
964
965 $meta = $this->standard_head_html();
966 // The next line is a nasty hack. having set $meta to standard_head_html, we have already
967 // got the contents of include($CFG->javascript). However, legacy themes are going to
968 // include($CFG->javascript) again. We want to make sure that when they do, nothing is output.
969 $CFG->javascript = $CFG->libdir . '/emptyfile.php';
970
971 // Set up local variables that footer.html expects.
972 $homelink = $this->home_link();
973 $loggedinas = $this->login_info();
974 $course = $this->page->course;
975 $performanceinfo = self::PERFORMANCE_INFO_TOKEN;
976
977 if (!$menu && $navigation) {
978 $menu = $loggedinas;
979 }
980
981 ob_start();
982 include($THEME->dir . '/header.html');
983 $this->page->requires->get_top_of_body_code();
984 echo self::MAIN_CONTENT_TOKEN;
985
986 $menu = str_replace('navmenu', 'navmenufooter', $menu);
987 include($THEME->dir . '/footer.html');
988
989 $output = ob_get_contents();
990 ob_end_clean();
991
992 $output = str_replace('</body>', self::END_HTML_TOKEN . '</body>', $output);
993
994 return $output;
995 }
996
997 public function footer() {
998 $output = '';
999 if ($this->opencontainers->count() != 1) {
1000 debugging('Some HTML tags were opened in the body of the page but not closed.', DEBUG_DEVELOPER);
1001 $output .= $this->opencontainers->pop_all_but_last();
1002 }
1003
1004 $footer = $this->opencontainers->pop('header/footer');
1005
1006 // Provide some performance info if required
1007 $performanceinfo = '';
1008 if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) {
1009 $perf = get_performance_info();
1010 if (defined('MDL_PERFTOLOG') && !function_exists('register_shutdown_function')) {
1011 error_log("PERF: " . $perf['txt']);
1012 }
1013 if (defined('MDL_PERFTOFOOT') || debugging() || $CFG->perfdebug > 7) {
1014 $performanceinfo = $perf['html'];
1015 }
1016 }
1017 $footer = str_replace(self::PERFORMANCE_INFO_TOKEN, $performanceinfo, $footer);
1018
1019 $footer = str_replace(self::END_HTML_TOKEN, $this->page->requires->get_end_code(), $footer);
1020
1021 $this->page->set_state(moodle_page::STATE_DONE);
1022
1023 return $output . $footer;
1024 }
1025
a5cb8d69 1026 /**
1027 * Prints a nice side block with an optional header.
1028 *
1029 * The content is described
1030 * by a {@link block_contents} object.
1031 *
1032 * @param block $content HTML for the content
1033 * @return string the HTML to be output.
1034 */
1035 function block($bc) {
1036 $bc = clone($bc);
1037 $bc->prepare();
1038
1039 $title = strip_tags($bc->title);
1040 if (empty($title)) {
1041 $output = '';
1042 $skipdest = '';
1043 } else {
1044 $output = $this->output_tag('a', array('href' => '#sb-' . $bc->skipid, 'class' => 'skip-block'),
1045 get_string('skipa', 'access', $title));
1046 $skipdest = $this->output_tag('span', array('id' => 'sb-' . $bc->skipid, 'class' => 'skip-block-to'), '');
1047 }
1048
1049 $bc->attributes['id'] = $bc->id;
1050 $bc->attributes['class'] = $bc->get_classes_string();
1051 $output .= $this->output_start_tag('div', $bc->attributes);
1052
1053 if ($bc->heading) {
1054 // Some callers pass in complete html for the heading, which may include
1055 // complicated things such as the 'hide block' button; some just pass in
1056 // text. If they only pass in plain text i.e. it doesn't include a
1057 // <div>, then we add in standard tags that make it look like a normal
1058 // page block including the h2 for accessibility
1059 if (strpos($bc->heading, '</div>') === false) {
1060 $bc->heading = $this->output_tag('div', array('class' => 'title'),
1061 $this->output_tag('h2', null, $bc->heading));
1062 }
1063
1064 $output .= $this->output_tag('div', array('class' => 'header'), $bc->heading);
1065 }
1066
1067 $output .= $this->output_start_tag('div', array('class' => 'content'));
1068
1069 if ($bc->content) {
1070 $output .= $bc->content;
1071
1072 } else if ($bc->list) {
1073 $row = 0;
a5cb8d69 1074 $items = array();
1075 foreach ($bc->list as $key => $string) {
1076 $item = $this->output_start_tag('li', array('class' => 'r' . $row));
1077 if ($bc->icons) {
1078 $item .= $this->output_tag('div', array('class' => 'icon column c0'), $bc->icons[$key]);
1079 }
1080 $item .= $this->output_tag('div', array('class' => 'column c1'), $string);
1081 $item .= $this->output_end_tag('li');
1082 $items[] = $item;
1083 $row = 1 - $row; // Flip even/odd.
1084 }
1085 $output .= $this->output_tag('ul', array('class' => 'list'), implode("\n", $items));
1086 }
1087
1088 if ($bc->footer) {
1089 $output .= $this->output_tag('div', array('class' => 'footer'), $bc->footer);
1090 }
1091
1092 $output .= $this->output_end_tag('div');
1093 $output .= $this->output_end_tag('div');
1094 $output .= $skipdest;
1095
1096 if (!empty($CFG->allowuserblockhiding) && isset($attributes['id'])) {
1097 $strshow = addslashes_js(get_string('showblocka', 'access', $title));
1098 $strhide = addslashes_js(get_string('hideblocka', 'access', $title));
1099 $output .= $this->page->requires->js_function_call('elementCookieHide', array(
1100 $bc->id, $strshow, $strhide))->asap();
1101 }
1102
1103 return $output;
1104 }
1105
8954245a 1106 public function link_to_popup_window() {
a5cb8d69 1107
8954245a 1108 }
571fa828 1109
8954245a 1110 public function button_to_popup_window() {
a5cb8d69 1111
8954245a 1112 }
1113
1114 public function close_window_button($buttontext = null, $reloadopener = false) {
1115 if (empty($buttontext)) {
1116 $buttontext = get_string('closewindow');
1117 }
1118 // TODO
1119 }
1120
1121 public function close_window($delay = 0, $reloadopener = false) {
1122 // TODO
1123 }
1124
1125 /**
1126 * Output a <select> menu.
1127 *
1128 * You can either call this function with a single moodle_select_menu argument
1129 * or, with a list of parameters, in which case those parameters are sent to
1130 * the moodle_select_menu constructor.
1131 *
1132 * @param moodle_select_menu $selectmenu a moodle_select_menu that describes
1133 * the select menu you want output.
1134 * @return string the HTML for the <select>
1135 */
1136 public function select_menu($selectmenu) {
1137 $selectmenu = clone($selectmenu);
1138 $selectmenu->prepare();
1139
1140 if ($selectmenu->nothinglabel) {
1141 $selectmenu->options = array($selectmenu->nothingvalue => $selectmenu->nothinglabel) +
1142 $selectmenu->options;
1143 }
1144
1145 if (empty($selectmenu->id)) {
1146 $selectmenu->id = 'menu' . str_replace(array('[', ']'), '', $selectmenu->name);
1147 }
1148
1149 $attributes = array(
1150 'name' => $selectmenu->name,
1151 'id' => $selectmenu->id,
1152 'class' => $selectmenu->get_classes_string(),
1153 'onchange' => $selectmenu->script,
1154 );
1155 if ($selectmenu->disabled) {
1156 $attributes['disabled'] = 'disabled';
1157 }
1158 if ($selectmenu->tabindex) {
1159 $attributes['tabindex'] = $tabindex;
1160 }
1161
1162 if ($selectmenu->listbox) {
1163 if (is_integer($selectmenu->listbox)) {
1164 $size = $selectmenu->listbox;
1165 } else {
1166 $size = min($selectmenu->maxautosize, count($selectmenu->options));
1167 }
1168 $attributes['size'] = $size;
1169 if ($selectmenu->multiple) {
1170 $attributes['multiple'] = 'multiple';
1171 }
1172 }
1173
1174 $html = $this->output_start_tag('select', $attributes) . "\n";
1175 foreach ($selectmenu->options as $value => $label) {
1176 $attributes = array('value' => $value);
1177 if ((string)$value == (string)$selectmenu->selectedvalue ||
1178 (is_array($selectmenu->selectedvalue) && in_array($value, $selectmenu->selectedvalue))) {
1179 $attributes['selected'] = 'selected';
1180 }
1181 $html .= ' ' . $this->output_tag('option', $attributes, s($label)) . "\n";
1182 }
1183 $html .= $this->output_end_tag('select') . "\n";
1184
1185 return $html;
1186 }
1187
1188 // TODO choose_from_menu_nested
1189
1190 // TODO choose_from_radio
1191
1192 /**
1193 * Output an error message. By default wraps the error message in <span class="error">.
1194 * If the error message is blank, nothing is output.
1195 * @param $message the error message.
1196 * @return string the HTML to output.
1197 */
1198 public function error_text($message) {
1199 if (empty($message)) {
1200 return '';
1201 }
1202 return $this->output_tag('span', array('class' => 'error'), $message);
1203 }
34a2777c 1204
1205 /**
1206 * Do not call this function directly.
1207 *
1208 * To terminate the current script with a fatal error, call the {@link print_error}
1209 * function, or throw an exception. Doing either of those things will then call this
1210 * funciton to display the error, before terminating the exection.
1211 *
1212 * @param string $message
1213 * @param string $moreinfourl
1214 * @param string $link
1215 * @param array $backtrace
1216 * @param string $debuginfo
1217 * @param bool $showerrordebugwarning
1218 * @return string the HTML to output.
1219 */
1220 public function fatal_error($message, $moreinfourl, $link, $backtrace,
1221 $debuginfo = null, $showerrordebugwarning = false) {
1222
1223 $output = '';
1224
e8775320 1225 if ($this->has_started()) {
1226 $output .= $this->opencontainers->pop_all_but_last();
1227 } else {
34a2777c 1228 // Header not yet printed
1229 @header('HTTP/1.0 404 Not Found');
c84a2dbe 1230 $this->page->set_title(get_string('error'));
1231 $output .= $this->header();
34a2777c 1232 }
1233
1234 $message = '<p class="errormessage">' . $message . '</p>'.
1235 '<p class="errorcode"><a href="' . $moreinfourl . '">' .
1236 get_string('moreinformation') . '</a></p>';
1237 $output .= $this->box($message, 'errorbox');
1238
1239 if (debugging('', DEBUG_DEVELOPER)) {
1240 if ($showerrordebugwarning) {
1241 $output .= $this->notification('error() is a deprecated function. ' .
1242 'Please call print_error() instead of error()', 'notifytiny');
1243 }
1244 if (!empty($debuginfo)) {
1245 $output .= $this->notification($debuginfo, 'notifytiny');
1246 }
1247 if (!empty($backtrace)) {
1248 $output .= $this->notification('Stack trace: ' .
b7009474 1249 format_backtrace($backtrace), 'notifytiny');
34a2777c 1250 }
1251 }
1252
1253 if (!empty($link)) {
1254 $output .= $this->continue_button($link);
1255 }
1256
c84a2dbe 1257 $output .= $this->footer();
34a2777c 1258
1259 // Padding to encourage IE to display our error page, rather than its own.
1260 $output .= str_repeat(' ', 512);
1261
1262 return $output;
1263 }
1264
1265 /**
1266 * Output a notification (that is, a status message about something that has
1267 * just happened).
1268 *
1269 * @param string $message the message to print out
1270 * @param string $classes normally 'notifyproblem' or 'notifysuccess'.
1271 * @return string the HTML to output.
1272 */
1273 public function notification($message, $classes = 'notifyproblem') {
1274 return $this->output_tag('div', array('class' =>
1275 moodle_renderer_base::prepare_classes($classes)), clean_text($message));
1276 }
1277
1278 /**
1279 * Print a continue button that goes to a particular URL.
1280 *
1281 * @param string|moodle_url $link The url the button goes to.
1282 * @return string the HTML to output.
1283 */
1284 public function continue_button($link) {
1285 if (!is_a($link, 'moodle_url')) {
1286 $link = new moodle_url($link);
1287 }
1288 return $this->output_tag('div', array('class' => 'continuebutton'),
1289 print_single_button($link->out(true), $link->params(), get_string('continue'), 'get', '', true));
1290 }
1291
1292 /**
1293 * Output the place a skip link goes to.
1294 * @param $id The target name from the corresponding $PAGE->requires->skip_link_to($target) call.
1295 * @return string the HTML to output.
1296 */
1297 public function skip_link_target($id = 'maincontent') {
1298 return $this->output_tag('span', array('id' => $id), '');
1299 }
1300
1301 public function heading($text, $level, $classes = 'main', $id = '') {
1302 $level = (integer) $level;
1303 if ($level < 1 or $level > 6) {
1304 throw new coding_exception('Heading level must be an integer between 1 and 6.');
1305 }
1306 return $this->output_tag('h' . $level,
1307 array('id' => $id, 'class' => moodle_renderer_base::prepare_classes($classes)), $text);
1308 }
1309
1310 public function box($contents, $classes = 'generalbox', $id = '') {
1311 return $this->box_start($classes, $id) . $contents . $this->box_end();
1312 }
1313
1314 public function box_start($classes = 'generalbox', $id = '') {
1315 $this->opencontainers->push('box', $this->output_end_tag('div'));
1316 return $this->output_start_tag('div', array('id' => $id,
1317 'class' => 'box ' . moodle_renderer_base::prepare_classes($classes)));
1318 }
1319
1320 public function box_end() {
1321 return $this->opencontainers->pop('box');
1322 }
1323
1324 public function container($contents, $classes = '', $id = '') {
1325 return $this->container_start($classes, $id) . $contents . $this->container_end();
1326 }
1327
1328 public function container_start($classes = '', $id = '') {
1329 $this->opencontainers->push('container', $this->output_end_tag('div'));
1330 return $this->output_start_tag('div', array('id' => $id,
1331 'class' => moodle_renderer_base::prepare_classes($classes)));
1332 }
1333
1334 public function container_end() {
1335 return $this->opencontainers->pop('container');
1336 }
a5cb8d69 1337
1338 /**
1339 * At the moment we frequently have a problem with $CFG->pixpath not being
1340 * initialised when it is needed. Unfortunately, there is no nice way to handle
1341 * this. I think we need to replace $CFG->pixpath with something like $OUTPUT->icon(...).
1342 * However, until then, we need a way to force $CFG->pixpath to be initialised,
1343 * to fix the error messages, and that is what this function if for.
1344 */
1345 public function initialise_deprecated_cfg_pixpath() {
1346 // Actually, we don't have to do anything here. Just calling any method
1347 // of $OBJECT is enough. However, if the only reason you are calling
1348 // an $OUTPUT method is to get $CFG->pixpath initialised, please use this
1349 // method, so we can find them and clean them up later once we have
1350 // found a better replacement for $CFG->pixpath.
1351 }
8954245a 1352}
1353
1354
b7009474 1355/**
1356 *This class represents the configuration variables of a Moodle theme.
1357 *
1358 * Normally, to create an instance of this class, you should use the
1359 * {@link theme_config::load()} factory method to load a themes config.php file.
1360 *
1361 * @copyright 2009 Tim Hunt
1362 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1363 * @since Moodle 2.0
1364 */
1365class theme_config {
1366 /**
1367 * @var array The names of all the stylesheets from this theme that you would
1368 * like included, in order.
1369 */
1370 public $sheets = array('styles_layout', 'styles_fonts', 'styles_color');
1371
1372 public $standardsheets = true;
1373
1374/// This variable can be set to an array containing
1375/// filenames from the *STANDARD* theme. If the
1376/// array exists, it will be used to choose the
1377/// files to include in the standard style sheet.
1378/// When false, then no files are used.
1379/// When true or NON-EXISTENT, then ALL standard files are used.
1380/// This parameter can be used, for example, to prevent
1381/// having to override too many classes.
1382/// Note that the trailing .css should not be included
1383/// eg $THEME->standardsheets = array('styles_layout','styles_fonts','styles_color');
1384////////////////////////////////////////////////////////////////////////////////
1385
1386
1387 public $parent = null;
1388
1389/// This variable can be set to the name of a parent theme
1390/// which you want to have included before the current theme.
1391/// This can make it easy to make modifications to another
1392/// theme without having to actually change the files
1393/// If this variable is empty or false then a parent theme
1394/// is not used.
1395////////////////////////////////////////////////////////////////////////////////
1396
1397
1398 public $parentsheets = false;
1399
1400/// This variable can be set to an array containing
1401/// filenames from a chosen *PARENT* theme. If the
1402/// array exists, it will be used to choose the
1403/// files to include in the standard style sheet.
1404/// When false, then no files are used.
1405/// When true or NON-EXISTENT, then ALL standard files are used.
1406/// This parameter can be used, for example, to prevent
1407/// having to override too many classes.
1408/// Note that the trailing .css should not be included
1409/// eg $THEME->parentsheets = array('styles_layout','styles_fonts','styles_color');
1410////////////////////////////////////////////////////////////////////////////////
1411
1412
1413 public $modsheets = true;
1414
1415/// When this is enabled, then this theme will search for
1416/// files named "styles.php" inside all Activity modules and
1417/// include them. This allows modules to provide some basic
1418/// layouts so they work out of the box.
1419/// It is HIGHLY recommended to leave this enabled.
1420
1421
1422 public $blocksheets = true;
1423
1424/// When this is enabled, then this theme will search for
1425/// files named "styles.php" inside all Block modules and
1426/// include them. This allows Blocks to provide some basic
1427/// layouts so they work out of the box.
1428/// It is HIGHLY recommended to leave this enabled.
1429
1430
1431 public $langsheets = false;
1432
1433/// By setting this to true, then this theme will search for
1434/// a file named "styles.php" inside the current language
1435/// directory. This allows different languages to provide
1436/// different styles.
1437
1438
1439 public $courseformatsheets = true;
1440
1441/// When this is enabled, this theme will search for files
1442/// named "styles.php" inside all course formats and
1443/// include them. This allows course formats to provide
1444/// their own default styles.
1445
1446
1447 public $metainclude = false;
1448
1449/// When this is enabled (or not set!) then Moodle will try
1450/// to include a file meta.php from this theme into the
1451/// <head></head> part of the page.
1452
1453
1454 public $standardmetainclude = true;
1455
1456
1457/// When this is enabled (or not set!) then Moodle will try
1458/// to include a file meta.php from the standard theme into the
1459/// <head></head> part of the page.
1460
1461
1462 public $parentmetainclude = false;
1463
1464/// When this is enabled (or not set!) then Moodle will try
1465/// to include a file meta.php from the parent theme into the
1466/// <head></head> part of the page.
1467
1468
1469 public $navmenuwidth = 50;
1470
1471/// You can use this to control the cutoff point for strings
1472/// in the navmenus (list of activities in popup menu etc)
1473/// Default is 50 characters wide.
1474
1475
1476 public $makenavmenulist = false;
1477
1478/// By setting this to true, then you will have access to a
1479/// new variable in your header.html and footer.html called
1480/// $navmenulist ... this contains a simple XHTML menu of
1481/// all activities in the current course, mostly useful for
1482/// creating popup navigation menus and so on.
1483
1484
1485
1486 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';
1487
1488/// With this you can control the colours of the "big" MP3 player
1489/// that is used for MP3 resources.
1490
1491
1492 public $filter_mediaplugin_colors = 'bgColour=000000&btnColour=ffffff&btnBorderColour=cccccc&iconColour=000000&iconOverColour=00cc00&trackColour=cccccc&handleColour=ffffff&loaderColour=ffffff&waitForPlay=yes';
1493
1494/// ...And this controls the small embedded player
1495
1496
1497 public $custompix = false;
1498
1499/// If true, then this theme must have a "pix"
1500/// subdirectory that contains copies of all
1501/// files from the moodle/pix directory, plus a
1502/// "pix/mod" directory containing all the icons
1503/// for all the activity modules.
1504
1505
1506///$THEME->rarrow = '&#x25BA;' //OR '&rarr;';
1507///$THEME->larrow = '&#x25C4;' //OR '&larr;';
1508///$CFG->block_search_button = link_arrow_right(get_string('search'), $url='', $accesshide=true);
1509///
1510/// Accessibility: Right and left arrow-like characters are
1511/// used in the breadcrumb trail, course navigation menu
1512/// (previous/next activity), calendar, and search forum block.
1513///
1514/// If the theme does not set characters, appropriate defaults
1515/// are set by (lib/weblib.php:check_theme_arrows). The suggestions
1516/// above are 'silent' in a screen-reader like JAWS. Please DO NOT
1517/// use &lt; &gt; &raquo; - these are confusing for blind users.
1518////////////////////////////////////////////////////////////////////////////////
1519
1520
1521 public $blockregions = array('side-pre', 'side-post');
1522 public $defaultblockregion = 'side-post';
1523/// Areas where blocks may appear on any page that uses this theme. For each
1524/// region you list in $THEME->blockregions you must call blocks_print_group
1525/// with that region id somewhere in header.html or footer.html.
1526/// defaultblockregion is the region where new blocks will be added, and
1527/// where any blocks in unrecognised regions will be shown. (Suppose someone
1528/// added a block when anther theme was selected).
1529////////////////////////////////////////////////////////////////////////////////
1530
db8d89d8 1531 /** @var string the name of this theme. Set automatically. */
b7009474 1532 public $name;
db8d89d8 1533 /** @var string the folder where this themes fiels are stored. $CFG->themedir . '/' . $this->name */
b7009474 1534 public $dir;
1535
db8d89d8 1536 /** @var string Name of the renderer factory class to use. */
b7009474 1537 public $rendererfactory = 'standard_renderer_factory';
db8d89d8 1538 /** @var renderer_factory Instance of the renderer_factory class. */
1539 protected $rf = null;
b7009474 1540
1541 /**
1542 * If you want to do custom processing on the CSS before it is output (for
1543 * example, to replace certain variable names with particular values) you can
1544 * give the name of a function here.
1545 *
1546 * There are two functions avaiable that you may wish to use (defined in lib/outputlib.php):
1547 * output_css_replacing_constants
1548 * output_css_for_css_edit
1549 * If you wish to write your own function, use those two as examples, and it
1550 * should be clear what you have to do.
1551 *
1552 * @var string the name of a function.
1553 */
1554 public $customcssoutputfunction = null;
1555
1556 /**
1557 * Load the config.php file for a particular theme, and return an instance
1558 * of this class. (That is, this is a factory method.)
1559 *
1560 * @param string $themename the name of the theme.
1561 * @return theme_config an instance of this class.
1562 */
1563 public static function load($themename) {
db8d89d8 1564 global $CFG;
b7009474 1565
1566 // We have to use the variable name $THEME (upper case) becuase that
1567 // is what is used in theme config.php files.
1568
1569 // Set some other standard properties of the theme.
1570 $THEME = new theme_config;
1571 $THEME->name = $themename;
1572 $THEME->dir = $CFG->themedir . '/' . $themename;
1573
1574 // Load up the theme config
1575 $configfile = $THEME->dir . '/config.php';
1576 if (!is_readable($configfile)) {
1577 throw new coding_exception('Cannot use theme ' . $themename .
1578 '. The file ' . $configfile . ' does not exist or is not readable.');
1579 }
1580 include($configfile);
1581
1582 $THEME->update_legacy_information();
1583
1584 return $THEME;
1585 }
1586
db8d89d8 1587 /**
1588 * Get the renderer for a part of Moodle for this theme.
1589 * @param string $module the name of part of moodle. E.g. 'core', 'quiz', 'qtype_multichoice'.
1590 * @param moodle_page $page the page we are rendering
1591 * @return moodle_renderer_base the requested renderer.
1592 */
1593 public function get_renderer($module, $page) {
1594 if (is_null($this->rf)) {
1595 if (CLI_SCRIPT) {
1596 $classname = 'cli_renderer_factory';
1597 } else {
1598 $classname = $this->rendererfactory;
1599 }
1600 $this->rf = new $classname($this);
1601 }
1602
1603 return $this->rf->get_renderer($module, $page);
1604 }
1605
b7009474 1606 /**
1607 * Set the variable $CFG->pixpath and $CFG->modpixpath to be the right
1608 * ones for this theme.
1609 */
1610 public function setup_cfg_paths() {
1611 global $CFG;
1612 if (!empty($CFG->smartpix)) {
1613 if ($CFG->slasharguments) {
1614 // Use this method if possible for better caching
1615 $extra = '';
1616 } else {
1617 $extra = '?file=';
1618 }
1619 $CFG->pixpath = $CFG->httpswwwroot . '/pix/smartpix.php' . $extra . '/' . $this->name;
1620 $CFG->modpixpath = $CFG->httpswwwroot . '/pix/smartpix.php' . $extra . '/' . $this->name . '/mod';
1621
1622 } else if (empty($THEME->custompix)) {
1623 $CFG->pixpath = $CFG->httpswwwroot . '/pix';
1624 $CFG->modpixpath = $CFG->httpswwwroot . '/mod';
1625
1626 } else {
1627 $CFG->pixpath = $CFG->httpsthemewww . '/' . $this->name . '/pix';
1628 $CFG->modpixpath = $CFG->httpsthemewww . '/' . $this->name . '/pix/mod';
1629 }
1630 }
1631
1632 /**
1633 * Get the list of stylesheet URLs that need to go in the header for this theme.
1634 * @return array of URLs.
1635 */
1636 public function get_stylesheet_urls() {
1637 global $CFG;
1638
1639 // Put together the parameters
1640 $params = '?for=' . $this->name;
1641
1642 // Stylesheets, in order (standard, parent, this - some of which may be the same).
1643 $stylesheets = array();
1644 if ($this->name != 'standard' && $this->standardsheets) {
1645 $stylesheets[] = $CFG->httpsthemewww . '/standard/styles.php' . $params;
1646 }
1647 if (!empty($this->parent)) {
1648 $stylesheets[] = $CFG->httpsthemewww . '/' . $this->parent . '/styles.php' . $params;
1649 }
1650
1651 // Pass on the current language, if it will be needed.
1652 if (!empty($this->langsheets)) {
1653 $params .= '&lang=' . current_language();
1654 }
1655 $stylesheets[] = $CFG->httpsthemewww . '/' . $this->name . '/styles.php' . $params;
1656
1657 // Additional styles for right-to-left languages.
1658 if (right_to_left()) {
1659 $stylesheets[] = $CFG->httpsthemewww . '/standard/rtl.css';
1660
1661 if (!empty($this->parent) && file_exists($CFG->themedir . '/' . $this->parent . '/rtl.css')) {
1662 $stylesheets[] = $CFG->httpsthemewww . '/' . $this->parent . '/rtl.css';
1663 }
1664
1665 if (file_exists($this->dir . '/rtl.css')) {
1666 $stylesheets[] = $CFG->httpsthemewww . '/' . $this->name . '/rtl.css';
1667 }
1668 }
1669
1670 return $stylesheets;
1671 }
1672
1673 /**
1674 * This methon looks a the settings that have been loaded, to see whether
1675 * any legacy things are being used, and outputs warning and tries to update
1676 * things to use equivalent newer settings.
1677 */
1678 protected function update_legacy_information() {
1679 if (!empty($this->customcorners)) {
1680 // $THEME->customcorners is deprecated but we provide support for it via the
1681 // custom_corners_renderer_factory class in lib/deprecatedlib.php
1682 debugging('$THEME->customcorners is deprecated. Please use the new $THEME->rendererfactory ' .
1683 'to control HTML generation. Please use $this->rendererfactory = \'custom_corners_renderer_factory\'; ' .
1684 'in your config.php file instead.', DEBUG_DEVELOPER);
1685 $this->rendererfactory = 'custom_corners_renderer_factory';
1686 }
1687
1688 if (!empty($this->cssconstants)) {
1689 debugging('$THEME->cssconstants is deprecated. Please use ' .
1690 '$THEME->customcssoutputfunction = \'output_css_replacing_constants\'; ' .
1691 'in your config.php file instead.', DEBUG_DEVELOPER);
1692 $this->customcssoutputfunction = 'output_css_replacing_constants';
1693 }
1694
1695 if (!empty($this->CSSEdit)) {
1696 debugging('$THEME->CSSEdit is deprecated. Please use ' .
1697 '$THEME->customcssoutputfunction = \'output_css_for_css_edit\'; ' .
1698 'in your config.php file instead.', DEBUG_DEVELOPER);
1699 $this->customcssoutputfunction = 'output_css_for_css_edit';
1700 }
1701 }
1702}
1703
1704
8954245a 1705/**
1706 * Base class for classes representing HTML elements, like moodle_select_menu.
1707 *
1708 * Handles the id and class attribues.
1709 *
1710 * @copyright 2009 Tim Hunt
1711 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1712 * @since Moodle 2.0
1713 */
1714class moodle_html_component {
1715 /**
1716 * @var string value to use for the id attribute of this HTML tag.
1717 */
1718 public $id = '';
1719 /**
1720 * @var array class names to add to this HTML element.
1721 */
1722 public $classes = array();
1723
1724 /**
1725 * Ensure some class names are an array.
1726 * @param mixed $classes either an array of class names or a space-separated
1727 * string containing class names.
1728 * @return array the class names as an array.
1729 */
1730 public static function clean_clases($classes) {
1731 if (is_array($classes)) {
1732 return $classes;
1733 } else {
a5cb8d69 1734 return explode(' ', trim($classes));
8954245a 1735 }
1736 }
1737
1738 /**
1739 * Set the class name array.
1740 * @param mixed $classes either an array of class names or a space-separated
1741 * string containing class names.
1742 */
1743 public function set_classes($classes) {
1744 $this->classes = self::clean_clases($classes);
1745 }
1746
1747 /**
1748 * Add a class name to the class names array.
1749 * @param string $class the new class name to add.
1750 */
1751 public function add_class($class) {
1752 $this->classes[] = $class;
1753 }
1754
1755 /**
1756 * Add a whole lot of class names to the class names array.
1757 * @param mixed $classes either an array of class names or a space-separated
1758 * string containing class names.
1759 */
1760 public function add_classes($classes) {
1761 $this->classes += self::clean_clases($classes);
1762 }
1763
1764 /**
1765 * Get the class names as a string.
1766 * @return string the class names as a space-separated string. Ready to be put in the class="" attribute.
1767 */
1768 public function get_classes_string() {
1769 return implode(' ', $this->classes);
1770 }
1771
1772 /**
1773 * Perform any cleanup or final processing that should be done before an
1774 * instance of this class is output.
1775 */
1776 public function prepare() {
1777 $this->classes = array_unique(self::clean_clases($this->classes));
1778 }
1779}
1780
1781
1782/**
1783 * This class hold all the information required to describe a <select> menu that
34a2777c 1784 * will be printed by {@link moodle_core_renderer::select_menu()}. (Or by an overridden
8954245a 1785 * version of that method in a subclass.)
1786 *
1787 * All the fields that are not set by the constructor have sensible defaults, so
1788 * you only need to set the properties where you want non-default behaviour.
1789 *
1790 * @copyright 2009 Tim Hunt
1791 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1792 * @since Moodle 2.0
1793 */
1794class moodle_select_menu extends moodle_html_component {
1795 /**
1796 * @var array the choices to show in the menu. An array $value => $label.
1797 */
1798 public $options;
1799 /**
1800 * @var string the name of this form control. That is, the name of the GET/POST
1801 * variable that will be set if this select is submmitted as part of a form.
1802 */
1803 public $name;
1804 /**
1805 * @var string the option to select initially. Should match one
1806 * of the $options array keys. Default none.
1807 */
1808 public $selectedvalue;
1809 /**
1810 * @var string The label for the 'nothing is selected' option.
1811 * Defaults to get_string('choosedots').
1812 * Set this to '' if you do not want a 'nothing is selected' option.
1813 */
1814 public $nothinglabel = null;
1815 /**
1816 * @var string The value returned by the 'nothing is selected' option. Defaults to 0.
1817 */
1818 public $nothingvalue = 0;
1819 /**
1820 * @var boolean set this to true if you want the control to appear disabled.
1821 */
1822 public $disabled = false;
1823 /**
1824 * @var integer if non-zero, sets the tabindex attribute on the <select> element. Default 0.
1825 */
1826 public $tabindex = 0;
1827 /**
1828 * @var mixed Defaults to false, which means display the select as a dropdown menu.
1829 * If true, display this select as a list box whose size is chosen automatically.
1830 * If an integer, display as list box of that size.
1831 */
1832 public $listbox = false;
1833 /**
1834 * @var integer if you are using $listbox === true to get an automatically
1835 * sized list box, the size of the list box will be the number of options,
1836 * or this number, whichever is smaller.
1837 */
1838 public $maxautosize = 10;
1839 /**
1840 * @var boolean if true, allow multiple selection. Only used if $listbox is true.
1841 */
1842 public $multiple = false;
1843 /**
1844 * @deprecated
1845 * @var string JavaScript to add as an onchange attribute. Do not use this.
1846 * Use the YUI even library instead.
1847 */
1848 public $script = '';
1849
1850 /* @see lib/moodle_html_component#prepare() */
1851 public function prepare() {
1852 if (empty($this->id)) {
1853 $this->id = 'menu' . str_replace(array('[', ']'), '', $this->name);
1854 }
1855 if (empty($this->classes)) {
1856 $this->set_classes(array('menu' . str_replace(array('[', ']'), '', $this->name)));
1857 }
1858 $this->add_class('select');
1859 parent::prepare();
1860 }
1861
1862 /**
1863 * This is a shortcut for making a simple select menu. It lets you specify
1864 * the options, name and selected option in one line of code.
1865 * @param array $options used to initialise {@link $options}.
1866 * @param string $name used to initialise {@link $name}.
1867 * @param string $selected used to initialise {@link $selected}.
1868 * @return moodle_select_menu A moodle_select_menu object with the three common fields initialised.
1869 */
1870 public static function make($options, $name, $selected = '') {
1871 $menu = new moodle_select_menu();
1872 $menu->options = $options;
1873 $menu->name = $name;
1874 $menu->selectedvalue = $selected;
1875 return $menu;
1876 }
1877
1878 /**
1879 * This is a shortcut for making a yes/no select menu.
1880 * @param string $name used to initialise {@link $name}.
1881 * @param string $selected used to initialise {@link $selected}.
1882 * @return moodle_select_menu A menu initialised with yes/no options.
1883 */
1884 public static function make_yes_no($name, $selected) {
1885 return self::make(array(0 => get_string('no'), 1 => get_string('yes')), $name, $selected);
1886 }
571fa828 1887}
1888
1889
a5cb8d69 1890/**
1891 * This class hold all the information required to describe a Moodle block.
1892 *
1893 * That is, it holds all the different bits of HTML content that need to be put into the block.
1894 *
1895 * @copyright 2009 Tim Hunt
1896 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1897 * @since Moodle 2.0
1898 */
1899class block_contents extends moodle_html_component {
1900 protected static $idcounter = 1;
1901 /**
1902 * @param string $heading HTML for the heading. Can include full HTML or just
1903 * plain text - plain text will automatically be enclosed in the appropriate
1904 * heading tags.
1905 */
1906 public $heading = '';
1907 /**
1908 * @param string $title Plain text title, as embedded in the $heading.
1909 */
1910 public $title = '';
1911 /**
1912 * @param string $content HTML for the content
1913 */
1914 public $content = '';
1915 /**
1916 * @param array $list an alternative to $content, it you want a list of things with optional icons.
1917 */
1918 public $list = array();
1919 /**
1920 * @param array $icons optional icons for the things in $list.
1921 */
1922 public $icons = array();
1923 /**
1924 * @param string $footer Extra HTML content that gets output at the end, inside a &lt;div class="footer">
1925 */
1926 public $footer = '';
1927 /**
1928 * @param array $attributes an array of attribute => value pairs that are put on the
1929 * outer div of this block. {@link $id} and {@link $classes} attributes should be set separately.
1930 */
1931 public $attributes = array();
1932 /**
1933 * @param integer $skipid do not set this manually. It is set automatically be the {@link prepare()} method.
1934 */
1935 public $skipid;
1936
1937 /* @see lib/moodle_html_component#prepare() */
1938 public function prepare() {
1939 $this->skipid = self::$idcounter;
1940 self::$idcounter += 1;
1941 $this->add_class('sideblock');
1942 parent::prepare();
1943 }
1944}
1945
1946
34a2777c 1947/**
1948 * A renderer that generates output for commandlines scripts.
1949 *
1950 * The implementation of this renderer is probably incomplete.
1951 *
1952 * @copyright 2009 Tim Hunt
1953 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1954 * @since Moodle 2.0
1955 */
1956class cli_core_renderer extends moodle_core_renderer {
1957 public function header() {
1958 output_starting_hook();
1959 return $this->page->heading . "\n";
1960 }
1961
1962 public function heading($text, $level, $classes = 'main', $id = '') {
1963 $text .= "\n";
1964 switch ($level) {
1965 case 1:
1966 return '=>' . $text;
1967 case 2:
1968 return '-->' . $text;
1969 default:
1970 return $text;
1971 }
1972 }
1973
1974 public function fatal_error($errorcode, $module, $a, $link, $backtrace,
1975 $debuginfo = null, $showerrordebugwarning = false) {
1976 $output = "!!! $message !!!\n";
1977
1978 if (debugging('', DEBUG_DEVELOPER)) {
1979 if (!empty($debuginfo)) {
1980 $this->notification($debuginfo, 'notifytiny');
1981 }
1982 if (!empty($backtrace)) {
1983 $this->notification('Stack trace: ' . format_backtrace($backtrace, true), 'notifytiny');
1984 }
1985 }
1986 }
1987
1988 public function notification($message, $classes = 'notifyproblem') {
1989 $message = clean_text($message);
1990 if ($style === 'notifysuccess') {
1991 return "++ $message ++\n";
1992 }
1993 return "!! $message !!\n";
1994 }
1995}
1996
b7009474 1997
1998/**
1999 * Output CSS while replacing constants/variables. See MDL-6798 for details
2000 *
2001 * Information from Urs Hunkler:
2002 *
2003 * This is an adaptation of Shaun Inman's "CSS Server-side Constants" for Moodle.
2004 * http://www.shauninman.com/post/heap/2005/08/09/css_constants
2005 *
2006 * To use, specify $THEME->customcssoutputfunction = 'output_css_replacing_constants';
2007 * in your theme's config.php file.
2008 *
2009 * The constant definitions are written into a separate CSS file named like
2010 * constants.css and loaded first in config.php. You can use constants for any
2011 * CSS properties. The constant definition looks like:
2012 * <code>
2013 * \@server constants {
2014 * fontColor: #3a2830;
2015 * aLink: #116699;
2016 * aVisited: #AA2200;
2017 * aHover: #779911;
2018 * pageBackground: #FFFFFF;
2019 * backgroundColor: #EEEEEE;
2020 * backgroundSideblockHeader: #a8a4e9;
2021 * fontcolorSideblockHeader: #222222;
2022 * color1: #98818b;
2023 * color2: #bd807b;
2024 * color3: #f9d1d7;
2025 * color4: #e8d4d8;
2026 * }
2027 * </code>
2028 *
2029 * The lines in the CSS files using CSS constants look like:
2030 * <code>
2031 * body {
2032 * font-size: 100%;
2033 * background-color: pageBackground;
2034 * color: fontColor;
2035 * font-family: 'Bitstream Vera Serif', georgia, times, serif;
2036 * margin: 0;
2037 * padding: 0;
2038 * }
2039 * div#page {
2040 * margin: 0 10px;
2041 * padding-top: 5px;
2042 * border-top-width: 10px;
2043 * border-top-style: solid;
2044 * border-top-color: color3;
2045 * }
2046 * div.clearer {
2047 * clear: both;
2048 * }
2049 * a:link {
2050 * color: aLink;
2051 * }
2052 * </code>
2053 *
2054 * @param array $files an arry of the CSS fiels that need to be output.
2055 */
2056function output_css_replacing_constants($files) {
2057 global $CFG;
2058 // Get all the CSS.
2059 $toreplace = array($CFG->dirroot, $CFG->themedir);
2060 ob_start();
2061 foreach ($files as $file) {
2062 $shortname = str_replace($toreplace, '', $file);
2063 echo '/******* ' . $shortname . " start *******/\n\n";
2064 @include_once($file);
2065 echo '/******* ' . $shortname . " end *******/\n\n";
2066 }
2067 $css = ob_get_contents();
2068 ob_end_clean();
2069
2070 if (preg_match_all("/@server\s+(?:variables|constants)\s*\{\s*([^\}]+)\s*\}\s*/i", $css, $matches)) {
2071 $variables = array();
2072 foreach ($matches[0] as $key => $server) {
2073 $css = str_replace($server, '', $css);
2074 preg_match_all("/([^:\}\s]+)\s*:\s*([^;\}]+);/", $matches[1][$key], $vars);
2075 foreach ($vars[1] as $var => $value) {
2076 $variables[$value] = $vars[2][$var];
2077 }
2078 }
2079 $css = str_replace(array_keys($variables), array_values($variables), $css);
2080 }
2081 echo $css;
2082}
2083
2084/**
2085 * This CSS output function will link to CSS files rather than including them
2086 * inline.
2087 *
2088 * The single CSS files can then be edited and saved with interactive
2089 * CSS editors like CSSEdit. Any files that have a .php extension are still included
2090 * inline.
2091 *
2092 * @param array $files an arry of the CSS fiels that need to be output.
2093 */
2094function output_css_for_css_edit($files) {
2095 global $CFG;
2096 $toreplace = array($CFG->dirroot, $CFG->themedir);
2097 foreach ($files as $file) {
2098 $shortname = str_replace($toreplace, '', $file);
2099 echo '/* @group ' . $shortname . " */\n\n";
2100 if (strpos($file, '.css') !== false) {
2101 echo '@import url("' . $file . '");'."\n\n";
2102 } else {
2103 @include_once($file);
2104 }
2105 echo "/* @end */\n\n";
2106 }
2107}