]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-19077 Split outputlib.php into smaller logical files.
authornicolasconnault <nicolasconnault>
Mon, 10 Aug 2009 06:22:04 +0000 (06:22 +0000)
committernicolasconnault <nicolasconnault>
Mon, 10 Aug 2009 06:22:04 +0000 (06:22 +0000)
lib/outputactions.php [new file with mode: 0644]
lib/outputcomponents.php [new file with mode: 0644]
lib/outputfactories.php [new file with mode: 0644]
lib/outputlib.php
lib/outputpixfinders.php [new file with mode: 0644]
lib/outputrenderers.php [new file with mode: 0644]

diff --git a/lib/outputactions.php b/lib/outputactions.php
new file mode 100644 (file)
index 0000000..3d29553
--- /dev/null
@@ -0,0 +1,160 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Classes representing JS event handlers, used by output components.
+ *
+ * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML
+ * for an overview.
+ *
+ * @package   moodlecore
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Helper class used by other components that involve an action on the page (URL or JS).
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class component_action {
+
+    /**
+     * The DOM event that will trigger this action when caught
+     * @var string $event DOM event
+     */
+    public $event;
+
+    /**
+     * The JS function you create must have two arguments:
+     *      1. The event object
+     *      2. An object/array of arguments ($jsfunctionargs)
+     * @var string $jsfunction A function name to call when the button is clicked
+     */
+    public $jsfunction = false;
+
+    /**
+     * @var array $jsfunctionargs An array of arguments to pass to the JS function
+     */
+    public $jsfunctionargs = array();
+
+    /**
+     * Constructor
+     * @param string $event DOM event
+     * @param moodle_url $url A moodle_url object, required if no jsfunction is given
+     * @param string $method 'post' or 'get'
+     * @param string $jsfunction An optional JS function. Required if jsfunctionargs is given
+     * @param array  $jsfunctionargs An array of arguments to pass to the jsfunction
+     * @return void
+     */
+    public function __construct($event, $jsfunction, $jsfunctionargs=array()) {
+        $this->event = $event;
+
+        $this->jsfunction = $jsfunction;
+        $this->jsfunctionargs = $jsfunctionargs;
+
+        if (!empty($this->jsfunctionargs)) {
+            if (empty($this->jsfunction)) {
+                throw new coding_exception('The component_action object needs a jsfunction value to pass the jsfunctionargs to.');
+            }
+        }
+    }
+}
+
+/**
+ * Component action for a popup window.
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class popup_action extends component_action {
+    /**
+     * @var array $params An array of parameters that will be passed to the openpopup JS function
+     */
+    public $params = array(
+            'height' =>  400,
+            'width' => 500,
+            'top' => 0,
+            'left' => 0,
+            'menubar' => false,
+            'location' => false,
+            'scrollbars' => true,
+            'resizable' => true,
+            'toolbar' => true,
+            'status' => true,
+            'directories' => false,
+            'fullscreen' => false,
+            'dependent' => true);
+
+    /**
+     * Constructor
+     * @param string $event DOM event
+     * @param moodle_url $url A moodle_url object, required if no jsfunction is given
+     * @param string $method 'post' or 'get'
+     * @param array  $params An array of popup parameters
+     * @return void
+     */
+    public function __construct($event, $url, $name='popup', $params=array()) {
+        global $CFG;
+        $this->name = $name;
+
+        $url = new moodle_url($url);
+
+        if ($this->name) {
+            $_name = $this->name;
+            if (($_name = preg_replace("/\s/", '_', $_name)) != $this->name) {
+                throw new coding_exception('The $name of a popup window shouldn\'t contain spaces - string modified. '. $this->name .' changed to '. $_name);
+                $this->name = $_name;
+            }
+        } else {
+            $this->name = 'popup';
+        }
+
+        foreach ($this->params as $var => $val) {
+            if (array_key_exists($var, $params)) {
+                $this->params[$var] = $params[$var];
+            }
+        }
+        parent::__construct($event, 'openpopup', array('url' => $url->out(false, array(), false), 'name' => $name, 'options' => $this->get_js_options($params)));
+    }
+
+    /**
+     * Returns a string of concatenated option->value pairs used by JS to call the popup window,
+     * based on this object's variables
+     *
+     * @return string String of option->value pairs for JS popup function.
+     */
+    public function get_js_options() {
+        $jsoptions = '';
+
+        foreach ($this->params as $var => $val) {
+            if (is_string($val) || is_int($val)) {
+                $jsoptions .= "$var=$val,";
+            } elseif (is_bool($val)) {
+                $jsoptions .= ($val) ? "$var," : "$var=0,";
+            }
+        }
+
+        $jsoptions = substr($jsoptions, 0, strlen($jsoptions) - 1);
+
+        return $jsoptions;
+    }
+}
+
diff --git a/lib/outputcomponents.php b/lib/outputcomponents.php
new file mode 100644 (file)
index 0000000..37e9931
--- /dev/null
@@ -0,0 +1,2113 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Classes representing HTML elements, used by $OUTPUT methods
+ *
+ * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML
+ * for an overview.
+ *
+ * @package   moodlecore
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Base class for classes representing HTML elements, like moodle_select.
+ *
+ * Handles the id and class attributes.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class moodle_html_component {
+    /**
+     * @var string value to use for the id attribute of this HTML tag.
+     */
+    public $id = '';
+    /**
+     * @var string $alt value to use for the alt attribute of this HTML tag.
+     */
+    public $alt = '';
+    /**
+     * @var string $style value to use for the style attribute of this HTML tag.
+     */
+    public $style = '';
+    /**
+     * @var array class names to add to this HTML element.
+     */
+    public $classes = array();
+    /**
+     * @var string $title The title attributes applicable to any XHTML element
+     */
+    public $title = '';
+    /**
+     * An optional array of component_action objects handling the action part of this component.
+     * @var array $actions
+     */
+    protected $actions = array();
+    /**
+     * This array of generated ids is kept static to avoid id collisions
+     * @var array $generated_ids
+     */
+    public static $generated_ids = array();
+
+    /**
+     * Ensure some class names are an array.
+     * @param mixed $classes either an array of class names or a space-separated
+     *      string containing class names.
+     * @return array the class names as an array.
+     */
+    public static function clean_classes($classes) {
+        if (is_array($classes)) {
+            return $classes;
+        } else {
+            return explode(' ', trim($classes));
+        }
+    }
+
+    /**
+     * Set the class name array.
+     * @param mixed $classes either an array of class names or a space-separated
+     *      string containing class names.
+     * @return void
+     */
+    public function set_classes($classes) {
+        $this->classes = self::clean_classes($classes);
+    }
+
+    /**
+     * Add a class name to the class names array.
+     * @param string $class the new class name to add.
+     * @return void
+     */
+    public function add_class($class) {
+        $this->classes[] = $class;
+    }
+
+    /**
+     * Add a whole lot of class names to the class names array.
+     * @param mixed $classes either an array of class names or a space-separated
+     *      string containing class names.
+     * @return void
+     */
+    public function add_classes($classes) {
+        $this->classes += self::clean_classes($classes);
+    }
+
+    /**
+     * Get the class names as a string.
+     * @return string the class names as a space-separated string. Ready to be put in the class="" attribute.
+     */
+    public function get_classes_string() {
+        return implode(' ', $this->classes);
+    }
+
+    /**
+     * Perform any cleanup or final processing that should be done before an
+     * instance of this class is output.
+     * @return void
+     */
+    public function prepare() {
+        $this->classes = array_unique(self::clean_classes($this->classes));
+    }
+
+    /**
+     * This checks developer do not try to assign a property directly
+     * if we have a setter for it. Otherwise, the property is set as expected.
+     * @param string $name The name of the variable to set
+     * @param mixed $value The value to assign to the variable
+     * @return void
+     */
+    public function __set($name, $value) {
+        if ($name == 'class') {
+            debugging('this way of setting css class has been deprecated. use set_classes() method instead.');
+            $this->set_classes($value);
+        } else {
+            $this->{$name} = $value;
+        }
+    }
+
+    /**
+     * Adds a JS action to this component.
+     * Note: the JS function you write must have only two arguments: (string)event and (object|array)args
+     * If you want to add an instantiated component_action (or one of its subclasses), give the object as the only parameter
+     *
+     * @param mixed  $event a DOM event (click, mouseover etc.) or a component_action object
+     * @param string $jsfunction The name of the JS function to call. required if argument 1 is a string (event)
+     * @param array  $jsfunctionargs An optional array of JS arguments to pass to the function
+     */
+    public function add_action($event, $jsfunction=null, $jsfunctionargs=array()) {
+        if (empty($this->id)) {
+            $this->generate_id();
+        }
+
+        if ($event instanceof component_action) {
+            $this->actions[] = $event;
+        } else {
+            if (empty($jsfunction)) {
+                throw new coding_exception('moodle_html_component::add_action requires a JS function argument if the first argument is a string event');
+            }
+            $this->actions[] = new component_action($event, $jsfunction, $jsfunctionargs);
+        }
+    }
+
+    /**
+     * Internal method for generating a unique ID for the purpose of event handlers.
+     */
+    protected function generate_id() {
+        // Generate an id that is not already used.
+        do {
+            $newid = get_class($this) . '-' . substr(sha1(microtime() * rand(0, 500)), 0, 6);
+        } while (in_array($this->id, moodle_html_component::$generated_ids));
+        $this->id = $newid;
+        moodle_html_component::$generated_ids[] = $newid;
+    }
+
+    /**
+     * Returns the array of component_actions.
+     * @return array Component actions
+     */
+    public function get_actions() {
+        return $this->actions;
+    }
+
+    /**
+     * Adds a descriptive label to the component.
+     *
+     * This can be used in two ways:
+     *
+     * <pre>
+     * $component->set_label($elementlabel, $elementid);
+     * // OR
+     * $label = new html_label();
+     * $label->for = $elementid;
+     * $label->text = $elementlabel;
+     * $component->set_label($label);
+     * </pre>
+     *
+     * Use the second form when you need to add additional HTML attributes
+     * to the label and/or JS actions.
+     *
+     * @param mixed $text Either the text of the label or a html_label object
+     * @param text  $for The value of the "for" attribute (the associated element's id)
+     * @return void
+     */
+    public function set_label($text, $for=null) {
+        if ($text instanceof html_label) {
+            $this->label = $text;
+        } else if (!empty($text)) {
+            $this->label = new html_label();
+            $this->label->for = $for;
+            if (empty($for) && !empty($this->id)) {
+                $this->label->for = $this->id;
+            }
+            $this->label->text = $text;
+        }
+    }
+}
+
+
+/**
+ * This class hold all the information required to describe a <select> menu that
+ * will be printed by {@link moodle_core_renderer::select()}. (Or by an overridden
+ * version of that method in a subclass.)
+ *
+ * This component can also hold enough metadata to be used as a popup form. It just
+ * needs a bit more setting up than for a simple menu. See the shortcut methods for
+ * developer-friendly usage.
+ *
+ * All the fields that are not set by the constructor have sensible defaults, so
+ * you only need to set the properties where you want non-default behaviour.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class moodle_select extends moodle_html_component {
+    /**
+     * The moodle_select object parses an array of options into component objects
+     * @see nested attribute
+     * @var mixed $options the choices to show in the menu. An array $value => $display, of html_select_option or of html_select_optgroup objects.
+     */
+    public $options;
+    /**
+     * @var string $name the name of this form control. That is, the name of the GET/POST
+     * variable that will be set if this select is submitted as part of a form.
+     */
+    public $name;
+    /**
+     * @var mixed $label The label for that component. String or html_label object
+     */
+    public $label;
+    /**
+     * @var string $selectedvalue the option to select initially. Should match one
+     * of the $options array keys. Default none.
+     */
+    public $selectedvalue;
+    /**
+     * Defaults to get_string('choosedots').
+     * Set this to '' if you do not want a 'nothing is selected' option.
+     * This is ignored if the rendertype is 'radio' or 'checkbox'
+     * @var string The label for the 'nothing is selected' option.
+     */
+    public $nothinglabel = null;
+    /**
+     * @var string The value returned by the 'nothing is selected' option. Defaults to 0.
+     */
+    public $nothingvalue = 0;
+    /**
+     * @var boolean set this to true if you want the control to appear disabled.
+     */
+    public $disabled = false;
+    /**
+     * @var integer if non-zero, sets the tabindex attribute on the <select> element. Default 0.
+     */
+    public $tabindex = 0;
+    /**
+     * @var mixed Defaults to false, which means display the select as a dropdown menu.
+     * If true, display this select as a list box whose size is chosen automatically.
+     * If an integer, display as list box of that size.
+     */
+    public $listbox = false;
+    /**
+     * @var integer if you are using $listbox === true to get an automatically
+     * sized list box, the size of the list box will be the number of options,
+     * or this number, whichever is smaller.
+     */
+    public $maxautosize = 10;
+    /**
+     * @var boolean if true, allow multiple selection. Only used if $listbox is true, or if
+     *      the select is to be output as checkboxes.
+     */
+    public $multiple = false;
+    /**
+     * Another way to use nested menu is to prefix optgroup labels with -- and end the optgroup with --
+     * Leave this setting to false if you are using the latter method.
+     * @var boolean $nested if true, uses $options' keys as option headings (optgroup)
+     */
+    public $nested = false;
+    /**
+     * @var html_form $form An optional html_form component
+     */
+    public $form;
+    /**
+     * @var help_icon $form An optional help_icon component
+     */
+    public $helpicon;
+    /**
+     * @var boolean $rendertype How the select element should be rendered: menu or radio (checkbox is just radio + multiple)
+     */
+    public $rendertype = 'menu';
+
+    /**
+     * @see moodle_html_component::prepare()
+     * @return void
+     */
+    public function prepare() {
+        global $CFG;
+
+        // name may contain [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading
+        if (empty($this->id)) {
+            $this->id = 'menu' . str_replace(array('[', ']'), '', $this->name);
+        }
+
+        if (empty($this->classes)) {
+            $this->set_classes(array('menu' . str_replace(array('[', ']'), '', $this->name)));
+        }
+
+        if (is_null($this->nothinglabel)) {
+            $this->nothinglabel = get_string('choosedots');
+        }
+
+        if (!empty($this->label) && !($this->label instanceof html_label)) {
+            $label = new html_label();
+            $label->text = $this->label;
+            $label->for = $this->name;
+            $this->label = $label;
+        }
+
+        $this->add_class('select');
+
+        $this->initialise_options();
+        parent::prepare();
+    }
+
+    /**
+     * This is a shortcut for making a simple select menu. It lets you specify
+     * the options, name and selected option in one line of code.
+     * @param array $options used to initialise {@link $options}.
+     * @param string $name used to initialise {@link $name}.
+     * @param string $selected  used to initialise {@link $selected}.
+     * @return moodle_select A moodle_select object with the three common fields initialised.
+     */
+    public static function make($options, $name, $selected = '') {
+        $menu = new moodle_select();
+        $menu->options = $options;
+        $menu->name = $name;
+        $menu->selectedvalue = $selected;
+        return $menu;
+    }
+
+    /**
+     * This is a shortcut for making a yes/no select menu.
+     * @param string $name used to initialise {@link $name}.
+     * @param string $selected  used to initialise {@link $selected}.
+     * @return moodle_select A menu initialised with yes/no options.
+     */
+    public static function make_yes_no($name, $selected) {
+        return self::make(array(0 => get_string('no'), 1 => get_string('yes')), $name, $selected);
+    }
+
+    /**
+     * This is a shortcut for making an hour selector menu.
+     * @param string $type The type of selector (years, months, days, hours, minutes)
+     * @param string $name fieldname
+     * @param int $currenttime A default timestamp in GMT
+     * @param int $step minute spacing
+     * @return moodle_select A menu initialised with hour options.
+     */
+    public static function make_time_selector($type, $name, $currenttime=0, $step=5) {
+
+        if (!$currenttime) {
+            $currenttime = time();
+        }
+        $currentdate = usergetdate($currenttime);
+        $userdatetype = $type;
+
+        switch ($type) {
+            case 'years':
+                for ($i=1970; $i<=2020; $i++) {
+                    $timeunits[$i] = $i;
+                }
+                $userdatetype = 'year';
+                break;
+            case 'months':
+                for ($i=1; $i<=12; $i++) {
+                    $timeunits[$i] = userdate(gmmktime(12,0,0,$i,15,2000), "%B");
+                }
+                $userdatetype = 'month';
+                break;
+            case 'days':
+                for ($i=1; $i<=31; $i++) {
+                    $timeunits[$i] = $i;
+                }
+                $userdatetype = 'mday';
+                break;
+            case 'hours':
+                for ($i=0; $i<=23; $i++) {
+                    $timeunits[$i] = sprintf("%02d",$i);
+                }
+                break;
+            case 'minutes':
+                if ($step != 1) {
+                    $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
+                }
+
+                for ($i=0; $i<=59; $i+=$step) {
+                    $timeunits[$i] = sprintf("%02d",$i);
+                }
+                break;
+            default:
+                throw new coding_exception("Time type $type is not supported by moodle_select::make_time_selector().");
+        }
+
+        $timerselector = self::make($timeunits, $name, $currentdate[$userdatetype]);
+        $timerselector->label = new html_label();
+        $timerselector->label->text = get_string(substr($type, -1), 'form');
+        $timerselector->label->for = "menu$timerselector->name";
+        $timerselector->label->add_class('accesshide');
+        $timerselector->nothinglabel = '';
+
+        return $timerselector;
+    }
+
+    /**
+     * Given an associative array of type => fieldname and an optional timestamp,
+     * returns an array of moodle_select components representing date/time selectors.
+     * @param array $selectors Arrays of type => fieldname. Selectors will be returned in the order of the types given
+     * @param int $currenttime A UNIX timestamp
+     * @param int $step minute spacing
+     * @return array Instantiated date/time selectors
+     */
+    public function make_time_selectors($selectors, $currenttime=0, $step=5) {
+        $selects = array();
+        foreach ($selectors as $type => $name) {
+            $selects[] = moodle_select::make_time_selector($type, $name, $currenttime, $step);
+        }
+        return $selects;
+    }
+
+    /**
+     * This is a shortcut for making a select popup form.
+     * @param mixed $baseurl The target URL, string or moodle_url
+     * @param string $name The variable which this select's options are changing in the URL
+     * @param array $options A list of value-label pairs for the popup list
+     * @param string $formid id for the control. Must be unique on the page. Used in the HTML.
+     * @param string $selected The option that is initially selected
+     * @return moodle_select A menu initialised as a popup form.
+     */
+    public function make_popup_form($baseurl, $name, $options, $formid, $selected=null) {
+        global $CFG;
+
+        $selectedurl = null;
+
+        if (!($baseurl instanceof moodle_url)) {
+            $baseurl = new moodle_url($baseurl);
+        }
+
+        if (!empty($selected)) {
+            $selectedurl = $baseurl->out(false, array($name => $selected), false);
+        }
+
+        if (!($baseurl instanceof moodle_url)) {
+            $baseurl = new moodle_url($baseurl);
+        }
+
+        // Replace real value by formatted URLs
+        foreach ($options as $value => $label) {
+            $options[$baseurl->out(false, array($name => $value), false)] = $label;
+            unset($options[$value]);
+        }
+
+        $select = self::make($options, 'jump', $selectedurl);
+
+        $select->form = new html_form();
+        $select->form->id = $formid;
+        $select->form->method = 'get';
+        $select->form->add_class('popupform');
+        $select->form->url = new moodle_url($CFG->wwwroot . '/course/jumpto.php', array('sesskey' => sesskey()));
+        $select->form->button->text = get_string('go');
+
+        $select->id = $formid . '_jump';
+
+        $select->add_action('change', 'submit_form_by_id', array('id' => $formid, 'selectid' => $select->id));
+
+        return $select;
+    }
+
+    /**
+     * Override the URLs of the default popup_form, which only supports one base URL
+     * @param array $options value=>label pairs representing select options
+     * @return void
+     */
+    public function override_option_values($options) {
+        global $PAGE;
+
+        $this->initialise_options();
+
+        reset($options);
+
+        foreach ($this->options as $optkey => $optgroup) {
+            if ($optgroup instanceof html_select_optgroup) {
+                foreach ($optgroup->options as $key => $option) {
+                    next($options);
+                    $this->options[$optkey]->options[$key]->value = key($options);
+
+                    $optionurl = new moodle_url(key($options));
+
+                    if ($optionurl->compare($PAGE->url, URL_MATCH_PARAMS)) {
+                        $this->options[$optkey]->options[$key]->selected = 'selected';
+                    }
+                }
+                next($options);
+            } else if ($optgroup instanceof html_select_option) {
+                next($options);
+                $this->options[$optkey]->value = key($options);
+                $optionurl = new moodle_url(key($options));
+
+                if ($optionurl->compare($PAGE->url, URL_MATCH_PARAMS)) {
+                    $this->options[$optkey]->selected = 'selected';
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds a help icon next to the select menu.
+     *
+     * This can be used in two ways:
+     *
+     * <pre>
+     * $select->set_help_icon($page, $text, $linktext);
+     * // OR
+     * $helpicon = new help_icon();
+     * $helpicon->page = $page;
+     * $helpicon->text = $text;
+     * $helpicon->linktext = $linktext;
+     * $select->set_help_icon($helpicon);
+     * </pre>
+     *
+     * Use the second form when you need to add additional HTML attributes
+     * to the label and/or JS actions.
+     *
+     * @param mixed $page Either the keyword that defines a help page or a help_icon object
+     * @param text  $text The text of the help icon
+     * @param boolean $linktext Whether or not to show text next to the icon
+     * @return void
+     */
+    public function set_help_icon($page, $text, $linktext=false) {
+        if ($page instanceof help_icon) {
+            $this->helpicon = $page;
+        } else if (!empty($page)) {
+            $this->helpicon = new help_icon();
+            $this->helpicon->page = $page;
+            $this->helpicon->text = $text;
+            $this->helpicon->linktext = $linktext;
+        }
+    }
+
+    /**
+     * Parses the $options array and instantiates html_select_option objects in
+     * the place of the original value => label pairs. This is useful for when you
+     * need to setup extra html attributes and actions on individual options before
+     * the component is sent to the renderer
+     * @return void;
+     */
+    public function initialise_options() {
+        // If options are already instantiated objects, stop here
+        $firstoption = reset($this->options);
+        if ($firstoption instanceof html_select_option || $firstoption instanceof html_select_optgroup) {
+            return;
+        }
+
+        if ($this->rendertype == 'radio' && $this->multiple) {
+            $this->rendertype = 'checkbox';
+        }
+
+        // If nested is on, or if radio/checkbox rendertype is set, remove the default Choose option
+        if ($this->nested || $this->rendertype == 'radio' || $this->rendertype == 'checkbox') {
+            $this->nothinglabel = '';
+        }
+
+        $options = $this->options;
+
+        $this->options = array();
+
+        if ($this->nested && $this->rendertype != 'menu') {
+            throw new coding_exception('moodle_select cannot render nested options as radio buttons or checkboxes.');
+        } else if ($this->nested) {
+            foreach ($options as $section => $values) {
+                $optgroup = new html_select_optgroup();
+                $optgroup->text = $section;
+
+                foreach ($values as $value => $display) {
+                    $option = new html_select_option();
+                    $option->value = s($value);
+                    $option->text = $display;
+                    if ($display === '') {
+                        $option->text = $value;
+                    }
+
+                    if ((string) $value == (string) $this->selectedvalue ||
+                            (is_array($this->selectedvalue) && in_array($value, $this->selectedvalue))) {
+                        $option->selected = 'selected';
+                    }
+
+                    $optgroup->options[] = $option;
+                }
+
+                $this->options[] = $optgroup;
+            }
+        } else {
+            $inoptgroup = false;
+            $optgroup = false;
+
+            foreach ($options as $value => $display) {
+                if ($display == '--') { /// we are ending previous optgroup
+                    // $this->options[] = $optgroup;
+                    $inoptgroup = false;
+                    continue;
+                } else if (substr($display,0,2) == '--') { /// we are starting a new optgroup
+                    if (!empty($optgroup->options)) {
+                        $this->options[] = $optgroup;
+                    }
+
+                    $optgroup = new html_select_optgroup();
+                    $optgroup->text = substr($display,2); // stripping the --
+
+                    $inoptgroup = true; /// everything following will be in an optgroup
+                    continue;
+
+                } else {
+                    // Add $nothing option if there are not optgroups
+                    if ($this->nothinglabel && empty($this->options[0]) && !$inoptgroup) {
+                        $nothingoption = new html_select_option();
+                        $nothingoption->value = 0;
+                        if (!empty($this->nothingvalue)) {
+                            $nothingoption->value = $this->nothingvalue;
+                        }
+                        $nothingoption->text = $this->nothinglabel;
+                        $this->options = array($nothingoption) + $this->options;
+                    }
+
+                    $option = new html_select_option();
+                    $option->text = $display;
+
+                    if ($display === '') {
+                        $option->text = $value;
+                    }
+
+                    if ((string) $value == (string) $this->selectedvalue ||
+                            (is_array($this->selectedvalue) && in_array($value, $this->selectedvalue))) {
+                        $option->selected = 'selected';
+                    }
+
+                    $option->value = s($value);
+
+                    if ($inoptgroup) {
+                        $optgroup->options[] = $option;
+                    } else {
+                        $this->options[] = $option;
+                    }
+                }
+            }
+
+            if ($optgroup) {
+                $this->options[] = $optgroup;
+            }
+        }
+    }
+}
+
+/**
+ * This class represents a label element
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class html_label extends moodle_html_component {
+    /**
+     * @var string $text The text to display in the label
+     */
+    public $text;
+    /**
+     * @var string $for The name of the form field this label is associated with
+     */
+    public $for;
+
+    /**
+     * @see moodle_html_component::prepare()
+     * @return void
+     */
+    public function prepare() {
+        if (empty($this->text)) {
+            throw new coding_exception('html_label must have a $text value.');
+        }
+        parent::prepare();
+    }
+}
+
+/**
+ * This class represents a select option element
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class html_select_option extends moodle_html_component {
+    /**
+     * @var string $value The value of this option (will be sent with form)
+     */
+    public $value;
+    /**
+     * @var string $text The display value of the option
+     */
+    public $text;
+    /**
+     * @var boolean $selected Whether or not this option is selected
+     */
+    public $selected = false;
+    /**
+     * @var mixed $label The label for that component. String or html_label object
+     */
+    public $label;
+
+    public function __construct() {
+        $this->label = new html_label();
+    }
+
+    /**
+     * @see moodle_html_component::prepare()
+     * @return void
+     */
+    public function prepare() {
+        if (empty($this->text)) {
+            throw new coding_exception('html_select_option requires a $text value.');
+        }
+
+        if (empty($this->label->text)) {
+            $this->set_label($this->text);
+        } else if (!($this->label instanceof html_label)) {
+            $this->set_label($this->label);
+        }
+        if (empty($this->id)) {
+            $this->generate_id();
+        }
+
+        parent::prepare();
+    }
+
+    /**
+     * Shortcut for making a checkbox-ready option
+     * @param string $value The value of the checkbox
+     * @param boolean $checked
+     * @param string $label
+     * @param string $alt
+     * @return html_select_option A component ready for $OUTPUT->checkbox()
+     */
+    public function make_checkbox($value, $checked, $label='', $alt='') {
+        $checkbox = new html_select_option();
+        $checkbox->value = $value;
+        $checkbox->selected = $checked;
+        $checkbox->text = $label;
+        $checkbox->label->text = $label;
+        $checkbox->alt = $alt;
+        return $checkbox;
+    }
+}
+
+/**
+ * This class represents a select optgroup element
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class html_select_optgroup extends moodle_html_component {
+    /**
+     * @var string $text The display value of the optgroup
+     */
+    public $text;
+    /**
+     * @var array $options An array of html_select_option objects
+     */
+    public $options = array();
+
+    public function prepare() {
+        if (empty($this->text)) {
+            throw new coding_exception('html_select_optgroup requires a $text value.');
+        }
+        if (empty($this->options)) {
+            throw new coding_exception('html_select_optgroup requires at least one html_select_option object');
+        }
+        parent::prepare();
+    }
+}
+
+/**
+ * This class represents an input field
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class html_field extends moodle_html_component {
+    /**
+     * @var string $name The name attribute of the field
+     */
+    public $name;
+    /**
+     * @var string $value The value attribute of the field
+     */
+    public $value;
+    /**
+     * @var string $type The type attribute of the field (text, submit, checkbox etc)
+     */
+    public $type;
+    /**
+     * @var string $maxlength The maxlength attribute of the field (only applies to text type)
+     */
+    public $maxlength;
+    /**
+     * @var mixed $label The label for that component. String or html_label object
+     */
+    public $label;
+
+    public function __construct() {
+        $this->label = new html_label();
+    }
+
+    /**
+     * @see moodle_html_component::prepare()
+     * @return void
+     */
+    public function prepare() {
+        if (empty($this->style)) {
+            $this->style = 'width: 4em;';
+        }
+        if (empty($this->id)) {
+            $this->generate_id();
+        }
+        parent::prepare();
+    }
+
+    /**
+     * Shortcut for creating a text input component.
+     * @param string $name    The name of the text field
+     * @param string $value   The value of the text field
+     * @param string $alt     The info to be inserted in the alt tag
+     * @param int $maxlength Sets the maxlength attribute of the field. Not set by default
+     * @return html_field The field component
+     */
+    public static function make_text($name='unnamed', $value, $alt, $maxlength=0) {
+        $field = new html_field();
+        if (empty($alt)) {
+            $alt = get_string('textfield');
+        }
+        $field->type = 'text';
+        $field->name = $name;
+        $field->value = $value;
+        $field->alt = $alt;
+        $field->maxlength = $maxlength;
+        return $field;
+    }
+}
+
+/**
+ * This class represents how a block appears on a page.
+ *
+ * During output, each block instance is asked to return a block_contents object,
+ * those are then passed to the $OUTPUT->block function for display.
+ *
+ * {@link $contents} should probably be generated using a moodle_block_..._renderer.
+ *
+ * Other block-like things that need to appear on the page, for example the
+ * add new block UI, are also represented as block_contents objects.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class block_contents extends moodle_html_component {
+    /** @var int used to set $skipid. */
+    protected static $idcounter = 1;
+
+    const NOT_HIDEABLE = 0;
+    const VISIBLE = 1;
+    const HIDDEN = 2;
+
+    /**
+     * @param integer $skipid All the blocks (or things that look like blocks)
+     * printed on a page are given a unique number that can be used to construct
+     * id="" attributes. This is set automatically be the {@link prepare()} method.
+     * Do not try to set it manually.
+     */
+    public $skipid;
+
+    /**
+     * @var integer If this is the contents of a real block, this should be set to
+     * the block_instance.id. Otherwise this should be set to 0.
+     */
+    public $blockinstanceid = 0;
+
+    /**
+     * @var integer if this is a real block instance, and there is a corresponding
+     * block_position.id for the block on this page, this should be set to that id.
+     * Otherwise it should be 0.
+     */
+    public $blockpositionid = 0;
+
+    /**
+     * @param array $attributes an array of attribute => value pairs that are put on the
+     * outer div of this block. {@link $id} and {@link $classes} attributes should be set separately.
+     */
+    public $attributes = array();
+
+    /**
+     * @param string $title The title of this block. If this came from user input,
+     * it should already have had format_string() processing done on it. This will
+     * be output inside <h2> tags. Please do not cause invalid XHTML.
+     */
+    public $title = '';
+
+    /**
+     * @param string $content HTML for the content
+     */
+    public $content = '';
+
+    /**
+     * @param array $list an alternative to $content, it you want a list of things with optional icons.
+     */
+    public $footer = '';
+
+    /**
+     * Any small print that should appear under the block to explain to the
+     * teacher about the block, for example 'This is a sticky block that was
+     * added in the system context.'
+     * @var string
+     */
+    public $annotation = '';
+
+    /**
+     * @var integer one of the constants NOT_HIDEABLE, VISIBLE, HIDDEN. Whether
+     * the user can toggle whether this block is visible.
+     */
+    public $collapsible = self::NOT_HIDEABLE;
+
+    /**
+     * A (possibly empty) array of editing controls. Each element of this array
+     * should be an array('url' => $url, 'icon' => $icon, 'caption' => $caption).
+     * $icon is the icon name. Fed to $OUTPUT->old_icon_url.
+     * @var array
+     */
+    public $controls = array();
+
+    /**
+     * @see moodle_html_component::prepare()
+     * @return void
+     */
+    public function prepare() {
+        $this->skipid = self::$idcounter;
+        self::$idcounter += 1;
+        $this->add_class('sideblock');
+        if (empty($this->blockinstanceid) || !strip_tags($this->title)) {
+            $this->collapsible = self::NOT_HIDEABLE;
+        }
+        if ($this->collapsible == self::HIDDEN) {
+            $this->add_class('hidden');
+        }
+        if (!empty($this->controls)) {
+            $this->add_class('block_with_controls');
+        }
+        parent::prepare();
+    }
+}
+
+
+/**
+ * This class represents a target for where a block can go when it is being moved.
+ *
+ * This needs to be rendered as a form with the given hidden from fields, and
+ * clicking anywhere in the form should submit it. The form action should be
+ * $PAGE->url.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class block_move_target extends moodle_html_component {
+    /**
+     * List of hidden form fields.
+     * @var array
+     */
+    public $url = array();
+    /**
+     * List of hidden form fields.
+     * @var array
+     */
+    public $text = '';
+}
+
+
+/**
+ * Holds all the information required to render a <table> by
+ * {@see moodle_core_renderer::table()} or by an overridden version of that
+ * method in a subclass.
+ *
+ * Example of usage:
+ * $t = new html_table();
+ * ... // set various properties of the object $t as described below
+ * echo $OUTPUT->table($t);
+ *
+ * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class html_table extends moodle_html_component {
+    /**
+     * @var array of headings. The n-th array item is used as a heading of the n-th column.
+     *
+     * Example of usage:
+     * $t->head = array('Student', 'Grade');
+     */
+    public $head;
+    /**
+     * @var array can be used to make a heading span multiple columns
+     *
+     * Example of usage:
+     * $t->headspan = array(2,1);
+     *
+     * In this example, {@see html_table:$data} is supposed to have three columns. For the first two columns,
+     * the same heading is used. Therefore, {@see html_table::$head} should consist of two items.
+     */
+    public $headspan;
+    /**
+     * @var array of column alignments. The value is used as CSS 'text-align' property. Therefore, possible
+     * values are 'left', 'right', 'center' and 'justify'. Specify 'right' or 'left' from the perspective
+     * of a left-to-right (LTR) language. For RTL, the values are flipped automatically.
+     *
+     * Examples of usage:
+     * $t->align = array(null, 'right');
+     * or
+     * $t->align[1] = 'right';
+     *
+     */
+    public $align;
+    /**
+     * @var array of column sizes. The value is used as CSS 'size' property.
+     *
+     * Examples of usage:
+     * $t->size = array('50%', '50%');
+     * or
+     * $t->size[1] = '120px';
+     */
+    public $size;
+    /**
+     * @var array of wrapping information. The only possible value is 'nowrap' that sets the
+     * CSS property 'white-space' to the value 'nowrap' in the given column.
+     *
+     * Example of usage:
+     * $t->wrap = array(null, 'nowrap');
+     */
+    public $wrap;
+    /**
+     * @var array of arrays or html_table_row objects containing the data. Alternatively, if you have
+     * $head specified, the string 'hr' (for horizontal ruler) can be used
+     * instead of an array of cells data resulting in a divider rendered.
+     *
+     * Example of usage with array of arrays:
+     * $row1 = array('Harry Potter', '76 %');
+     * $row2 = array('Hermione Granger', '100 %');
+     * $t->data = array($row1, $row2);
+     *
+     * Example with array of html_table_row objects: (used for more fine-grained control)
+     * $cell1 = new html_table_cell();
+     * $cell1->text = 'Harry Potter';
+     * $cell1->colspan = 2;
+     * $row1 = new html_table_row();
+     * $row1->cells[] = $cell1;
+     * $cell2 = new html_table_cell();
+     * $cell2->text = 'Hermione Granger';
+     * $cell3 = new html_table_cell();
+     * $cell3->text = '100 %';
+     * $row2 = new html_table_row();
+     * $row2->cells = array($cell2, $cell3);
+     * $t->data = array($row1, $row2);
+     */
+    public $data;
+    /**
+     * @var string width of the table, percentage of the page preferred. Defaults to 80% of the page width.
+     * @deprecated since Moodle 2.0. Styling should be in the CSS.
+     */
+    public $width = null;
+    /**
+     * @var string alignment the whole table. Can be 'right', 'left' or 'center' (default).
+     * @deprecated since Moodle 2.0. Styling should be in the CSS.
+     */
+    public $tablealign = null;
+    /**
+     * @var int padding on each cell, in pixels
+     * @deprecated since Moodle 2.0. Styling should be in the CSS.
+     */
+    public $cellpadding = null;
+    /**
+     * @var int spacing between cells, in pixels
+     * @deprecated since Moodle 2.0. Styling should be in the CSS.
+     */
+    public $cellspacing = null;
+    /**
+     * @var array classes to add to particular rows, space-separated string.
+     * Classes 'r0' or 'r1' are added automatically for every odd or even row,
+     * respectively. Class 'lastrow' is added automatically for the last row
+     * in the table.
+     *
+     * Example of usage:
+     * $t->rowclasses[9] = 'tenth'
+     */
+    public $rowclasses;
+    /**
+     * @var array classes to add to every cell in a particular column,
+     * space-separated string. Class 'cell' is added automatically by the renderer.
+     * Classes 'c0' or 'c1' are added automatically for every odd or even column,
+     * respectively. Class 'lastcol' is added automatically for all last cells
+     * in a row.
+     *
+     * Example of usage:
+     * $t->colclasses = array(null, 'grade');
+     */
+    public $colclasses;
+    /**
+     * @var string description of the contents for screen readers.
+     */
+    public $summary;
+    /**
+     * @var bool true causes the contents of the heading cells to be rotated 90 degrees.
+     */
+    public $rotateheaders = false;
+
+    /**
+     * @see moodle_html_component::prepare()
+     * @return void
+     */
+    public function prepare() {
+        if (!empty($this->align)) {
+            foreach ($this->align as $key => $aa) {
+                if ($aa) {
+                    $this->align[$key] = 'text-align:'. fix_align_rtl($aa) .';';  // Fix for RTL languages
+                } else {
+                    $this->align[$key] = '';
+                }
+            }
+        }
+        if (!empty($this->size)) {
+            foreach ($this->size as $key => $ss) {
+                if ($ss) {
+                    $this->size[$key] = 'width:'. $ss .';';
+                } else {
+                    $this->size[$key] = '';
+                }
+            }
+        }
+        if (!empty($this->wrap)) {
+            foreach ($this->wrap as $key => $ww) {
+                if ($ww) {
+                    $this->wrap[$key] = 'white-space:nowrap;';
+                } else {
+                    $this->wrap[$key] = '';
+                }
+            }
+        }
+        if (!empty($this->head)) {
+            foreach ($this->head as $key => $val) {
+                if (!isset($this->align[$key])) {
+                    $this->align[$key] = '';
+                }
+                if (!isset($this->size[$key])) {
+                    $this->size[$key] = '';
+                }
+                if (!isset($this->wrap[$key])) {
+                    $this->wrap[$key] = '';
+                }
+
+            }
+        }
+        if (empty($this->classes)) { // must be done before align
+            $this->set_classes(array('generaltable'));
+        }
+        if (!empty($this->tablealign)) {
+            $this->add_class('boxalign' . $this->tablealign);
+        }
+        if (!empty($this->rotateheaders)) {
+            $this->add_class('rotateheaders');
+        } else {
+            $this->rotateheaders = false; // Makes life easier later.
+        }
+        parent::prepare();
+    }
+    /**
+     * @param string $name The name of the variable to set
+     * @param mixed $value The value to assign to the variable
+     * @return void
+     */
+    public function __set($name, $value) {
+        if ($name == 'rowclass') {
+            debugging('rowclass[] has been deprecated for html_table ' .
+                      'and should be replaced with rowclasses[]. please fix the code.');
+            $this->rowclasses = $value;
+        } else {
+            parent::__set($name, $value);
+        }
+    }
+}
+
+/**
+ * Component representing a table row.
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class html_table_row extends moodle_html_component {
+    /**
+     * @var array $cells Array of html_table_cell objects
+     */
+    public $cells = array();
+
+    /**
+     * @see lib/moodle_html_component#prepare()
+     * @return void
+     */
+    public function prepare() {
+        parent::prepare();
+    }
+}
+
+/**
+ * Component representing a table cell.
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class html_table_cell extends moodle_html_component {
+    /**
+     * @var string $text The contents of the cell
+     */
+    public $text;
+    /**
+     * @var string $abbr Abbreviated version of the contents of the cell
+     */
+    public $abbr = '';
+    /**
+     * @var int $colspan Number of columns this cell should span
+     */
+    public $colspan = '';
+    /**
+     * @var int $rowspan Number of rows this cell should span
+     */
+    public $rowspan = '';
+    /**
+     * @var string $scope Defines a way to associate header cells and data cells in a table
+     */
+    public $scope = '';
+
+    /**
+     * @see lib/moodle_html_component#prepare()
+     * @return void
+     */
+    public function prepare() {
+        parent::prepare();
+    }
+}
+
+/**
+ * Component representing a XHTML link.
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class html_link extends moodle_html_component {
+    /**
+     * URL can be simple text or a moodle_url object
+     * @var mixed $url
+     */
+    public $url;
+
+    /**
+     * @var string $text The text that will appear between the link tags
+     */
+    public $text;
+
+    /**
+     * @see lib/moodle_html_component#prepare()
+     * @return void
+     */
+    public function prepare() {
+        // We can't accept an empty text value
+        if (empty($this->text)) {
+            throw new coding_exception('A html_link must have a descriptive text value!');
+        }
+
+        parent::prepare();
+    }
+
+    /**
+     * Shortcut for creating a link component.
+     * @param mixed  $url String or moodle_url
+     * @param string $text The text of the link
+     * @return html_link The link component
+     */
+    public function make($url, $text) {
+        $link = new html_link();
+        $link->url = $url;
+        $link->text = $text;
+        return $link;
+    }
+}
+
+/**
+ * Component representing a help icon.
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class help_icon extends moodle_html_component {
+    /**
+     * @var html_link $link A html_link object that will hold the URL info
+     */
+    public $link;
+    /**
+     * @var string $text A descriptive text
+     */
+    public $text;
+    /**
+     * @var string $page  The keyword that defines a help page
+     */
+    public $page;
+    /**
+     * @var string $module Which module is the page defined in
+     */
+    public $module = 'moodle';
+    /**
+     * @var boolean $linktext Whether or not to show text next to the icon
+     */
+    public $linktext = false;
+    /**
+     * @var mixed $image The help icon. Can be set to true (will use default help icon),
+     *                   false (will not use any icon), the URL to an image, or a full
+     *                   html_image object.
+     */
+    public $image;
+
+    /**
+     * Constructor: sets up the other components in case they are needed
+     * @return void
+     */
+    public function __construct() {
+        $this->link = new html_link();
+        $this->image = new html_image();
+    }
+
+    /**
+     * @see lib/moodle_html_component#prepare()
+     * @return void
+     */
+    public function prepare() {
+        global $COURSE, $OUTPUT;
+
+        if (empty($this->page)) {
+            throw new coding_exception('A help_icon object requires a $page parameter');
+        }
+
+        if (empty($this->text)) {
+            throw new coding_exception('A help_icon object requires a $text parameter');
+        }
+
+        $this->link->text = $this->text;
+
+        // fix for MDL-7734
+        $this->link->url = new moodle_url('/help.php', array('module' => $this->module, 'file' => $this->page .'.html'));
+
+        // fix for MDL-7734
+        if (!empty($COURSE->lang)) {
+            $this->link->url->param('forcelang', $COURSE->lang);
+        }
+
+        // Catch references to the old text.html and emoticons.html help files that
+        // were renamed in MDL-13233.
+        if (in_array($this->page, array('text', 'emoticons', 'richtext'))) {
+            $oldname = $this->page;
+            $this->page .= '2';
+            debugging("You are referring to the old help file '$oldname'. " .
+                    "This was renamed to '$this->page' because of MDL-13233. " .
+                    "Please update your code.", DEBUG_DEVELOPER);
+        }
+
+        if ($this->module == '') {
+            $this->module = 'moodle';
+        }
+
+        // Warn users about new window for Accessibility
+        $this->title = get_string('helpprefix2', '', trim($this->text, ". \t")) .' ('.get_string('newwindow').')';
+
+        // Prepare image and linktext
+        if ($this->image && !($this->image instanceof html_image)) {
+            $image = fullclone($this->image);
+            $this->image = new html_image();
+
+            if ($image instanceof moodle_url) {
+                $this->image->src = $image->out();
+            } else if ($image === true) {
+                $this->image->src = $OUTPUT->old_icon_url('help');
+            } else if (is_string($image)) {
+                $this->image->src = $image;
+            }
+            $this->image->alt = $this->text;
+
+            if ($this->linktext) {
+                $this->image->alt = get_string('helpwiththis');
+            } else {
+                $this->image->alt = $this->title;
+            }
+            $this->image->add_class('iconhelp');
+        } else if (empty($this->image->src)) {
+            if (!($this->image instanceof html_image)) {
+                $this->image = new html_image();
+            }
+            $this->image->src = $OUTPUT->old_icon_url('help');
+        }
+
+        parent::prepare();
+    }
+
+    public static function make_scale_menu($courseid, $scale) {
+        $helpbutton = new help_icon();
+        $strscales = get_string('scales');
+        $helpbutton->image->alt = $scale->name;
+        $helpbutton->link->url = new moodle_url('/course/scales.php', array('id' => $courseid, 'list' => true, 'scaleid' => $scale->id));
+        $popupaction = new popup_action('click', $helpbutton->url, 'ratingscale', $popupparams);
+        $popupaction->width = 500;
+        $popupaction->height = 400;
+        $helpbutton->link->add_action($popupaction);
+        $helpbutton->link->title = $scale->name;
+        return $helpbutton;
+    }
+}
+
+
+/**
+ * Component representing a XHTML button (input of type 'button').
+ * The renderer will either output it as a button with an onclick event,
+ * or as a form with hidden inputs.
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class html_button extends moodle_html_component {
+    /**
+     * @var string $text
+     */
+    public $text;
+
+    /**
+     * @var boolean $disabled Whether or not this button is disabled
+     */
+    public $disabled = false;
+
+    /**
+     * @see lib/moodle_html_component#prepare()
+     * @return void
+     */
+    public function prepare() {
+        $this->add_class('singlebutton');
+
+        if (empty($this->text)) {
+            throw new coding_exception('A html_button must have a text value!');
+        }
+
+        if ($this->disabled) {
+            $this->disabled = 'disabled';
+        }
+
+        parent::prepare();
+    }
+}
+
+/**
+ * Component representing an icon linking to a Moodle page.
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class action_icon extends moodle_html_component {
+    /**
+     * @var string $linktext Optional text to display next to the icon
+     */
+    public $linktext;
+    /**
+     * @var html_image $image The icon
+     */
+    public $image;
+    /**
+     * @var html_link $link The link
+     */
+    public $link;
+
+    /**
+     * Constructor: sets up the other components in case they are needed
+     * @return void
+     */
+    public function __construct() {
+        $this->image = new html_image();
+        $this->link = new html_link();
+    }
+
+    /**
+     * @see lib/moodle_html_component#prepare()
+     * @return void
+     */
+    public function prepare() {
+        $this->image->add_class('action-icon');
+
+        parent::prepare();
+
+        if (empty($this->image->src)) {
+            throw new coding_exception('action_icon->image->src must not be empty');
+        }
+
+        if (empty($this->image->alt) && !empty($this->linktext)) {
+            $this->image->alt = $this->linktext;
+        } else if (empty($this->image->alt)) {
+            debugging('action_icon->image->alt should not be empty.', DEBUG_DEVELOPER);
+        }
+    }
+}
+
+/**
+ * Component representing an image.
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class html_image extends moodle_html_component {
+    /**
+     * @var string $alt A descriptive text
+     */
+    public $alt = HTML_ATTR_EMPTY;
+    /**
+     * @var string $src The path to the image being used
+     */
+    public $src;
+
+    /**
+     * @see lib/moodle_html_component#prepare()
+     * @return void
+     */
+    public function prepare() {
+        $this->add_class('image');
+        parent::prepare();
+    }
+}
+
+/**
+ * Component representing a user picture.
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class user_picture extends moodle_html_component {
+    /**
+     * @var mixed $user A userid or a user object with at least fields id, picture, imagealrt, firstname and lastname set.
+     */
+    public $user;
+    /**
+     * @var int $courseid The course id. Used when constructing the link to the user's profile.
+     */
+    public $courseid;
+    /**
+     * @var html_image $image A custom image used as the user picture.
+     */
+    public $image;
+    /**
+     * @var mixed $url False: picture not enclosed in a link. True: default link. moodle_url: custom link.
+     */
+    public $url;
+    /**
+     * @var int $size Size in pixels. Special values are (true/1 = 100px) and (false/0 = 35px) for backward compatibility
+     */
+    public $size;
+    /**
+     * @var boolean $alttext add non-blank alt-text to the image. (Default true, set to false for purely
+     */
+    public $alttext = true;
+    /**
+     * @var boolean $popup Whether or not to open the link in a popup window
+     */
+    public $popup = false;
+
+    /**
+     * Constructor: sets up the other components in case they are needed
+     * @return void
+     */
+    public function __construct() {
+        $this->image = new html_image();
+    }
+
+    /**
+     * @see lib/moodle_html_component#prepare()
+     * @return void
+     */
+    public function prepare() {
+        global $CFG, $DB, $OUTPUT;
+
+        if (empty($this->user)) {
+            throw new coding_exception('A user_picture object must have a $user object before being rendered.');
+        }
+
+        if (empty($this->courseid)) {
+            throw new coding_exception('A user_picture object must have a courseid value before being rendered.');
+        }
+
+        if (!($this->image instanceof html_image)) {
+            debugging('user_picture::image must be an instance of html_image', DEBUG_DEVELOPER);
+        }
+
+        $needrec = false;
+        // only touch the DB if we are missing data...
+        if (is_object($this->user)) {
+            // Note - both picture and imagealt _can_ be empty
+            // what we are trying to see here is if they have been fetched
+            // from the DB. We should use isset() _except_ that some installs
+            // have those fields as nullable, and isset() will return false
+            // on null. The only safe thing is to ask array_key_exists()
+            // which works on objects. property_exists() isn't quite
+            // what we want here...
+            if (! (array_key_exists('picture', $this->user)
+                   && ($this->alttext && array_key_exists('imagealt', $this->user)
+                       || (isset($this->user->firstname) && isset($this->user->lastname)))) ) {
+                $needrec = true;
+                $this->user = $this->user->id;
+            }
+        } else {
+            if ($this->alttext) {
+                // we need firstname, lastname, imagealt, can't escape...
+                $needrec = true;
+            } else {
+                $userobj = new StdClass; // fake it to save DB traffic
+                $userobj->id = $this->user;
+                $userobj->picture = $this->image->src;
+                $this->user = clone($userobj);
+                unset($userobj);
+            }
+        }
+        if ($needrec) {
+            $this->user = $DB->get_record('user', array('id' => $this->user), 'id,firstname,lastname,imagealt');
+        }
+
+        if ($this->url === true) {
+            $this->url = new moodle_url('/user/view.php', array('id' => $this->user->id, 'course' => $this->courseid));
+        }
+
+        if (!empty($this->url) && $this->popup) {
+            $this->add_action(new popup_action('click', $this->url));
+        }
+
+        if (empty($this->size)) {
+            $file = 'f2';
+            $this->size = 35;
+        } else if ($this->size === true or $this->size == 1) {
+            $file = 'f1';
+            $this->size = 100;
+        } else if ($this->size >= 50) {
+            $file = 'f1';
+        } else {
+            $file = 'f2';
+        }
+
+        if (!empty($this->size)) {
+            $this->image->width = $this->size;
+            $this->image->height = $this->size;
+        }
+
+        $this->add_class('userpicture');
+
+        if (empty($this->image->src) && !empty($this->user->picture)) {
+            $this->image->src = $this->user->picture;
+        }
+
+        if (!empty($this->image->src)) {
+            require_once($CFG->libdir.'/filelib.php');
+            $this->image->src = new moodle_url(get_file_url($this->user->id.'/'.$file.'.jpg', null, 'user'));
+        } else { // Print default user pictures (use theme version if available)
+            $this->add_class('defaultuserpic');
+            $this->image->src = $OUTPUT->old_icon_url('u/' . $file);
+        }
+
+        if ($this->alttext) {
+            if (!empty($this->user->imagealt)) {
+                $this->image->alt = $this->user->imagealt;
+            } else {
+                $this->image->alt = get_string('pictureof','',fullname($this->user));
+            }
+        }
+
+        parent::prepare();
+    }
+}
+
+/**
+ * Component representing a textarea.
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class html_textarea extends moodle_html_component {
+    /**
+     * @param string $name Name to use for the textarea element.
+     */
+    public $name;
+    /**
+     * @param string $value Initial content to display in the textarea.
+     */
+    public $value;
+    /**
+     * @param int $rows Number of rows to display  (minimum of 10 when $height is non-null)
+     */
+    public $rows;
+    /**
+     * @param int $cols Number of columns to display (minimum of 65 when $width is non-null)
+     */
+    public $cols;
+    /**
+     * @param bool $usehtmleditor Enables the use of the htmleditor for this field.
+     */
+    public $usehtmleditor;
+
+    /**
+     * @see lib/moodle_html_component#prepare()
+     * @return void
+     */
+    public function prepare() {
+        $this->add_class('form-textarea');
+
+        if (empty($this->id)) {
+            $this->id = "edit-$this->name";
+        }
+
+        if ($this->usehtmleditor) {
+            editors_head_setup();
+            $editor = get_preferred_texteditor(FORMAT_HTML);
+            $editor->use_editor($this->id, array('legacy'=>true));
+            $this->value = htmlspecialchars($value);
+        }
+
+        parent::prepare();
+    }
+}
+
+/**
+ * Component representing a simple form wrapper. Its purpose is mainly to enclose
+ * a submit input with the appropriate action and hidden inputs.
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class html_form extends moodle_html_component {
+    /**
+     * @var string $method post or get
+     */
+    public $method = 'post';
+    /**
+     * If a string is given, it will be converted to a moodle_url during prepare()
+     * @var mixed $url A moodle_url including params or a string
+     */
+    public $url;
+    /**
+     * @var array $params Optional array of parameters. Ignored if $url instanceof moodle_url
+     */
+    public $params = array();
+    /**
+     * @var boolean $showbutton If true, the submit button will always be shown even if JavaScript is available
+     */
+    public $showbutton = false;
+    /**
+     * @var string $targetwindow The name of the target page to open the linked page in.
+     */
+    public $targetwindow = 'self';
+    /**
+     * @var html_button $button A submit button
+     */
+    public $button;
+
+    /**
+     * Constructor: sets up the other components in case they are needed
+     * @return void
+     */
+    public function __construct() {
+        static $yes;
+        $this->button = new html_button();
+        if (!isset($yes)) {
+            $yes = get_string('yes');
+            $this->button->text = $yes;
+        }
+    }
+
+    /**
+     * @see lib/moodle_html_component#prepare()
+     * @return void
+     */
+    public function prepare() {
+
+        if (empty($this->url)) {
+            throw new coding_exception('A html_form must have a $url value (string or moodle_url).');
+        }
+
+        if (!($this->url instanceof moodle_url)) {
+            $this->url = new moodle_url($this->url, $this->params);
+        }
+
+        if ($this->method == 'post') {
+            $this->url->param('sesskey', sesskey());
+        }
+
+        parent::prepare();
+    }
+}
+
+/**
+ * Component representing a paging bar.
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class moodle_paging_bar extends moodle_html_component {
+    /**
+     * @var int $maxdisplay The maximum number of pagelinks to display
+     */
+    public $maxdisplay = 18;
+    /**
+     * @var int $totalcount post or get
+     */
+    public $totalcount;
+    /**
+     * @var int $page The page you are currently viewing
+     */
+    public $page = 0;
+    /**
+     * @var int $perpage The number of entries that should be shown per page
+     */
+    public $perpage;
+    /**
+     * @var string $baseurl If this  is a string then it is the url which will be appended with $pagevar, an equals sign and the page number.
+     *      If this is a moodle_url object then the pagevar param will be replaced by the page no, for each page.
+     */
+    public $baseurl;
+    /**
+     * @var string $pagevar This is the variable name that you use for the page number in your code (ie. 'tablepage', 'blogpage', etc)
+     */
+    public $pagevar = 'page';
+    /**
+     * @var bool $nocurr do not display the current page as a link
+     */
+    public $nocurr;
+    /**
+     * @var html_link $previouslink A HTML link representing the "previous" page
+     */
+    public $previouslink = null;
+    /**
+     * @var html_link $nextlink A HTML link representing the "next" page
+     */
+    public $nextlink = null;
+    /**
+     * @var html_link $firstlink A HTML link representing the first page
+     */
+    public $firstlink = null;
+    /**
+     * @var html_link $lastlink A HTML link representing the last page
+     */
+    public $lastlink = null;
+    /**
+     * @var array $pagelinks An array of html_links. One of them is just a string: the current page
+     */
+    public $pagelinks = array();
+
+    /**
+     * @see lib/moodle_html_component#prepare()
+     * @return void
+     */
+    public function prepare() {
+        if (empty($this->totalcount)) {
+            throw new coding_exception('moodle_paging_bar requires a totalcount value.');
+        }
+        if (!isset($this->page) || is_null($this->page)) {
+            throw new coding_exception('moodle_paging_bar requires a page value.');
+        }
+        if (empty($this->perpage)) {
+            throw new coding_exception('moodle_paging_bar requires a perpage value.');
+        }
+        if (empty($this->baseurl)) {
+            throw new coding_exception('moodle_paging_bar requires a baseurl value.');
+        }
+        if (!($this->baseurl instanceof moodle_url)) {
+            $this->baseurl = new moodle_url($this->baseurl);
+        }
+
+        if ($this->totalcount > $this->perpage) {
+            $pagenum = $this->page - 1;
+
+            if ($this->page > 0) {
+                $this->previouslink = new html_link();
+                $this->previouslink->add_class('previous');
+                $this->previouslink->url = clone($this->baseurl);
+                $this->previouslink->url->param($this->pagevar, $pagenum);
+                $this->previouslink->text = get_string('previous');
+            }
+
+            if ($this->perpage > 0) {
+                $lastpage = ceil($this->totalcount / $this->perpage);
+            } else {
+                $lastpage = 1;
+            }
+
+            if ($this->page > 15) {
+                $startpage = $this->page - 10;
+
+                $this->firstlink = new html_link();
+                $this->firstlink->url = clone($this->baseurl);
+                $this->firstlink->url->param($this->pagevar, 0);
+                $this->firstlink->text = 1;
+                $this->firstlink->add_class('first');
+            } else {
+                $startpage = 0;
+            }
+
+            $currpage = $startpage;
+            $displaycount = $displaypage = 0;
+
+            while ($displaycount < $this->maxdisplay and $currpage < $lastpage) {
+                $displaypage = $currpage + 1;
+
+                if ($this->page == $currpage && empty($this->nocurr)) {
+                    $this->pagelinks[] = $displaypage;
+                } else {
+                    $pagelink = new html_link();
+                    $pagelink->url = clone($this->baseurl);
+                    $pagelink->url->param($this->pagevar, $currpage);
+                    $pagelink->text = $displaypage;
+                    $this->pagelinks[] = $pagelink;
+                }
+
+                $displaycount++;
+                $currpage++;
+            }
+
+            if ($currpage < $lastpage) {
+                $lastpageactual = $lastpage - 1;
+                $this->lastlink = new html_link();
+                $this->lastlink->url = clone($this->baseurl);
+                $this->lastlink->url->param($this->pagevar, $lastpageactual);
+                $this->lastlink->text = $lastpage;
+                $this->lastlink->add_class('last');
+            }
+
+            $pagenum = $this->page + 1;
+
+            if ($pagenum != $displaypage) {
+                $this->nextlink = new html_link();
+                $this->nextlink->url = clone($this->baseurl);
+                $this->nextlink->url->param($this->pagevar, $pagenum);
+                $this->nextlink->text = get_string('next');
+                $this->nextlink->add_class('next');
+            }
+        }
+    }
+
+    /**
+     * Shortcut for initialising a moodle_paging_bar with only the required params.
+     *
+     * @param int $totalcount Thetotal number of entries available to be paged through
+     * @param int $page The page you are currently viewing
+     * @param int $perpage The number of entries that should be shown per page
+     * @param mixed $baseurl If this  is a string then it is the url which will be appended with $pagevar, an equals sign and the page number.
+     *                          If this is a moodle_url object then the pagevar param will be replaced by the page no, for each page.
+     */
+    public function make($totalcount, $page, $perpage, $baseurl) {
+        $pagingbar = new moodle_paging_bar();
+        $pagingbar->totalcount = $totalcount;
+        $pagingbar->page = $page;
+        $pagingbar->perpage = $perpage;
+        $pagingbar->baseurl = $baseurl;
+        return $pagingbar;
+    }
+}
+
+/**
+ * Component representing a list.
+ *
+ * The advantage of using this object instead of a flat array is that you can load it
+ * with metadata (CSS classes, event handlers etc.) which can be used by the renderers.
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class html_list extends moodle_html_component {
+
+    /**
+     * @var array $items An array of html_list_item or html_list objects
+     */
+    public $items = array();
+
+    /**
+     * @var string $type The type of list (ordered|unordered), definition type not yet supported
+     */
+    public $type = 'unordered';
+
+    /**
+     * @see lib/moodle_html_component#prepare()
+     * @return void
+     */
+    public function prepare() {
+        parent::prepare();
+    }
+
+    /**
+     * This function takes a nested array of data and maps it into this list's $items array
+     * as proper html_list_item and html_list objects, with appropriate metadata.
+     *
+     * @param array $tree A nested array (array keys are ignored);
+     * @param int $row Used in identifying the iteration level and in ul classes
+     * @return void
+     */
+    public function load_data($tree, $level=0) {
+
+        $this->add_class("list-$level");
+
+        foreach ($tree as $key => $element) {
+            if (is_array($element)) {
+                $newhtmllist = new html_list();
+                $newhtmllist->load_data($element, $level + 1);
+                $this->items[] = $newhtmllist;
+            } else {
+                $listitem = new html_list_item();
+                $listitem->value = $element;
+                $listitem->add_class("list-item-$level-$key");
+                $this->items[] = $listitem;
+            }
+        }
+    }
+
+    /**
+     * Adds a html_list_item or html_list to this list.
+     * If the param is a string, a html_list_item will be added.
+     * @param mixed $item String, html_list or html_list_item object
+     * @return void
+     */
+    public function add_item($item) {
+        if ($item instanceof html_list_item || $item instanceof html_list) {
+            $this->items[] = $item;
+        } else {
+            $listitem = new html_list_item();
+            $listitem->value = $item;
+            $this->items[] = $item;
+        }
+    }
+}
+
+/**
+ * Component representing a list item.
+ *
+ * @copyright 2009 Nicolas Connault
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class html_list_item extends moodle_html_component {
+    /**
+     * @var string $value The value of the list item
+     */
+    public $value;
+
+    /**
+     * @see lib/moodle_html_component#prepare()
+     * @return void
+     */
+    public function prepare() {
+        parent::prepare();
+    }
+}
diff --git a/lib/outputfactories.php b/lib/outputfactories.php
new file mode 100644 (file)
index 0000000..66540cf
--- /dev/null
@@ -0,0 +1,332 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Interface and classes for creating appropriate renderers for various
+ * parts of Moodle.
+ *
+ * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML
+ * for an overview.
+ *
+ * @package   moodlecore
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * A renderer factory is just responsible for creating an appropriate renderer
+ * for any given part of Moodle.
+ *
+ * Which renderer factory to use is chose by the current theme, and an instance
+ * if created automatically when the theme is set up.
+ *
+ * A renderer factory must also have a constructor that takes a theme_config object.
+ * (See {@link renderer_factory_base::__construct} for an example.)
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+interface renderer_factory {
+    /**
+     * Return the renderer for a particular part of Moodle.
+     *
+     * The renderer interfaces are defined by classes called moodle_{plugin}_renderer
+     * where {plugin} is the name of the component. The renderers for core Moodle are
+     * defined in lib/renderer.php. For plugins, they will be defined in a file
+     * called renderer.php inside the plugin.
+     *
+     * Renderers will normally want to subclass the moodle_renderer_base class.
+     * (However, if you really know what you are doing, you don't have to do that.)
+     *
+     * There is no separate interface definition for renderers. The default
+     * moodle_{plugin}_renderer implementation also serves to define the API for
+     * other implementations of the interface, whether or not they subclass it.
+     * For example, {@link custom_corners_core_renderer} does subclass
+     * {@link moodle_core_renderer}. On the other hand, if you are using
+     * {@link template_renderer_factory} then you always get back an instance
+     * of the {@link template_renderer} class, whatever type of renderer you ask
+     * for. This uses the fact that PHP is a dynamic language.
+     *
+     * A particular plugin can define multiple renderers if it wishes, using the
+     * $subtype parameter. For example moodle_mod_workshop_renderer,
+     * moodle_mod_workshop_allocation_manual_renderer etc.
+     *
+     * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
+     * @param moodle_page $page the page the renderer is outputting content for.
+     * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
+     * @return object an object implementing the requested renderer interface.
+     */
+    public function get_renderer($component, $page, $subtype=null);
+}
+
+/**
+ * This is a base class to help you implement the renderer_factory interface.
+ *
+ * It keeps a cache of renderers that have been constructed, so you only need
+ * to construct each one once in you subclass.
+ *
+ * It also has a method to get the name of, and include the renderer.php with
+ * the definition of, the standard renderer class for a given module.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+abstract class renderer_factory_base implements renderer_factory {
+    /** @var theme_config the theme we belong to. */
+    protected $theme;
+
+    /**
+     * Constructor.
+     * @param theme_config $theme the theme we belong to.
+     */
+    public function __construct($theme) {
+        $this->theme = $theme;
+    }
+    /**
+     * For a given module name, return the name of the standard renderer class
+     * that defines the renderer interface for that module.
+     *
+     * Also, if it exists, include the renderer.php file for that module, so
+     * the class definition of the default renderer has been loaded.
+     *
+     * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
+     * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
+     * @return string the name of the standard renderer class for that module.
+     */
+    protected function standard_renderer_class_for_module($component, $subtype=null) {
+        if ($component != 'core') {
+            $pluginrenderer = get_component_directory($component) . '/renderer.php';
+            if (file_exists($pluginrenderer)) {
+                include_once($pluginrenderer);
+            }
+        }
+        if (is_null($subtype)) {
+            $class = 'moodle_' . $component . '_renderer';
+        } else {
+            $class = 'moodle_' . $component . '_' . $subtype . '_renderer';
+        }
+        if (!class_exists($class)) {
+            throw new coding_exception('Request for an unknown renderer class ' . $class);
+        }
+        return $class;
+    }
+}
+
+
+/**
+ * This is the default renderer factory for Moodle. It simply returns an instance
+ * of the appropriate standard renderer class.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class standard_renderer_factory extends renderer_factory_base {
+    /**
+     * Implement the subclass method
+     * @param string $module name such as 'core', 'mod_forum' or 'qtype_multichoice'.
+     * @param moodle_page $page the page the renderer is outputting content for.
+     * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
+     * @return object an object implementing the requested renderer interface.
+     */
+    public function get_renderer($module, $page, $subtype=null) {
+        if ($module == 'core') {
+            return new moodle_core_renderer($page);
+        } else {
+            $class = $this->standard_renderer_class_for_module($module, $subtype);
+            return new $class($page, $this->get_renderer('core', $page));
+        }
+    }
+}
+
+
+/**
+ * This is a slight variation on the standard_renderer_factory used by CLI scripts.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class cli_renderer_factory extends standard_renderer_factory {
+    /**
+     * Implement the subclass method
+     * @param string $module name such as 'core', 'mod_forum' or 'qtype_multichoice'.
+     * @param moodle_page $page the page the renderer is outputting content for.
+     * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
+     * @return object an object implementing the requested renderer interface.
+     */
+    public function get_renderer($module, $page, $subtype=null) {
+        if ($module == 'core') {
+            return new cli_core_renderer($page);
+        } else {
+            parent::get_renderer($module, $page, $subtype);
+        }
+    }
+}
+
+
+/**
+ * This is renderer factory allows themes to override the standard renderers using
+ * php code.
+ *
+ * It will load any code from theme/mytheme/renderers.php and
+ * theme/parenttheme/renderers.php, if then exist. Then whenever you ask for
+ * a renderer for 'component', it will create a mytheme_component_renderer or a
+ * parenttheme_component_renderer, instead of a moodle_component_renderer,
+ * if either of those classes exist.
+ *
+ * This generates the slightly different HTML that the custom_corners theme expects.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class theme_overridden_renderer_factory extends standard_renderer_factory {
+    protected $prefixes = array();
+
+    /**
+     * Constructor.
+     * @param object $theme the theme we are rendering for.
+     */
+    public function __construct($theme) {
+        global $CFG;
+        parent::__construct($theme);
+
+        // Initialise $this->prefixes.
+        $renderersfile = $theme->dir . '/renderers.php';
+        if (is_readable($renderersfile)) {
+            include_once($renderersfile);
+            $this->prefixes[] = $theme->name . '_';
+        }
+        if (!empty($theme->parent)) {
+            $renderersfile = $CFG->themedir .'/'. $theme->parent . '/renderers.php';
+            if (is_readable($renderersfile)) {
+                include_once($renderersfile);
+                $this->prefixes[] = $theme->parent . '_';
+            }
+        }
+    }
+
+    /**
+     * Implement the subclass method
+     * @param string $module name such as 'core', 'mod_forum' or 'qtype_multichoice'.
+     * @param moodle_page $page the page the renderer is outputting content for.
+     * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
+     * @return object an object implementing the requested renderer interface.
+     */
+    public function get_renderer($module, $page, $subtype=null) {
+        foreach ($this->prefixes as $prefix) {
+            if (is_null($subtype)) {
+                $classname = $prefix . $module . '_renderer';
+            } else {
+                $classname = $prefix . $module . '_' . $subtype . '_renderer';
+            }
+            if (class_exists($classname)) {
+                if ($module == 'core') {
+                    return new $classname($page);
+                } else {
+                    return new $classname($page, $this->get_renderer('core', $page));
+                }
+            }
+        }
+        return parent::get_renderer($module, $page, $subtype);
+    }
+}
+
+
+/**
+ * This is renderer factory that allows you to create templated themes.
+ *
+ * This should be considered an experimental proof of concept. In particular,
+ * the performance is probably not very good. Do not try to use in on a busy site
+ * without doing careful load testing first!
+ *
+ * This renderer factory returns instances of {@link template_renderer} class
+ * which which implement the corresponding renderer interface in terms of
+ * templates. To use this your theme must have a templates folder inside it.
+ * Then suppose the method moodle_core_renderer::greeting($name = 'world');
+ * exists. Then, a call to $OUTPUT->greeting() will cause the template
+ * /theme/yourtheme/templates/core/greeting.php to be rendered, with the variable
+ * $name available. The greeting.php template might contain
+ *
+ * <pre>
+ * <h1>Hello <?php echo $name ?>!</h1>
+ * </pre>
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class template_renderer_factory extends renderer_factory_base {
+    /**
+     * An array of paths of where to search for templates. Normally this theme,
+     * the parent theme then the standardtemplate theme. (If some of these do
+     * not exist, or are the same as each other, then the list will be shorter.
+     */
+    protected $searchpaths = array();
+
+    /**
+     * Constructor.
+     * @param object $theme the theme we are rendering for.
+     */
+    public function __construct($theme) {
+        global $CFG;
+        parent::__construct($theme);
+
+        // Initialise $this->searchpaths.
+        if ($theme->name != 'standardtemplate') {
+            $templatesdir = $theme->dir . '/templates';
+            if (is_dir($templatesdir)) {
+                $this->searchpaths[] = $templatesdir;
+            }
+        }
+        if (!empty($theme->parent)) {
+            $templatesdir = $CFG->themedir .'/'. $theme->parent . '/templates';
+            if (is_dir($templatesdir)) {
+                $this->searchpaths[] = $templatesdir;
+            }
+        }
+        $this->searchpaths[] = $CFG->themedir .'/standardtemplate/templates';
+    }
+
+    /**
+     * Implement the subclass method
+     * @param string $module name such as 'core', 'mod_forum' or 'qtype_multichoice'.
+     * @param moodle_page $page the page the renderer is outputting content for.
+     * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
+     * @return object an object implementing the requested renderer interface.
+     */
+    public function get_renderer($module, $page, $subtype=null) {
+        // Refine the list of search paths for this module.
+        $searchpaths = array();
+        foreach ($this->searchpaths as $rootpath) {
+            $path = $rootpath . '/' . $module;
+            if (!is_null($subtype)) {
+                $path .= '/' . $subtype;
+            }
+            if (is_dir($path)) {
+                $searchpaths[] = $path;
+            }
+        }
+
+        // Create a template_renderer that copies the API of the standard renderer.
+        $copiedclass = $this->standard_renderer_class_for_module($module, $subtype);
+        return new template_renderer($copiedclass, $searchpaths, $page);
+    }
+}
index ee939bb2e3e6b1883aeb73eef5f0f05b5431c338..aa4eb64ea75274e7ea4bdf030d65615318af7ec5 100644 (file)
  */
 define('HTML_ATTR_EMPTY', '@EMPTY@');
 
+require_once($CFG->libdir.'/outputcomponents.php');
+require_once($CFG->libdir.'/outputactions.php');
+require_once($CFG->libdir.'/outputfactories.php');
+require_once($CFG->libdir.'/outputpixfinders.php');
+require_once($CFG->libdir.'/outputrenderers.php');
+
 /**
  * Functions for generating the HTML that Moodle should output.
  *
@@ -34,93 +40,6 @@ define('HTML_ATTR_EMPTY', '@EMPTY@');
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-/**
- * A renderer factory is just responsible for creating an appropriate renderer
- * for any given part of Moodle.
- *
- * Which renderer factory to use is chose by the current theme, and an instance
- * if created automatically when the theme is set up.
- *
- * A renderer factory must also have a constructor that takes a theme_config object.
- * (See {@link renderer_factory_base::__construct} for an example.)
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-interface renderer_factory {
-    /**
-     * Return the renderer for a particular part of Moodle.
-     *
-     * The renderer interfaces are defined by classes called moodle_{plugin}_renderer
-     * where {plugin} is the name of the component. The renderers for core Moodle are
-     * defined in lib/renderer.php. For plugins, they will be defined in a file
-     * called renderer.php inside the plugin.
-     *
-     * Renderers will normally want to subclass the moodle_renderer_base class.
-     * (However, if you really know what you are doing, you don't have to do that.)
-     *
-     * There is no separate interface definition for renderers. The default
-     * moodle_{plugin}_renderer implementation also serves to define the API for
-     * other implementations of the interface, whether or not they subclass it.
-     * For example, {@link custom_corners_core_renderer} does subclass
-     * {@link moodle_core_renderer}. On the other hand, if you are using
-     * {@link template_renderer_factory} then you always get back an instance
-     * of the {@link template_renderer} class, whatever type of renderer you ask
-     * for. This uses the fact that PHP is a dynamic language.
-     *
-     * A particular plugin can define multiple renderers if it wishes, using the
-     * $subtype parameter. For example moodle_mod_workshop_renderer,
-     * moodle_mod_workshop_allocation_manual_renderer etc.
-     *
-     * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
-     * @param moodle_page $page the page the renderer is outputting content for.
-     * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
-     * @return object an object implementing the requested renderer interface.
-     */
-    public function get_renderer($component, $page, $subtype=null);
-}
-
-
-/**
- * An icon finder is responsible for working out the correct URL for an icon.
- *
- * A icon finder must also have a constructor that takes a theme object.
- * (See {@link standard_icon_finder::__construct} for an example.)
- *
- * Note that we are planning to change the Moodle icon naming convention before
- * the Moodle 2.0 release. Therefore, this API will probably change.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-interface icon_finder {
-    /**
-     * Return the URL for an icon identified as in pre-Moodle 2.0 code.
-     *
-     * Suppose you have old code like $url = "$CFG->pixpath/i/course.gif";
-     * then old_icon_url('i/course'); will return the equivalent URL that is correct now.
-     *
-     * @param string $iconname the name of the icon.
-     * @return string the URL for that icon.
-     */
-    public function old_icon_url($iconname);
-
-    /**
-     * Return the URL for an icon identified as in pre-Moodle 2.0 code.
-     *
-     * Suppose you have old code like $url = "$CFG->modpixpath/$mod/icon.gif";
-     * then mod_icon_url('icon', $mod); will return the equivalent URL that is correct now.
-     *
-     * @param string $iconname the name of the icon.
-     * @param string $module the module the icon belongs to.
-     * @return string the URL for that icon.
-     */
-    public function mod_icon_url($iconname, $module);
-}
-
-
 /**
  * This class represents the configuration variables of a Moodle theme.
  *
@@ -822,5014 +741,151 @@ class theme_config {
     }
 }
 
-
 /**
- * This icon finder implements the old scheme that was used when themes that had
- * $THEME->custompix = false.
+ * This class keeps track of which HTML tags are currently open.
+ *
+ * This makes it much easier to always generate well formed XHTML output, even
+ * if execution terminates abruptly. Any time you output some opening HTML
+ * without the matching closing HTML, you should push the necessary close tags
+ * onto the stack.
  *
  * @copyright 2009 Tim Hunt
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  * @since     Moodle 2.0
  */
-class pix_icon_finder implements icon_finder {
-    /**
-     * Constructor
-     * @param theme_config $theme the theme we are finding icons for (which is irrelevant).
-     */
-    public function __construct($theme) {
-    }
-
+class xhtml_container_stack {
+    /** @var array stores the list of open containers. */
+    protected $opencontainers = array();
     /**
-     * Implement interface method.
-     * @param string $iconname the name of the icon.
-     * @return string the URL for that icon.
+     * @var array in developer debug mode, stores a stack trace of all opens and
+     * closes, so we can output helpful error messages when there is a mismatch.
      */
-    public function old_icon_url($iconname) {
-        global $CFG;
-        if (file_exists($CFG->dirroot . '/pix/' . $iconname . '.png')) {
-            return $CFG->httpswwwroot . '/pix/' . $iconname . '.png';
-        } else {
-            return $CFG->httpswwwroot . '/pix/' . $iconname . '.gif';
-        }
-    }
-
+    protected $log = array();
     /**
-     * Implement interface method.
-     * @param string $iconname the name of the icon.
-     * @param string $module the module the icon belongs to.
-     * @return string the URL for that icon.
+     * Store whether we are developer debug mode. We need this in several places
+     * including in the destructor where we may no thave access to $CFG.
+     * @var boolean
      */
-    public function mod_icon_url($iconname, $module) {
-        global $CFG;
-        if (file_exists($CFG->dirroot . '/mod/' . $module . '/' . $iconname . '.png')) {
-            return $CFG->httpswwwroot . '/mod/' . $module . '/' . $iconname . '.png';
-        } else {
-            return $CFG->httpswwwroot . '/mod/' . $module . '/' . $iconname . '.gif';
-        }
-    }
-}
-
+    protected $isdebugging;
 
-/**
- * This icon finder implements the old scheme that was used for themes that had
- * $THEME->custompix = true.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class theme_icon_finder implements icon_finder {
-    protected $themename;
-    /**
-     * Constructor
-     * @param theme_config $theme the theme we are finding icons for.
-     */
-    public function __construct($theme) {
-        $this->themename = $theme->name;
+    public function __construct() {
+        $this->isdebugging = debugging('', DEBUG_DEVELOPER);
     }
 
     /**
-     * Implement interface method.
-     * @param string $iconname the name of the icon.
-     * @return string the URL for that icon.
+     * Push the close HTML for a recently opened container onto the stack.
+     * @param string $type The type of container. This is checked when {@link pop()}
+     *      is called and must match, otherwise a developer debug warning is output.
+     * @param string $closehtml The HTML required to close the container.
+     * @return void
      */
-    public function old_icon_url($iconname) {
-        global $CFG;
-        if (file_exists($CFG->themedir . '/' . $this->themename . '/pix/' . $iconname . '.png')) {
-            return $CFG->httpsthemewww . '/' . $this->themename . '/pix/' . $iconname . '.png';
-        } else {
-            return $CFG->httpsthemewww . '/' . $this->themename . '/pix/' . $iconname . '.gif';
+    public function push($type, $closehtml) {
+        $container = new stdClass;
+        $container->type = $type;
+        $container->closehtml = $closehtml;
+        if ($this->isdebugging) {
+            $this->log('Open', $type);
         }
+        array_push($this->opencontainers, $container);
     }
 
     /**
-     * Implement interface method.
-     * @param string $iconname the name of the icon.
-     * @param string $module the module the icon belongs to.
-     * @return string the URL for that icon.
+     * Pop the HTML for the next closing container from the stack. The $type
+     * must match the type passed when the container was opened, otherwise a
+     * warning will be output.
+     * @param string $type The type of container.
+     * @return string the HTML required to close the container.
      */
-    public function mod_icon_url($iconname, $module) {
-        global $CFG;
-        if (file_exists($CFG->themedir . '/' . $this->themename . '/pix/mod/' . $module . '/' . $iconname . '.png')) {
-            return $CFG->httpsthemewww . '/' . $this->themename . '/pix/mod/' . $module . '/' . $iconname . '.png';
-        } else {
-            return $CFG->httpsthemewww . '/' . $this->themename . '/pix/mod/' . $module . '/' . $iconname . '.gif';
+    public function pop($type) {
+        if (empty($this->opencontainers)) {
+            debugging('<p>There are no more open containers. This suggests there is a nesting problem.</p>' .
+                    $this->output_log(), DEBUG_DEVELOPER);
+            return;
         }
-    }
-}
-
 
-/**
- * This icon finder implements the algorithm in pix/smartpix.php.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class smartpix_icon_finder extends pix_icon_finder {
-    protected $places = array();
-
-    /**
-     * Constructor
-     * @param theme_config $theme the theme we are finding icons for.
-     */
-    public function __construct($theme) {
-        global $CFG;
-        $this->places[$CFG->themedir . '/' . $theme->name . '/pix/'] =
-                $CFG->httpsthemewww . '/' . $theme->name . '/pix/';
-        if (!empty($theme->parent)) {
-            $this->places[$CFG->themedir . '/' . $theme->parent . '/pix/'] =
-                    $CFG->httpsthemewww . '/' . $theme->parent . '/pix/';
+        $container = array_pop($this->opencontainers);
+        if ($container->type != $type) {
+            debugging('<p>The type of container to be closed (' . $container->type .
+                    ') does not match the type of the next open container (' . $type .
+                    '). This suggests there is a nesting problem.</p>' .
+                    $this->output_log(), DEBUG_DEVELOPER);
         }
-    }
-
-    /**
-     * Implement interface method.
-     * @param string $iconname the name of the icon.
-     * @return string the URL for that icon.
-     */
-    public function old_icon_url($iconname) {
-        foreach ($this->places as $dirroot => $urlroot) {
-            if (file_exists($dirroot . $iconname . '.png')) {
-                return $dirroot . $iconname . '.png';
-            } else if (file_exists($dirroot . $iconname . '.gif')) {
-                return $dirroot . $iconname . '.gif';
-            }
+        if ($this->isdebugging) {
+            $this->log('Close', $type);
         }
-        return parent::old_icon_url($iconname);
+        return $container->closehtml;
     }
 
     /**
-     * Implement interface method.
-     * @param string $iconname the name of the icon.
-     * @param string $module the module the icon belongs to.
-     * @return string the URL for that icon.
+     * Close all but the last open container. This is useful in places like error
+     * handling, where you want to close all the open containers (apart from <body>)
+     * before outputting the error message.
+     * @param bool $shouldbenone assert that the stack should be empty now - causes a
+     *      developer debug warning if it isn't.
+     * @return string the HTML required to close any open containers inside <body>.
      */
-    public function mod_icon_url($iconname, $module) {
-        foreach ($this->places as $dirroot => $urlroot) {
-            if (file_exists($dirroot . 'mod/' . $iconname . '.png')) {
-                return $dirroot . 'mod/' . $iconname . '.png';
-            } else if (file_exists($dirroot . 'mod/' . $iconname . '.gif')) {
-                return $dirroot . 'mod/' . $iconname . '.gif';
-            }
+    public function pop_all_but_last($shouldbenone = false) {
+        if ($shouldbenone && count($this->opencontainers) != 1) {
+            debugging('<p>Some HTML tags were opened in the body of the page but not closed.</p>' .
+                    $this->output_log(), DEBUG_DEVELOPER);
+        }
+        $output = '';
+        while (count($this->opencontainers) > 1) {
+            $container = array_pop($this->opencontainers);
+            $output .= $container->closehtml;
         }
-        return parent::old_icon_url($iconname, $module);
+        return $output;
     }
-}
-
-
-/**
- * This is a base class to help you implement the renderer_factory interface.
- *
- * It keeps a cache of renderers that have been constructed, so you only need
- * to construct each one once in you subclass.
- *
- * It also has a method to get the name of, and include the renderer.php with
- * the definition of, the standard renderer class for a given module.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-abstract class renderer_factory_base implements renderer_factory {
-    /** @var theme_config the theme we belong to. */
-    protected $theme;
 
     /**
-     * Constructor.
-     * @param theme_config $theme the theme we belong to.
+     * You can call this function if you want to throw away an instance of this
+     * class without properly emptying the stack (for example, in a unit test).
+     * Calling this method stops the destruct method from outputting a developer
+     * debug warning. After calling this method, the instance can no longer be used.
+     * @return void
      */
-    public function __construct($theme) {
-        $this->theme = $theme;
+    public function discard() {
+        $this->opencontainers = null;
     }
+
     /**
-     * For a given module name, return the name of the standard renderer class
-     * that defines the renderer interface for that module.
-     *
-     * Also, if it exists, include the renderer.php file for that module, so
-     * the class definition of the default renderer has been loaded.
-     *
-     * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
-     * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
-     * @return string the name of the standard renderer class for that module.
+     * Emergency fallback. If we get to the end of processing and not all
+     * containers have been closed, output the rest with a developer debug warning.
+     * @return void
      */
-    protected function standard_renderer_class_for_module($component, $subtype=null) {
-        if ($component != 'core') {
-            $pluginrenderer = get_component_directory($component) . '/renderer.php';
-            if (file_exists($pluginrenderer)) {
-                include_once($pluginrenderer);
-            }
-        }
-        if (is_null($subtype)) {
-            $class = 'moodle_' . $component . '_renderer';
-        } else {
-            $class = 'moodle_' . $component . '_' . $subtype . '_renderer';
+    public function __destruct() {
+        if (empty($this->opencontainers)) {
+            return;
         }
-        if (!class_exists($class)) {
-            throw new coding_exception('Request for an unknown renderer class ' . $class);
+
+        // It seems you cannot rely on $CFG, and hence the debugging function here,
+        // becuase $CFG may be destroyed before this object is.
+        if ($this->isdebugging) {
+            echo '<div class="notifytiny"><p>Some containers were left open. This suggests there is a nesting problem.</p>' .
+                    $this->output_log() . '</div>';
         }
-        return $class;
+        echo $this->pop_all_but_last();
+        $container = array_pop($this->opencontainers);
+        echo $container->closehtml;
     }
-}
-
 
-/**
- * This is the default renderer factory for Moodle. It simply returns an instance
- * of the appropriate standard renderer class.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class standard_renderer_factory extends renderer_factory_base {
     /**
-     * Implement the subclass method
-     * @param string $module name such as 'core', 'mod_forum' or 'qtype_multichoice'.
-     * @param moodle_page $page the page the renderer is outputting content for.
-     * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
-     * @return object an object implementing the requested renderer interface.
+     * Adds an entry to the log.
+     * @param string $action The name of the action
+     * @param string $type The type of action
+     * @return void
      */
-    public function get_renderer($module, $page, $subtype=null) {
-        if ($module == 'core') {
-            return new moodle_core_renderer($page);
-        } else {
-            $class = $this->standard_renderer_class_for_module($module, $subtype);
-            return new $class($page, $this->get_renderer('core', $page));
-        }
+    protected function log($action, $type) {
+        $this->log[] = '<li>' . $action . ' ' . $type . ' at:' .
+                format_backtrace(debug_backtrace()) . '</li>';
     }
-}
 
-
-/**
- * This is a slight variation on the standard_renderer_factory used by CLI scripts.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class cli_renderer_factory extends standard_renderer_factory {
     /**
-     * Implement the subclass method
-     * @param string $module name such as 'core', 'mod_forum' or 'qtype_multichoice'.
-     * @param moodle_page $page the page the renderer is outputting content for.
-     * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
-     * @return object an object implementing the requested renderer interface.
+     * Outputs the log's contents as a HTML list.
+     * @return string HTML list of the log
      */
-    public function get_renderer($module, $page, $subtype=null) {
-        if ($module == 'core') {
-            return new cli_core_renderer($page);
-        } else {
-            parent::get_renderer($module, $page, $subtype);
-        }
-    }
-}
-
-
-/**
- * This is renderer factory allows themes to override the standard renderers using
- * php code.
- *
- * It will load any code from theme/mytheme/renderers.php and
- * theme/parenttheme/renderers.php, if then exist. Then whenever you ask for
- * a renderer for 'component', it will create a mytheme_component_renderer or a
- * parenttheme_component_renderer, instead of a moodle_component_renderer,
- * if either of those classes exist.
- *
- * This generates the slightly different HTML that the custom_corners theme expects.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class theme_overridden_renderer_factory extends standard_renderer_factory {
-    protected $prefixes = array();
-
-    /**
-     * Constructor.
-     * @param object $theme the theme we are rendering for.
-     */
-    public function __construct($theme) {
-        global $CFG;
-        parent::__construct($theme);
-
-        // Initialise $this->prefixes.
-        $renderersfile = $theme->dir . '/renderers.php';
-        if (is_readable($renderersfile)) {
-            include_once($renderersfile);
-            $this->prefixes[] = $theme->name . '_';
-        }
-        if (!empty($theme->parent)) {
-            $renderersfile = $CFG->themedir .'/'. $theme->parent . '/renderers.php';
-            if (is_readable($renderersfile)) {
-                include_once($renderersfile);
-                $this->prefixes[] = $theme->parent . '_';
-            }
-        }
-    }
-
-    /**
-     * Implement the subclass method
-     * @param string $module name such as 'core', 'mod_forum' or 'qtype_multichoice'.
-     * @param moodle_page $page the page the renderer is outputting content for.
-     * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
-     * @return object an object implementing the requested renderer interface.
-     */
-    public function get_renderer($module, $page, $subtype=null) {
-        foreach ($this->prefixes as $prefix) {
-            if (is_null($subtype)) {
-                $classname = $prefix . $module . '_renderer';
-            } else {
-                $classname = $prefix . $module . '_' . $subtype . '_renderer';
-            }
-            if (class_exists($classname)) {
-                if ($module == 'core') {
-                    return new $classname($page);
-                } else {
-                    return new $classname($page, $this->get_renderer('core', $page));
-                }
-            }
-        }
-        return parent::get_renderer($module, $page, $subtype);
-    }
-}
-
-
-/**
- * This is renderer factory that allows you to create templated themes.
- *
- * This should be considered an experimental proof of concept. In particular,
- * the performance is probably not very good. Do not try to use in on a busy site
- * without doing careful load testing first!
- *
- * This renderer factory returns instances of {@link template_renderer} class
- * which which implement the corresponding renderer interface in terms of
- * templates. To use this your theme must have a templates folder inside it.
- * Then suppose the method moodle_core_renderer::greeting($name = 'world');
- * exists. Then, a call to $OUTPUT->greeting() will cause the template
- * /theme/yourtheme/templates/core/greeting.php to be rendered, with the variable
- * $name available. The greeting.php template might contain
- *
- * <pre>
- * <h1>Hello <?php echo $name ?>!</h1>
- * </pre>
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class template_renderer_factory extends renderer_factory_base {
-    /**
-     * An array of paths of where to search for templates. Normally this theme,
-     * the parent theme then the standardtemplate theme. (If some of these do
-     * not exist, or are the same as each other, then the list will be shorter.
-     */
-    protected $searchpaths = array();
-
-    /**
-     * Constructor.
-     * @param object $theme the theme we are rendering for.
-     */
-    public function __construct($theme) {
-        global $CFG;
-        parent::__construct($theme);
-
-        // Initialise $this->searchpaths.
-        if ($theme->name != 'standardtemplate') {
-            $templatesdir = $theme->dir . '/templates';
-            if (is_dir($templatesdir)) {
-                $this->searchpaths[] = $templatesdir;
-            }
-        }
-        if (!empty($theme->parent)) {
-            $templatesdir = $CFG->themedir .'/'. $theme->parent . '/templates';
-            if (is_dir($templatesdir)) {
-                $this->searchpaths[] = $templatesdir;
-            }
-        }
-        $this->searchpaths[] = $CFG->themedir .'/standardtemplate/templates';
-    }
-
-    /**
-     * Implement the subclass method
-     * @param string $module name such as 'core', 'mod_forum' or 'qtype_multichoice'.
-     * @param moodle_page $page the page the renderer is outputting content for.
-     * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
-     * @return object an object implementing the requested renderer interface.
-     */
-    public function get_renderer($module, $page, $subtype=null) {
-        // Refine the list of search paths for this module.
-        $searchpaths = array();
-        foreach ($this->searchpaths as $rootpath) {
-            $path = $rootpath . '/' . $module;
-            if (!is_null($subtype)) {
-                $path .= '/' . $subtype;
-            }
-            if (is_dir($path)) {
-                $searchpaths[] = $path;
-            }
-        }
-
-        // Create a template_renderer that copies the API of the standard renderer.
-        $copiedclass = $this->standard_renderer_class_for_module($module, $subtype);
-        return new template_renderer($copiedclass, $searchpaths, $page);
-    }
-}
-
-
-/**
- * Simple base class for Moodle renderers.
- *
- * Tracks the xhtml_container_stack to use, which is passed in in the constructor.
- *
- * Also has methods to facilitate generating HTML output.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class moodle_renderer_base {
-    /** @var xhtml_container_stack the xhtml_container_stack to use. */
-    protected $opencontainers;
-    /** @var moodle_page the page we are rendering for. */
-    protected $page;
-
-    /**
-     * Constructor
-     * @param moodle_page $page the page we are doing output for.
-     */
-    public function __construct($page) {
-        $this->opencontainers = $page->opencontainers;
-        $this->page = $page;
-    }
-
-    /**
-     * Have we started output yet?
-     * @return boolean true if the header has been printed.
-     */
-    public function has_started() {
-        return $this->page->state >= moodle_page::STATE_IN_BODY;
-    }
-
-    /**
-     * Outputs a tag with attributes and contents
-     * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
-     * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
-     * @param string $contents What goes between the opening and closing tags
-     * @return string HTML fragment
-     */
-    protected function output_tag($tagname, $attributes, $contents) {
-        return $this->output_start_tag($tagname, $attributes) . $contents .
-                $this->output_end_tag($tagname);
-    }
-
-    /**
-     * Outputs an opening tag with attributes
-     * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
-     * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
-     * @return string HTML fragment
-     */
-    protected function output_start_tag($tagname, $attributes) {
-        return '<' . $tagname . $this->output_attributes($attributes) . '>';
-    }
-
-    /**
-     * Outputs a closing tag
-     * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
-     * @return string HTML fragment
-     */
-    protected function output_end_tag($tagname) {
-        return '</' . $tagname . '>';
-    }
-
-    /**
-     * Outputs an empty tag with attributes
-     * @param string $tagname The name of tag ('input', 'img', 'br' etc.)
-     * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
-     * @return string HTML fragment
-     */
-    protected function output_empty_tag($tagname, $attributes) {
-        return '<' . $tagname . $this->output_attributes($attributes) . ' />';
-    }
-
-    /**
-     * Outputs a HTML attribute and value
-     * @param string $name The name of the attribute ('src', 'href', 'class' etc.)
-     * @param string $value The value of the attribute. The value will be escaped with {@link s()}
-     * @return string HTML fragment
-     */
-    protected function output_attribute($name, $value) {
-        if (is_array($value)) {
-            debugging("Passed an array for the HTML attribute $name", DEBUG_DEVELOPER);
-        }
-
-        $value = trim($value);
-        if ($value == HTML_ATTR_EMPTY) {
-            return ' ' . $name . '=""';
-        } else if ($value || is_numeric($value)) { // We want 0 to be output.
-            return ' ' . $name . '="' . s($value) . '"';
-        }
-    }
-
-    /**
-     * Outputs a list of HTML attributes and values
-     * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
-     *       The values will be escaped with {@link s()}
-     * @return string HTML fragment
-     */
-    protected function output_attributes($attributes) {
-        if (empty($attributes)) {
-            $attributes = array();
-        }
-        $output = '';
-        foreach ($attributes as $name => $value) {
-            $output .= $this->output_attribute($name, $value);
-        }
-        return $output;
-    }
-
-    /**
-     * Given an array or space-separated list of classes, prepares and returns the HTML class attribute value
-     * @param mixed $classes Space-separated string or array of classes
-     * @return string HTML class attribute value
-     */
-    public static function prepare_classes($classes) {
-        if (is_array($classes)) {
-            return implode(' ', array_unique($classes));
-        }
-        return $classes;
-    }
-
-    /**
-     * Return the URL for an icon identified as in pre-Moodle 2.0 code.
-     *
-     * Suppose you have old code like $url = "$CFG->pixpath/i/course.gif";
-     * then old_icon_url('i/course'); will return the equivalent URL that is correct now.
-     *
-     * @param string $iconname the name of the icon.
-     * @return string the URL for that icon.
-     */
-    public function old_icon_url($iconname) {
-        return $this->page->theme->old_icon_url($iconname);
-    }
-
-    /**
-     * Return the URL for an icon identified as in pre-Moodle 2.0 code.
-     *
-     * Suppose you have old code like $url = "$CFG->modpixpath/$mod/icon.gif";
-     * then mod_icon_url('icon', $mod); will return the equivalent URL that is correct now.
-     *
-     * @param string $iconname the name of the icon.
-     * @param string $module the module the icon belongs to.
-     * @return string the URL for that icon.
-     */
-    public function mod_icon_url($iconname, $module) {
-        return $this->page->theme->mod_icon_url($iconname, $module);
-    }
-
-    /**
-     * A helper function that takes a moodle_html_component subclass as param.
-     * If that component has an id attribute and an array of valid component_action objects,
-     * it sets up the appropriate event handlers.
-     *
-     * @param moodle_html_component $component
-     * @return void;
-     */
-    protected function prepare_event_handlers(&$component) {
-        $actions = $component->get_actions();
-        if (!empty($actions) && is_array($actions) && $actions[0] instanceof component_action) {
-            foreach ($actions as $action) {
-                if (!empty($action->jsfunction)) {
-                    $this->page->requires->event_handler($component->id, $action->event, $action->jsfunction, $action->jsfunctionargs);
-                }
-            }
-        }
-    }
-
-    /**
-     * Given a moodle_html_component with height and/or width set, translates them
-     * to appropriate CSS rules.
-     *
-     * @param moodle_html_component $component
-     * @return string CSS rules
-     */
-    protected function prepare_legacy_width_and_height($component) {
-        $output = '';
-        if (!empty($component->height)) {
-            // We need a more intelligent way to handle these warnings. If $component->height have come from
-            // somewhere in deprecatedlib.php, then there is no point outputting a warning here.
-            // debugging('Explicit height given to moodle_html_component leads to inline css. Use a proper CSS class instead.', DEBUG_DEVELOPER);
-            $output .= "height: {$component->height}px;";
-        }
-        if (!empty($component->width)) {
-            // debugging('Explicit width given to moodle_html_component leads to inline css. Use a proper CSS class instead.', DEBUG_DEVELOPER);
-            $output .= "width: {$component->width}px;";
-        }
-        return $output;
-    }
-}
-
-
-/**
- * This is the templated renderer which copies the API of another class, replacing
- * all methods calls with instantiation of a template.
- *
- * When the method method_name is called, this class will search for a template
- * called method_name.php in the folders in $searchpaths, taking the first one
- * that it finds. Then it will set up variables for each of the arguments of that
- * method, and render the template. This is implemented in the {@link __call()}
- * PHP magic method.
- *
- * Methods like print_box_start and print_box_end are handles specially, and
- * implemented in terms of the print_box.php method.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class template_renderer extends moodle_renderer_base {
-    /** @var ReflectionClass information about the class whose API we are copying. */
-    protected $copiedclass;
-    /** @var array of places to search for templates. */
-    protected $searchpaths;
-    protected $rendererfactory;
-
-    /**
-     * Magic word used when breaking apart container templates to implement
-     * _start and _end methods.
-     */
-    const CONTENTSTOKEN = '-@#-Contents-go-here-#@-';
-
-    /**
-     * Constructor
-     * @param string $copiedclass the name of a class whose API we should be copying.
-     * @param array $searchpaths a list of folders to search for templates in.
-     * @param moodle_page $page the page we are doing output for.
-     */
-    public function __construct($copiedclass, $searchpaths, $page) {
-        parent::__construct($page);
-        $this->copiedclass = new ReflectionClass($copiedclass);
-        $this->searchpaths = $searchpaths;
-    }
-
-    /**
-     * PHP magic method implementation. Do not use this method directly.
-     * @param string $method The method to call
-     * @param array $arguments The arguments to pass to the method
-     * @return mixed The return value of the called method
-     */
-    public function __call($method, $arguments) {
-        if (substr($method, -6) == '_start') {
-            return $this->process_start(substr($method, 0, -6), $arguments);
-        } else if (substr($method, -4) == '_end') {
-            return $this->process_end(substr($method, 0, -4), $arguments);
-        } else {
-            return $this->process_template($method, $arguments);
-        }
-    }
-
-    /**
-     * Render the template for a given method of the renderer class we are copying,
-     * using the arguments passed.
-     * @param string $method the method that was called.
-     * @param array $arguments the arguments that were passed to it.
-     * @return string the HTML to be output.
-     */
-    protected function process_template($method, $arguments) {
-        if (!$this->copiedclass->hasMethod($method) ||
-                !$this->copiedclass->getMethod($method)->isPublic()) {
-            throw new coding_exception('Unknown method ' . $method);
-        }
-
-        // Find the template file for this method.
-        $template = $this->find_template($method);
-
-        // Use the reflection API to find out what variable names the arguments
-        // should be stored in, and fill in any missing ones with the defaults.
-        $namedarguments = array();
-        $expectedparams = $this->copiedclass->getMethod($method)->getParameters();
-        foreach ($expectedparams as $param) {
-            $paramname = $param->getName();
-            if (!empty($arguments)) {
-                $namedarguments[$paramname] = array_shift($arguments);
-            } else if ($param->isDefaultValueAvailable()) {
-                $namedarguments[$paramname] = $param->getDefaultValue();
-            } else {
-                throw new coding_exception('Missing required argument ' . $paramname);
-            }
-        }
-
-        // Actually render the template.
-        return $this->render_template($template, $namedarguments);
-    }
-
-    /**
-     * Actually do the work of rendering the template.
-     * @param string $_template the full path to the template file.
-     * @param array $_namedarguments an array variable name => value, the variables
-     *      that should be available to the template.
-     * @return string the HTML to be output.
-     */
-    protected function render_template($_template, $_namedarguments) {
-        // Note, we intentionally break the coding guidelines with regards to
-        // local variable names used in this function, so that they do not clash
-        // with the names of any variables being passed to the template.
-
-        global $CFG, $SITE, $THEME, $USER;
-        // The next lines are a bit tricky. The point is, here we are in a method
-        // of a renderer class, and this object may, or may not, be the same as
-        // the global $OUTPUT object. When rendering the template, we want to use
-        // this object. However, people writing Moodle code expect the current
-        // renderer to be called $OUTPUT, not $this, so define a variable called
-        // $OUTPUT pointing at $this. The same comment applies to $PAGE and $COURSE.
-        $OUTPUT = $this;
-        $PAGE = $this->page;
-        $COURSE = $this->page->course;
-
-        // And the parameters from the function call.
-        extract($_namedarguments);
-
-        // Include the template, capturing the output.
-        ob_start();
-        include($_template);
-        $_result = ob_get_contents();
-        ob_end_clean();
-
-        return $_result;
-    }
-
-    /**
-     * Searches the folders in {@link $searchpaths} to try to find a template for
-     * this method name. Throws an exception if one cannot be found.
-     * @param string $method the method name.
-     * @return string the full path of the template to use.
-     */
-    protected function find_template($method) {
-        foreach ($this->searchpaths as $path) {
-            $filename = $path . '/' . $method . '.php';
-            if (file_exists($filename)) {
-                return $filename;
-            }
-        }
-        throw new coding_exception('Cannot find template for ' . $this->copiedclass->getName() . '::' . $method);
-    }
-
-    /**
-     * Handle methods like print_box_start by using the print_box template,
-     * splitting the result, pushing the end onto the stack, then returning the start.
-     * @param string $method the method that was called, with _start stripped off.
-     * @param array $arguments the arguments that were passed to it.
-     * @return string the HTML to be output.
-     */
-    protected function process_start($method, $arguments) {
-        array_unshift($arguments, self::CONTENTSTOKEN);
-        $html = $this->process_template($method, $arguments);
-        list($start, $end) = explode(self::CONTENTSTOKEN, $html, 2);
-        $this->opencontainers->push($method, $end);
-        return $start;
-    }
-
-    /**
-     * Handle methods like print_box_end, we just need to pop the end HTML from
-     * the stack.
-     * @param string $method the method that was called, with _end stripped off.
-     * @param array $arguments not used. Assumed to be irrelevant.
-     * @return string the HTML to be output.
-     */
-    protected function process_end($method, $arguments) {
-        return $this->opencontainers->pop($method);
-    }
-
-    /**
-     * @return array the list of paths where this class searches for templates.
-     */
-    public function get_search_paths() {
-        return $this->searchpaths;
-    }
-
-    /**
-     * @return string the name of the class whose API we are copying.
-     */
-    public function get_copied_class() {
-        return $this->copiedclass->getName();
-    }
-}
-
-
-/**
- * This class keeps track of which HTML tags are currently open.
- *
- * This makes it much easier to always generate well formed XHTML output, even
- * if execution terminates abruptly. Any time you output some opening HTML
- * without the matching closing HTML, you should push the necessary close tags
- * onto the stack.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class xhtml_container_stack {
-    /** @var array stores the list of open containers. */
-    protected $opencontainers = array();
-    /**
-     * @var array in developer debug mode, stores a stack trace of all opens and
-     * closes, so we can output helpful error messages when there is a mismatch.
-     */
-    protected $log = array();
-    /**
-     * Store whether we are developer debug mode. We need this in several places
-     * including in the destructor where we may no thave access to $CFG.
-     * @var boolean
-     */
-    protected $isdebugging;
-
-    public function __construct() {
-        $this->isdebugging = debugging('', DEBUG_DEVELOPER);
-    }
-
-    /**
-     * Push the close HTML for a recently opened container onto the stack.
-     * @param string $type The type of container. This is checked when {@link pop()}
-     *      is called and must match, otherwise a developer debug warning is output.
-     * @param string $closehtml The HTML required to close the container.
-     * @return void
-     */
-    public function push($type, $closehtml) {
-        $container = new stdClass;
-        $container->type = $type;
-        $container->closehtml = $closehtml;
-        if ($this->isdebugging) {
-            $this->log('Open', $type);
-        }
-        array_push($this->opencontainers, $container);
-    }
-
-    /**
-     * Pop the HTML for the next closing container from the stack. The $type
-     * must match the type passed when the container was opened, otherwise a
-     * warning will be output.
-     * @param string $type The type of container.
-     * @return string the HTML required to close the container.
-     */
-    public function pop($type) {
-        if (empty($this->opencontainers)) {
-            debugging('<p>There are no more open containers. This suggests there is a nesting problem.</p>' .
-                    $this->output_log(), DEBUG_DEVELOPER);
-            return;
-        }
-
-        $container = array_pop($this->opencontainers);
-        if ($container->type != $type) {
-            debugging('<p>The type of container to be closed (' . $container->type .
-                    ') does not match the type of the next open container (' . $type .
-                    '). This suggests there is a nesting problem.</p>' .
-                    $this->output_log(), DEBUG_DEVELOPER);
-        }
-        if ($this->isdebugging) {
-            $this->log('Close', $type);
-        }
-        return $container->closehtml;
-    }
-
-    /**
-     * Close all but the last open container. This is useful in places like error
-     * handling, where you want to close all the open containers (apart from <body>)
-     * before outputting the error message.
-     * @param bool $shouldbenone assert that the stack should be empty now - causes a
-     *      developer debug warning if it isn't.
-     * @return string the HTML required to close any open containers inside <body>.
-     */
-    public function pop_all_but_last($shouldbenone = false) {
-        if ($shouldbenone && count($this->opencontainers) != 1) {
-            debugging('<p>Some HTML tags were opened in the body of the page but not closed.</p>' .
-                    $this->output_log(), DEBUG_DEVELOPER);
-        }
-        $output = '';
-        while (count($this->opencontainers) > 1) {
-            $container = array_pop($this->opencontainers);
-            $output .= $container->closehtml;
-        }
-        return $output;
-    }
-
-    /**
-     * You can call this function if you want to throw away an instance of this
-     * class without properly emptying the stack (for example, in a unit test).
-     * Calling this method stops the destruct method from outputting a developer
-     * debug warning. After calling this method, the instance can no longer be used.
-     * @return void
-     */
-    public function discard() {
-        $this->opencontainers = null;
-    }
-
-    /**
-     * Emergency fallback. If we get to the end of processing and not all
-     * containers have been closed, output the rest with a developer debug warning.
-     * @return void
-     */
-    public function __destruct() {
-        if (empty($this->opencontainers)) {
-            return;
-        }
-
-        // It seems you cannot rely on $CFG, and hence the debugging function here,
-        // becuase $CFG may be destroyed before this object is.
-        if ($this->isdebugging) {
-            echo '<div class="notifytiny"><p>Some containers were left open. This suggests there is a nesting problem.</p>' .
-                    $this->output_log() . '</div>';
-        }
-        echo $this->pop_all_but_last();
-        $container = array_pop($this->opencontainers);
-        echo $container->closehtml;
-    }
-
-    /**
-     * Adds an entry to the log.
-     * @param string $action The name of the action
-     * @param string $type The type of action
-     * @return void
-     */
-    protected function log($action, $type) {
-        $this->log[] = '<li>' . $action . ' ' . $type . ' at:' .
-                format_backtrace(debug_backtrace()) . '</li>';
-    }
-
-    /**
-     * Outputs the log's contents as a HTML list.
-     * @return string HTML list of the log
-     */
-    protected function output_log() {
-        return '<ul>' . implode("\n", $this->log) . '</ul>';
-    }
-}
-
-
-/**
- * The standard implementation of the moodle_core_renderer interface.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class moodle_core_renderer extends moodle_renderer_base {
-    /** @var string used in {@link header()}. */
-    const PERFORMANCE_INFO_TOKEN = '%%PERFORMANCEINFO%%';
-    /** @var string used in {@link header()}. */
-    const END_HTML_TOKEN = '%%ENDHTML%%';
-    /** @var string used in {@link header()}. */
-    const MAIN_CONTENT_TOKEN = '[MAIN CONTENT GOES HERE]';
-    /** @var string used to pass information from {@link doctype()} to {@link standard_head_html()}. */
-    protected $contenttype;
-    /** @var string used by {@link redirect_message()} method to communicate with {@link header()}. */
-    protected $metarefreshtag = '';
-
-    /**
-     * Get the DOCTYPE declaration that should be used with this page. Designed to
-     * be called in theme layout.php files.
-     * @return string the DOCTYPE declaration (and any XML prologue) that should be used.
-     */
-    public function doctype() {
-        global $CFG;
-
-        $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n";
-        $this->contenttype = 'text/html; charset=utf-8';
-
-        if (empty($CFG->xmlstrictheaders)) {
-            return $doctype;
-        }
-
-        // We want to serve the page with an XML content type, to force well-formedness errors to be reported.
-        $prolog = '<?xml version="1.0" encoding="utf-8"?>' . "\n";
-        if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'application/xhtml+xml') !== false) {
-            // Firefox and other browsers that can cope natively with XHTML.
-            $this->contenttype = 'application/xhtml+xml; charset=utf-8';
-
-        } else if (preg_match('/MSIE.*Windows NT/', $_SERVER['HTTP_USER_AGENT'])) {
-            // IE can't cope with application/xhtml+xml, but it will cope if we send application/xml with an XSL stylesheet.
-            $this->contenttype = 'application/xml; charset=utf-8';
-            $prolog .= '<?xml-stylesheet type="text/xsl" href="' . $CFG->httpswwwroot . '/lib/xhtml.xsl"?>' . "\n";
-
-        } else {
-            $prolog = '';
-        }
-
-        return $prolog . $doctype;
-    }
-
-    /**
-     * The attributes that should be added to the <html> tag. Designed to
-     * be called in theme layout.php files.
-     * @return string HTML fragment.
-     */
-    public function htmlattributes() {
-        return get_html_lang(true) . ' xmlns="http://www.w3.org/1999/xhtml"';
-    }
-
-    /**
-     * The standard tags (meta tags, links to stylesheets and JavaScript, etc.)
-     * that should be included in the <head> tag. Designed to be called in theme
-     * layout.php files.
-     * @return string HTML fragment.
-     */
-    public function standard_head_html() {
-        global $CFG;
-        $output = '';
-        $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . "\n";
-        $output .= '<meta name="keywords" content="moodle, ' . $this->page->title . '" />' . "\n";
-        if (!$this->page->cacheable) {
-            $output .= '<meta http-equiv="pragma" content="no-cache" />' . "\n";
-            $output .= '<meta http-equiv="expires" content="0" />' . "\n";
-        }
-        // This is only set by the {@link redirect()} method
-        $output .= $this->metarefreshtag;
-
-        // Check if a periodic refresh delay has been set and make sure we arn't
-        // already meta refreshing
-        if ($this->metarefreshtag=='' && $this->page->periodicrefreshdelay!==null) {
-            $output .= '<meta http-equiv="refresh" content="'.$this->page->periodicrefreshdelay.';url='.$this->page->url->out().'" />';
-        }
-
-        $this->page->requires->js('lib/javascript-static.js')->in_head();
-        $this->page->requires->js('lib/javascript-deprecated.js')->in_head();
-        $this->page->requires->js('lib/javascript-mod.php')->in_head();
-        $this->page->requires->js('lib/overlib/overlib.js')->in_head();
-        $this->page->requires->js('lib/overlib/overlib_cssstyle.js')->in_head();
-        $this->page->requires->js('lib/cookies.js')->in_head();
-        $this->page->requires->js_function_call('setTimeout', Array('fix_column_widths()', 20));
-
-        $focus = $this->page->focuscontrol;
-        if (!empty($focus)) {
-            if (preg_match("#forms\['([a-zA-Z0-9]+)'\].elements\['([a-zA-Z0-9]+)'\]#", $focus, $matches)) {
-                // This is a horrifically bad way to handle focus but it is passed in
-                // through messy formslib::moodleform
-                $this->page->requires->js_function_call('old_onload_focus', Array($matches[1], $matches[2]));
-            } else if (strpos($focus, '.')!==false) {
-                // Old style of focus, bad way to do it
-                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);
-                $this->page->requires->js_function_call('old_onload_focus', explode('.', $focus, 2));
-            } else {
-                // Focus element with given id
-                $this->page->requires->js_function_call('focuscontrol', Array($focus));
-            }
-        }
-
-        // Add the meta tags from the themes if any were requested.
-        $output .= $this->page->theme->get_meta_tags($this->page);
-
-        // Get any HTML from the page_requirements_manager.
-        $output .= $this->page->requires->get_head_code();
-
-        // List alternate versions.
-        foreach ($this->page->alternateversions as $type => $alt) {
-            $output .= $this->output_empty_tag('link', array('rel' => 'alternate',
-                    'type' => $type, 'title' => $alt->title, 'href' => $alt->url));
-        }
-
-        return $output;
-    }
-
-    /**
-     * The standard tags (typically skip links) that should be output just inside
-     * the start of the <body> tag. Designed to be called in theme layout.php files.
-     * @return string HTML fragment.
-     */
-    public function standard_top_of_body_html() {
-        return  $this->page->requires->get_top_of_body_code();
-    }
-
-    /**
-     * The standard tags (typically performance information and validation links,
-     * if we are in developer debug mode) that should be output in the footer area
-     * of the page. Designed to be called in theme layout.php files.
-     * @return string HTML fragment.
-     */
-    public function standard_footer_html() {
-        global $CFG;
-
-        // This function is normally called from a layout.php file in {@link header()}
-        // but some of the content won't be known until later, so we return a placeholder
-        // for now. This will be replaced with the real content in {@link footer()}.
-        $output = self::PERFORMANCE_INFO_TOKEN;
-        if (!empty($CFG->debugpageinfo)) {
-            $output .= '<div class="performanceinfo">This page is: ' . $this->page->debug_summary() . '</div>';
-        }
-        if (!empty($CFG->debugvalidators)) {
-            $output .= '<div class="validators"><ul>
-              <li><a href="http://validator.w3.org/check?verbose=1&amp;ss=1&amp;uri=' . urlencode(qualified_me()) . '">Validate HTML</a></li>
-              <li><a href="http://www.contentquality.com/mynewtester/cynthia.exe?rptmode=-1&amp;url1=' . urlencode(qualified_me()) . '">Section 508 Check</a></li>
-              <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>
-            </ul></div>';
-        }
-        return $output;
-    }
-
-    /**
-     * The standard tags (typically script tags that are not needed earlier) that
-     * should be output after everything else, . Designed to be called in theme layout.php files.
-     * @return string HTML fragment.
-     */
-    public function standard_end_of_body_html() {
-        // This function is normally called from a layout.php file in {@link header()}
-        // but some of the content won't be known until later, so we return a placeholder
-        // for now. This will be replaced with the real content in {@link footer()}.
-        echo self::END_HTML_TOKEN;
-    }
-
-    /**
-     * Return the standard string that says whether you are logged in (and switched
-     * roles/logged in as another user).
-     * @return string HTML fragment.
-     */
-    public function login_info() {
-        global $USER;
-        return user_login_string($this->page->course, $USER);
-    }
-
-    /**
-     * Return the 'back' link that normally appears in the footer.
-     * @return string HTML fragment.
-     */
-    public function home_link() {
-        global $CFG, $SITE;
-
-        if ($this->page->pagetype == 'site-index') {
-            // Special case for site home page - please do not remove
-            return '<div class="sitelink">' .
-                   '<a title="Moodle ' . $CFG->release . '" href="http://moodle.org/">' .
-                   '<img style="width:100px;height:30px" src="' . $CFG->httpswwwroot . '/pix/moodlelogo.gif" alt="moodlelogo" /></a></div>';
-
-        } else if (!empty($CFG->target_release) && $CFG->target_release != $CFG->release) {
-            // Special case for during install/upgrade.
-            return '<div class="sitelink">'.
-                   '<a title="Moodle ' . $CFG->target_release . '" href="http://docs.moodle.org/en/Administrator_documentation" onclick="this.target=\'_blank\'">' .
-                   '<img style="width:100px;height:30px" src="' . $CFG->httpswwwroot . '/pix/moodlelogo.gif" alt="moodlelogo" /></a></div>';
-
-        } else if ($this->page->course->id == $SITE->id || strpos($this->page->pagetype, 'course-view') === 0) {
-            return '<div class="homelink"><a href="' . $CFG->wwwroot . '/">' .
-                    get_string('home') . '</a></div>';
-
-        } else {
-            return '<div class="homelink"><a href="' . $CFG->wwwroot . '/course/view.php?id=' . $this->page->course->id . '">' .
-                    format_string($this->page->course->shortname) . '</a></div>';
-        }
-    }
-
-    /**
-     * Redirects the user by any means possible given the current state
-     *
-     * This function should not be called directly, it should always be called using
-     * the redirect function in lib/weblib.php
-     *
-     * The redirect function should really only be called before page output has started
-     * however it will allow itself to be called during the state STATE_IN_BODY
-     *
-     * @param string $encodedurl The URL to send to encoded if required
-     * @param string $message The message to display to the user if any
-     * @param int $delay The delay before redirecting a user, if $message has been
-     *         set this is a requirement and defaults to 3, set to 0 no delay
-     * @param boolean $debugdisableredirect this redirect has been disabled for
-     *         debugging purposes. Display a message that explains, and don't
-     *         trigger the redirect.
-     * @return string The HTML to display to the user before dying, may contain
-     *         meta refresh, javascript refresh, and may have set header redirects
-     */
-    public function redirect_message($encodedurl, $message, $delay, $debugdisableredirect) {
-        global $CFG;
-        $url = str_replace('&amp;', '&', $encodedurl);
-
-        switch ($this->page->state) {
-            case moodle_page::STATE_BEFORE_HEADER :
-                // No output yet it is safe to delivery the full arsenal of redirect methods
-                if (!$debugdisableredirect) {
-                    // Don't use exactly the same time here, it can cause problems when both redirects fire at the same time.
-                    $this->metarefreshtag = '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />'."\n";
-                    $this->page->requires->js_function_call('document.location.replace', array($url))->after_delay($delay + 3);
-                }
-                $output = $this->header();
-                break;
-            case moodle_page::STATE_PRINTING_HEADER :
-                // We should hopefully never get here
-                throw new coding_exception('You cannot redirect while printing the page header');
-                break;
-            case moodle_page::STATE_IN_BODY :
-                // We really shouldn't be here but we can deal with this
-                debugging("You should really redirect before you start page output");
-                if (!$debugdisableredirect) {
-                    $this->page->requires->js_function_call('document.location.replace', array($url))->after_delay($delay);
-                }
-                $output = $this->opencontainers->pop_all_but_last();
-                break;
-            case moodle_page::STATE_DONE :
-                // Too late to be calling redirect now
-                throw new coding_exception('You cannot redirect after the entire page has been generated');
-                break;
-        }
-        $output .= $this->notification($message, 'redirectmessage');
-        $output .= '<a href="'. $encodedurl .'">'. get_string('continue') .'</a>';
-        if ($debugdisableredirect) {
-            $output .= '<p><strong>Error output, so disabling automatic redirect.</strong></p>';
-        }
-        $output .= $this->footer();
-        return $output;
-    }
-
-    /**
-     * Start output by sending the HTTP headers, and printing the HTML <head>
-     * and the start of the <body>.
-     *
-     * To control what is printed, you should set properties on $PAGE. If you
-     * are familiar with the old {@link print_header()} function from Moodle 1.9
-     * you will find that there are properties on $PAGE that correspond to most
-     * of the old parameters to could be passed to print_header.
-     *
-     * Not that, in due course, the remaining $navigation, $menu parameters here
-     * will be replaced by more properties of $PAGE, but that is still to do.
-     *
-     * @param string $navigation legacy, like the old parameter to print_header. Will be
-     *      removed when there is a $PAGE->... replacement.
-     * @param string $menu legacy, like the old parameter to print_header. Will be
-     *      removed when there is a $PAGE->... replacement.
-     * @return string HTML that you must output this, preferably immediately.
-     */
-    public function header($navigation = '', $menu='') {
-        // TODO remove $navigation and $menu arguments - replace with $PAGE->navigation
-        global $USER, $CFG;
-
-        $this->page->set_state(moodle_page::STATE_PRINTING_HEADER);
-
-        // Find the appropriate page template, based on $this->page->generaltype.
-        $templatefile = $this->page->theme->template_for_page($this->page->generaltype);
-        if ($templatefile) {
-            // Render the template.
-            $template = $this->render_page_template($templatefile, $menu, $navigation);
-        } else {
-            // New style template not found, fall back to using header.html and footer.html.
-            $template = $this->handle_legacy_theme($navigation, $menu);
-        }
-
-        // Slice the template output into header and footer.
-        $cutpos = strpos($template, self::MAIN_CONTENT_TOKEN);
-        if ($cutpos === false) {
-            throw new coding_exception('Layout template ' . $templatefile .
-                    ' does not contain the string "' . self::MAIN_CONTENT_TOKEN . '".');
-        }
-        $header = substr($template, 0, $cutpos);
-        $footer = substr($template, $cutpos + strlen(self::MAIN_CONTENT_TOKEN));
-
-        if (empty($this->contenttype)) {
-            debugging('The layout template did not call $OUTPUT->doctype()');
-            $this->doctype();
-        }
-
-        send_headers($this->contenttype, $this->page->cacheable);
-        $this->opencontainers->push('header/footer', $footer);
-        $this->page->set_state(moodle_page::STATE_IN_BODY);
-        return $header . $this->skip_link_target();
-    }
-
-    /**
-     * Renders and outputs the page template.
-     * @param string $templatefile The name of the template's file
-     * @param array $menu The menu that will be used in the included file
-     * @param array $navigation The navigation that will be used in the included file
-     * @return string HTML code
-     */
-    protected function render_page_template($templatefile, $menu, $navigation) {
-        global $CFG, $SITE, $THEME, $USER;
-        // The next lines are a bit tricky. The point is, here we are in a method
-        // of a renderer class, and this object may, or may not, be the same as
-        // the global $OUTPUT object. When rendering the template, we want to use
-        // this object. However, people writing Moodle code expect the current
-        // renderer to be called $OUTPUT, not $this, so define a variable called
-        // $OUTPUT pointing at $this. The same comment applies to $PAGE and $COURSE.
-        $OUTPUT = $this;
-        $PAGE = $this->page;
-        $COURSE = $this->page->course;
-
-        ob_start();
-        include($templatefile);
-        $template = ob_get_contents();
-        ob_end_clean();
-        return $template;
-    }
-
-    /**
-     * Renders and outputs a legacy template.
-     * @param array $navigation The navigation that will be used in the included file
-     * @param array $menu The menu that will be used in the included file
-     * @return string HTML code
-     */
-    protected function handle_legacy_theme($navigation, $menu) {
-        global $CFG, $SITE, $USER;
-        // Set a pretend global from the properties of this class.
-        // See the comment in render_page_template for a fuller explanation.
-        $COURSE = $this->page->course;
-        $THEME = $this->page->theme;
-
-        // Set up local variables that header.html expects.
-        $direction = $this->htmlattributes();
-        $title = $this->page->title;
-        $heading = $this->page->heading;
-        $focus = $this->page->focuscontrol;
-        $button = $this->page->button;
-        $pageid = $this->page->pagetype;
-        $pageclass = $this->page->bodyclasses;
-        $bodytags = ' class="' . $pageclass . '" id="' . $pageid . '"';
-        $home = $this->page->generaltype == 'home';
-
-        $meta = $this->standard_head_html();
-        // The next line is a nasty hack. having set $meta to standard_head_html, we have already
-        // got the contents of include($CFG->javascript). However, legacy themes are going to
-        // include($CFG->javascript) again. We want to make sure that when they do, nothing is output.
-        $CFG->javascript = $CFG->libdir . '/emptyfile.php';
-
-        // Set up local variables that footer.html expects.
-        $homelink = $this->home_link();
-        $loggedinas = $this->login_info();
-        $course = $this->page->course;
-        $performanceinfo = self::PERFORMANCE_INFO_TOKEN;
-
-        if (!$menu && $navigation) {
-            $menu = $loggedinas;
-        }
-
-        if (!empty($this->page->theme->layouttable)) {
-            $lt = $this->page->theme->layouttable;
-        } else {
-            $lt = array('left', 'middle', 'right');
-        }
-
-        if (!empty($this->page->theme->block_l_max_width)) {
-            $preferredwidthleft = $this->page->theme->block_l_max_width;
-        } else {
-            $preferredwidthleft = 210;
-        }
-        if (!empty($this->page->theme->block_r_max_width)) {
-            $preferredwidthright = $this->page->theme->block_r_max_width;
-        } else {
-            $preferredwidthright = 210;
-        }
-
-        ob_start();
-        include($this->page->theme->dir . '/header.html');
-
-        echo '<table id="layout-table"><tr>';
-        foreach ($lt as $column) {
-            if ($column == 'left' && $this->page->blocks->region_has_content(BLOCK_POS_LEFT, $this)) {
-                echo '<td id="left-column" class="block-region" style="width: ' . $preferredwidthright . 'px; vertical-align: top;">';
-                echo $this->container_start();
-                echo $this->blocks_for_region(BLOCK_POS_LEFT);
-                echo $this->container_end();
-                echo '</td>';
-
-            } else if ($column == 'middle') {
-                echo '<td id="middle-column" style="vertical-align: top;">';
-                echo $this->container_start();
-                echo $this->skip_link_target();
-                echo self::MAIN_CONTENT_TOKEN;
-                echo $this->container_end();
-                echo '</td>';
-
-            } else if ($column == 'right' && $this->page->blocks->region_has_content(BLOCK_POS_RIGHT, $this)) {
-                echo '<td id="right-column" class="block-region" style="width: ' . $preferredwidthright . 'px; vertical-align: top;">';
-                echo $this->container_start();
-                echo $this->blocks_for_region(BLOCK_POS_RIGHT);
-                echo $this->container_end();
-                echo '</td>';
-            }
-        }
-        echo '</tr></table>';
-
-        $menu = str_replace('navmenu', 'navmenufooter', $menu);
-        include($THEME->dir . '/footer.html');
-
-        $output = ob_get_contents();
-        ob_end_clean();
-
-        // Put in the start of body code. Bit of a hack, put it in before the first
-        // <div or <table.
-        $divpos = strpos($output, '<div');
-        $tablepos = strpos($output, '<table');
-        if ($divpos === false || ($tablepos !== false && $tablepos < $divpos)) {
-            $pos = $tablepos;
-        } else {
-            $pos = $divpos;
-        }
-        $output = substr($output, 0, $divpos) . $this->standard_top_of_body_html() .
-                substr($output, $divpos);
-
-        // Put in the end token before the end of body.
-        $output = str_replace('</body>', self::END_HTML_TOKEN . '</body>', $output);
-
-        // Make sure we use the correct doctype.
-        $output = preg_replace('/(<!DOCTYPE.+?>)/s', $this->doctype(), $output);
-
-        return $output;
-    }
-
-    /**
-     * Outputs the page's footer
-     * @return string HTML fragment
-     */
-    public function footer() {
-        $output = $this->opencontainers->pop_all_but_last(true);
-
-        $footer = $this->opencontainers->pop('header/footer');
-
-        // Provide some performance info if required
-        $performanceinfo = '';
-        if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) {
-            $perf = get_performance_info();
-            if (defined('MDL_PERFTOLOG') && !function_exists('register_shutdown_function')) {
-                error_log("PERF: " . $perf['txt']);
-            }
-            if (defined('MDL_PERFTOFOOT') || debugging() || $CFG->perfdebug > 7) {
-                $performanceinfo = $perf['html'];
-            }
-        }
-        $footer = str_replace(self::PERFORMANCE_INFO_TOKEN, $performanceinfo, $footer);
-
-        $footer = str_replace(self::END_HTML_TOKEN, $this->page->requires->get_end_code(), $footer);
-
-        $this->page->set_state(moodle_page::STATE_DONE);
-
-
-        return $output . $footer;
-    }
-
-    /**
-     * Output the row of editing icons for a block, as defined by the controls array.
-     * @param array $controls an array like {@link block_contents::$controls}.
-     * @return HTML fragment.
-     */
-    public function block_controls($controls) {
-        if (empty($controls)) {
-            return '';
-        }
-        $controlshtml = array();
-        foreach ($controls as $control) {
-            $controlshtml[] = $this->output_tag('a', array('class' => 'icon',
-                    'title' => $control['caption'], 'href' => $control['url']),
-                    $this->output_empty_tag('img',  array('src' => $this->old_icon_url($control['icon']),
-                    'alt' => $control['caption'])));
-        }
-        return $this->output_tag('div', array('class' => 'commands'), implode('', $controlshtml));
-    }
-
-    /**
-     * Prints a nice side block with an optional header.
-     *
-     * The content is described
-     * by a {@link block_contents} object.
-     *
-     * @param block_contents $bc HTML for the content
-     * @param string $region the region the block is appearing in.
-     * @return string the HTML to be output.
-     */
-    function block($bc, $region) {
-        $bc = clone($bc); // Avoid messing up the object passed in.
-        $bc->prepare();
-
-        $skiptitle = strip_tags($bc->title);
-        if (empty($skiptitle)) {
-            $output = '';
-            $skipdest = '';
-        } else {
-            $output = $this->output_tag('a', array('href' => '#sb-' . $bc->skipid, 'class' => 'skip-block'),
-                    get_string('skipa', 'access', $skiptitle));
-            $skipdest = $this->output_tag('span', array('id' => 'sb-' . $bc->skipid, 'class' => 'skip-block-to'), '');
-        }
-
-        $bc->attributes['id'] = $bc->id;
-        $bc->attributes['class'] = $bc->get_classes_string();
-        $output .= $this->output_start_tag('div', $bc->attributes);
-
-        $controlshtml = $this->block_controls($bc->controls);
-
-        $title = '';
-        if ($bc->title) {
-            $title = $this->output_tag('h2', null, $bc->title);
-        }
-
-        if ($title || $controlshtml) {
-            $output .= $this->output_tag('div', array('class' => 'header'),
-                    $this->output_tag('div', array('class' => 'title'),
-                    $title . $controlshtml));
-        }
-
-        $output .= $this->output_start_tag('div', array('class' => 'content'));
-        $output .= $bc->content;
-
-        if ($bc->footer) {
-            $output .= $this->output_tag('div', array('class' => 'footer'), $bc->footer);
-        }
-
-        $output .= $this->output_end_tag('div');
-        $output .= $this->output_end_tag('div');
-
-        if ($bc->annotation) {
-            $output .= $this->output_tag('div', array('class' => 'blockannotation'), $bc->annotation);
-        }
-        $output .= $skipdest;
-
-        $this->init_block_hider_js($bc);
-        return $output;
-    }
-
-    /**
-     * Calls the JS require function to hide a block.
-     * @param block_contents $bc A block_contents object
-     * @return void
-     */
-    protected function init_block_hider_js($bc) {
-        if ($bc->collapsible != block_contents::NOT_HIDEABLE) {
-            $userpref = 'block' . $bc->blockinstanceid . 'hidden';
-            user_preference_allow_ajax_update($userpref, PARAM_BOOL);
-            $this->page->requires->yui_lib('dom');
-            $this->page->requires->yui_lib('event');
-            $plaintitle = strip_tags($bc->title);
-            $this->page->requires->js_function_call('new block_hider', array($bc->id, $userpref,
-                    get_string('hideblocka', 'access', $plaintitle), get_string('showblocka', 'access', $plaintitle),
-                    $this->old_icon_url('t/switch_minus'), $this->old_icon_url('t/switch_plus')));
-        }
-    }
-
-    /**
-     * Render the contents of a block_list.
-     * @param array $icons the icon for each item.
-     * @param array $items the content of each item.
-     * @return string HTML
-     */
-    public function list_block_contents($icons, $items) {
-        $row = 0;
-        $lis = array();
-        foreach ($items as $key => $string) {
-            $item = $this->output_start_tag('li', array('class' => 'r' . $row));
-            if ($icons) {
-                $item .= $this->output_tag('div', array('class' => 'icon column c0'), $icons[$key]);
-            }
-            $item .= $this->output_tag('div', array('class' => 'column c1'), $string);
-            $item .= $this->output_end_tag('li');
-            $lis[] = $item;
-            $row = 1 - $row; // Flip even/odd.
-        }
-        return $this->output_tag('ul', array('class' => 'list'), implode("\n", $lis));
-    }
-
-    /**
-     * Output all the blocks in a particular region.
-     * @param string $region the name of a region on this page.
-     * @return string the HTML to be output.
-     */
-    public function blocks_for_region($region) {
-        $blockcontents = $this->page->blocks->get_content_for_region($region, $this);
-
-        $output = '';
-        foreach ($blockcontents as $bc) {
-            if ($bc instanceof block_contents) {
-                $output .= $this->block($bc, $region);
-            } else if ($bc instanceof block_move_target) {
-                $output .= $this->block_move_target($bc);
-            } else {
-                throw new coding_exception('Unexpected type of thing (' . get_class($bc) . ') found in list of block contents.');
-            }
-        }
-        return $output;
-    }
-
-    /**
-     * Output a place where the block that is currently being moved can be dropped.
-     * @param block_move_target $target with the necessary details.
-     * @return string the HTML to be output.
-     */
-    public function block_move_target($target) {
-        return $this->output_tag('a', array('href' => $target->url, 'class' => 'blockmovetarget'),
-                $this->output_tag('span', array('class' => 'accesshide'), $target->text));
-    }
-
-    /**
-     * Given a html_link object, outputs an <a> tag that uses the object's attributes.
-     *
-     * @param mixed $link A html_link object or a string URL (text param required in second case)
-     * @param string $text A descriptive text for the link. If $link is a html_link, this is not required.
-     * @return string HTML fragment
-     */
-    public function link($link, $text=null) {
-        $attributes = array();
-
-        if (is_a($link, 'html_link')) {
-            $link = clone($link);
-            $link->prepare();
-            $this->prepare_event_handlers($link);
-            $attributes['href'] = prepare_url($link->url);
-            $attributes['class'] = $link->get_classes_string();
-            $attributes['title'] = $link->title;
-            $attributes['id'] = $link->id;
-
-            $text = $link->text;
-
-        } else if (empty($text)) {
-            throw new coding_exception('$OUTPUT->link() must have a string as second parameter if the first param ($link) is a string');
-
-        } else {
-            $attributes['href'] = prepare_url($link);
-        }
-
-        return $this->output_tag('a', $attributes, $text);
-    }
-
-   /**
-    * Print a message along with button choices for Continue/Cancel. Labels default to Yes(Continue)/No(Cancel).
-    * If a string or moodle_url is given instead of a html_button, method defaults to post and text to Yes/No
-    * @param string $message The question to ask the user
-    * @param mixed $continue The html_form component representing the Continue answer. Can also be a moodle_url or string URL
-    * @param mixed $cancel The html_form component representing the Cancel answer. Can also be a moodle_url or string URL
-    * @return string HTML fragment
-    */
-    public function confirm($message, $continue, $cancel) {
-        if ($continue instanceof html_form) {
-            $continue = clone($continue);
-        } else if (is_string($continue)) {
-            $continueform = new html_form();
-            $continueform->url = new moodle_url($continue);
-            $continue = $continueform;
-        } else if ($continue instanceof moodle_url) {
-            $continueform = new html_form();
-            $continueform->url = $continue;
-            $continue = $continueform;
-        } else {
-            throw new coding_exception('The continue param to $OUTPUT->confirm must be either a URL (string/moodle_url) or a html_form object.');
-        }
-
-        if ($cancel instanceof html_form) {
-            $cancel = clone($cancel);
-        } else if (is_string($cancel)) {
-            $cancelform = new html_form();
-            $cancelform->url = new moodle_url($cancel);
-            $cancel = $cancelform;
-        } else if ($cancel instanceof moodle_url) {
-            $cancelform = new html_form();
-            $cancelform->url = $cancel;
-            $cancel = $cancelform;
-        } else {
-            throw new coding_exception('The cancel param to $OUTPUT->confirm must be either a URL (string/moodle_url) or a html_form object.');
-        }
-
-        if (empty($continue->button->text)) {
-            $continue->button->text = get_string('yes');
-        }
-        if (empty($cancel->button->text)) {
-            $cancel->button->text = get_string('no');
-        }
-
-        $output = $this->box_start('generalbox', 'notice');
-        $output .= $this->output_tag('p', array(), $message);
-        $output .= $this->output_tag('div', array('class' => 'buttons'), $this->button($continue) . $this->button($cancel));
-        $output .= $this->box_end();
-        return $output;
-    }
-
-    /**
-     * Given a html_form object, outputs an <input> tag within a form that uses the object's attributes.
-     *
-     * @param html_form $form A html_form object
-     * @return string HTML fragment
-     */
-    public function button($form) {
-        if (empty($form->button) or !($form->button instanceof html_button)) {
-            throw new coding_exception('$OUTPUT->button($form) requires $form to have a button (html_button) value');
-        }
-        $form = clone($form);
-        $form->button->prepare();
-
-        $this->prepare_event_handlers($form->button);
-
-        $buttonattributes = array('class' => $form->button->get_classes_string(),
-                                  'type' => 'submit',
-                                  'value' => $form->button->text,
-                                  'disabled' => $form->button->disabled,
-                                  'id' => $form->button->id);
-
-        $buttonoutput = $this->output_empty_tag('input', $buttonattributes);
-
-        // Removing the button so it doesn't get output again
-        unset($form->button);
-
-        return $this->form($form, $buttonoutput);
-    }
-
-    /**
-     * Given a html_form component and an optional rendered submit button,
-     * outputs a HTML form with correct divs and inputs and a single submit button.
-     * This doesn't render any other visible inputs. Use moodleforms for these.
-     * @param html_form $form A html_form instance
-     * @param string $contents HTML fragment to put inside the form. If given, must contain at least the submit button.
-     * @return string HTML fragment
-     */
-    public function form($form, $contents=null) {
-        $form = clone($form);
-        $form->prepare();
-        $this->prepare_event_handlers($form);
-        $buttonoutput = null;
-
-        if (empty($contents) && !empty($form->button)) {
-            debugging("You probably want to use \$OUTPUT->button(\$form), please read that function's documentation", DEBUG_DEVELOPER);
-        } else if (empty($contents)) {
-            $contents = $this->output_empty_tag('input', array('type' => 'submit', 'value' => get_string('ok')));
-        } else if (!empty($form->button)) {
-            $form->button->prepare();
-            $buttonoutput = $this->output_start_tag('div', array('id' => "noscript$form->id"));
-            $this->prepare_event_handlers($form->button);
-
-            $buttonattributes = array('class' => $form->button->get_classes_string(),
-                                      'type' => 'submit',
-                                      'value' => $form->button->text,
-                                      'disabled' => $form->button->disabled,
-                                      'id' => $form->button->id);
-
-            $buttonoutput .= $this->output_empty_tag('input', $buttonattributes);
-            $buttonoutput .= $this->output_end_tag('div');
-            $this->page->requires->js_function_call('hide_item', array("noscript$form->id"));
-
-        }
-
-        $hiddenoutput = '';
-
-        foreach ($form->url->params() as $var => $val) {
-            $hiddenoutput .= $this->output_empty_tag('input', array('type' => 'hidden', 'name' => $var, 'value' => $val));
-        }
-
-        $formattributes = array(
-                'method' => $form->method,
-                'action' => prepare_url($form->url, true),
-                'id' => $form->id,
-                'class' => $form->get_classes_string());
-
-        $divoutput = $this->output_tag('div', array(), $hiddenoutput . $contents . $buttonoutput);
-        $formoutput = $this->output_tag('form', $formattributes, $divoutput);
-        $output = $this->output_tag('div', array('class' => 'singlebutton'), $formoutput);
-
-        return $output;
-    }
-
-    /**
-     * Returns a string containing a link to the user documentation.
-     * Also contains an icon by default. Shown to teachers and admin only.
-     * @param string $path The page link after doc root and language, no leading slash.
-     * @param string $text The text to be displayed for the link
-     * @param string $iconpath The path to the icon to be displayed
-     */
-    public function doc_link($path, $text=false, $iconpath=false) {
-        global $CFG, $OUTPUT;
-        $icon = new action_icon();
-        $icon->linktext = $text;
-        $icon->image->alt = $text;
-        $icon->image->add_class('iconhelp');
-        $icon->link->url = new moodle_url(get_docs_url($path));
-
-        if (!empty($iconpath)) {
-            $icon->image->src = $iconpath;
-        } else {
-            $icon->image->src = $this->old_icon_url('docs');
-        }
-
-        if (!empty($CFG->doctonewwindow)) {
-            $icon->actions[] = new popup_action('click', $icon->link->url);
-        }
-
-        return $this->action_icon($icon);
-
-    }
-
-    /**
-     * Given a action_icon object, outputs an image linking to an action (URL or AJAX).
-     *
-     * @param action_icon $icon An action_icon object
-     * @return string HTML fragment
-     */
-    public function action_icon($icon) {
-        $icon = clone($icon);
-        $icon->prepare();
-        $imageoutput = $this->image($icon->image);
-
-        if ($icon->linktext) {
-            $imageoutput .= $icon->linktext;
-        }
-        $icon->link->text = $imageoutput;
-
-        return $this->link($icon->link);
-    }
-
-    /*
-     * Centered heading with attached help button (same title text)
-     * and optional icon attached
-     * @param help_icon $helpicon A help_icon object
-     * @param mixed $image An image URL or a html_image object
-     * @return string HTML fragment
-     */
-    public function heading_with_help($helpicon, $image=false) {
-        if (!($image instanceof html_image) && !empty($image)) {
-            $htmlimage = new html_image();
-            $htmlimage->src = $image;
-            $image = $htmlimage;
-        }
-        return $this->container($this->image($image) . $this->heading($helpicon->text, 2, 'main help') . $this->help_icon($helpicon), 'heading-with-help');
-    }
-
-    /**
-     * Print a help icon.
-     *
-     * @param help_icon $helpicon A help_icon object, subclass of html_link
-     *
-     * @return string  HTML fragment
-     */
-    public function help_icon($icon) {
-        global $COURSE;
-        $icon = clone($icon);
-        $icon->prepare();
-
-        $popup = new popup_action('click', $icon->link->url);
-        $icon->link->add_action($popup);
-
-        $image = null;
-
-        if (!empty($icon->image)) {
-            $image = $icon->image;
-            $image->add_class('iconhelp');
-        }
-
-        return $this->output_tag('span', array('class' => 'helplink'), $this->link_to_popup($icon->link, $image));
-    }
-
-    /**
-     * Creates and returns a button to a popup window
-     *
-     * @param html_link $link Subclass of moodle_html_component
-     * @param moodle_popup $popup A moodle_popup object
-     * @param html_image $image An optional image replacing the link text
-     *
-     * @return string HTML fragment
-     */
-    public function link_to_popup($link, $image=null) {
-        $link = clone($link);
-        $link->prepare();
-
-        $this->prepare_event_handlers($link);
-
-        if (empty($link->url)) {
-            throw new coding_exception('Called $OUTPUT->link_to_popup($link) method without $link->url set.');
-        }
-
-        $linkurl = prepare_url($link->url);
-
-        $tagoptions = array(
-                'title' => $link->title,
-                'id' => $link->id,
-                'href' => ($linkurl) ? $linkurl : prepare_url($popup->url),
-                'class' => $link->get_classes_string());
-
-        // Use image if one is given
-        if (!empty($image) && $image instanceof html_image) {
-
-            if (empty($image->alt)) {
-                $image->alt = $link->text;
-            }
-
-            $link->text = $this->image($image);
-
-            if (!empty($link->linktext)) {
-                $link->text = "$link->title &nbsp; $link->text";
-            }
-        }
-
-        return $this->output_tag('a', $tagoptions, $link->text);
-    }
-
-    /**
-     * Creates and returns a spacer image with optional line break.
-     *
-     * @param html_image $image Subclass of moodle_html_component
-     *
-     * @return string HTML fragment
-     */
-    public function spacer($image) {
-        $image = clone($image);
-        $image->prepare();
-        $image->add_class('spacer');
-
-        if (empty($image->src)) {
-            $image->src = $this->old_icon_url('spacer');
-        }
-
-        $output = $this->image($image);
-
-        return $output;
-    }
-
-    /**
-     * Creates and returns an image.
-     *
-     * @param html_image $image Subclass of moodle_html_component
-     *
-     * @return string HTML fragment
-     */
-    public function image($image) {
-        if ($image === false) {
-            return false;
-        }
-
-        $image = clone($image);
-        $image->prepare();
-
-        $this->prepare_event_handlers($image);
-
-        $attributes = array('class' => $image->get_classes_string(),
-                            'src' => prepare_url($image->src),
-                            'alt' => $image->alt,
-                            'style' => $image->style,
-                            'title' => $image->title,
-                            'id' => $image->id);
-
-        if (!empty($image->height) || !empty($image->width)) {
-            $attributes['style'] .= $this->prepare_legacy_width_and_height($image);
-        }
-        return $this->output_empty_tag('img', $attributes);
-    }
-
-    /**
-     * Print the specified user's avatar.
-     *
-     * This method can be used in two ways:
-     * <pre>
-     * // Option 1:
-     * $userpic = new user_picture();
-     * // Set properties of $userpic
-     * $OUTPUT->user_picture($userpic);
-     *
-     * // Option 2: (shortcut for simple cases)
-     * // $user has come from the DB and has fields id, picture, imagealt, firstname and lastname
-     * $OUTPUT->user_picture($user, $COURSE->id);
-     * </pre>
-     *
-     * @param object $userpic Object with at least fields id, picture, imagealt, firstname, lastname
-     *     If any of these are missing, or if a userid is passed, the database is queried. Avoid this
-     *     if at all possible, particularly for reports. It is very bad for performance.
-     *     A user_picture object is a better parameter.
-     * @param int $courseid courseid Used when constructing the link to the user's profile. Required if $userpic
-     *     is not a user_picture object
-     * @return string HTML fragment
-     */
-    public function user_picture($userpic, $courseid=null) {
-        // Instantiate a user_picture object if $user is not already one
-        if (!($userpic instanceof user_picture)) {
-            if (empty($courseid)) {
-                throw new coding_exception('Called $OUTPUT->user_picture with a $user object but no $courseid.');
-            }
-
-            $user = $userpic;
-            $userpic = new user_picture();
-            $userpic->user = $user;
-            $userpic->courseid = $courseid;
-        } else {
-            $userpic = clone($userpic);
-        }
-
-        $userpic->prepare();
-
-        $output = $this->image($userpic->image);
-
-        if (!empty($userpic->url)) {
-            $actions = $userpic->get_actions();
-            if ($userpic->popup && !empty($actions)) {
-                $link = new html_link();
-                $link->url = $userpic->url;
-                $link->text = fullname($userpic->user);
-                $link->title = fullname($userpic->user);
-
-                foreach ($actions as $action) {
-                    $link->add_action($action);
-                }
-                $output = $this->link_to_popup($link, $userpic->image);
-            } else {
-                $output = $this->link(prepare_url($userpic->url), $output);
-            }
-        }
-
-        return $output;
-    }
-
-    /**
-     * Prints the 'Update this Modulename' button that appears on module pages.
-     *
-     * @param string $cmid the course_module id.
-     * @param string $modulename the module name, eg. "forum", "quiz" or "workshop"
-     * @return string the HTML for the button, if this user has permission to edit it, else an empty string.
-     */
-    public function update_module_button($cmid, $modulename) {
-        global $CFG;
-        if (has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_MODULE, $cmid))) {
-            $modulename = get_string('modulename', $modulename);
-            $string = get_string('updatethis', '', $modulename);
-
-            $form = new html_form();
-            $form->url = new moodle_url("$CFG->wwwroot/course/mod.php", array('update' => $cmid, 'return' => true, 'sesskey' => sesskey()));
-            $form->button->text = $string;
-            return $this->button($form);
-        } else {
-            return '';
-        }
-    }
-
-    /**
-     * Prints a "Turn editing on/off" button in a form.
-     * @param moodle_url $url The URL + params to send through when clicking the button
-     * @return string HTML the button
-     */
-    public function edit_button(moodle_url $url) {
-        global $USER;
-        if (!empty($USER->editing)) {
-            $string = get_string('turneditingoff');
-            $edit = '0';
-        } else {
-            $string = get_string('turneditingon');
-            $edit = '1';
-        }
-
-        $form = new html_form();
-        $form->url = $url;
-        $form->url->param('edit', $edit);
-        $form->button->text = $string;
-
-        return $this->button($form);
-    }
-
-    /**
-     * Outputs a HTML nested list
-     *
-     * @param html_list $list A html_list object
-     * @return string HTML structure
-     */
-    public function htmllist($list) {
-        $list = clone($list);
-        $list->prepare();
-
-        $this->prepare_event_handlers($list);
-
-        if ($list->type == 'ordered') {
-            $tag = 'ol';
-        } else if ($list->type == 'unordered') {
-            $tag = 'ul';
-        }
-
-        $output = $this->output_start_tag($tag, array('class' => $list->get_classes_string()));
-
-        foreach ($list->items as $listitem) {
-            if ($listitem instanceof html_list) {
-                $output .= $this->output_start_tag('li', array());
-                $output .= $this->htmllist($listitem);
-                $output .= $this->output_end_tag('li');
-            } else if ($listitem instanceof html_list_item) {
-                $listitem->prepare();
-                $this->prepare_event_handlers($listitem);
-                $output .= $this->output_tag('li', array('class' => $listitem->get_classes_string()), $listitem->value);
-            }
-        }
-
-        return $output . $this->output_end_tag($tag);
-    }
-
-    /**
-     * Prints a simple button to close a window
-     *
-     * @global objec)t
-     * @param string $text The lang string for the button's label (already output from get_string())
-     * @return string|void if $return is true, void otherwise
-     */
-    public function close_window_button($text) {
-        if (empty($text)) {
-            $text = get_string('closewindow');
-        }
-        $closeform = new html_form();
-        $closeform->url = '#';
-        $closeform->button->text = $text;
-        $closeform->button->add_action('click', 'close_window');
-        $closeform->button->prepare();
-        return $this->container($this->button($closeform), 'closewindow');
-    }
-
-    /**
-     * Outputs a <select> menu or a list of radio/checkbox inputs.
-     *
-     * This method is extremely versatile, and can be used to output yes/no menus,
-     * form-enclosed menus with automatic redirects when an option is selected,
-     * descriptive labels and help icons. By default it just outputs a select
-     * menu.
-     *
-     * To add a descriptive label, use moodle_select::set_label($text, $for) or
-     * moodle_select::set_label($label) passing a html_label object
-     *
-     * To add a help icon, use moodle_select::set_help($page, $text, $linktext) or
-     * moodle_select::set_help($helpicon) passing a help_icon object
-     *
-     * If you moodle_select::$rendertype to "radio", it will render radio buttons
-     * instead of a <select> menu, unless $multiple is true, in which case it
-     * will render checkboxes.
-     *
-     * To surround the menu with a form, simply set moodle_select->form as a
-     * valid html_form object. Note that this function will NOT automatically
-     * add a form for non-JS browsers. If you do not set one up, it assumes
-     * that you are providing your own form in some other way.
-     *
-     * You can either call this function with a single moodle_select argument
-     * or, with a list of parameters, in which case those parameters are sent to
-     * the moodle_select constructor.
-     *
-     * @param moodle_select $select a moodle_select that describes
-     *      the select menu you want output.
-     * @return string the HTML for the <select>
-     */
-    public function select($select) {
-        $select = clone($select);
-        $select->prepare();
-
-        $this->prepare_event_handlers($select);
-
-        if (empty($select->id)) {
-            $select->id = 'menu' . str_replace(array('[', ']'), '', $select->name);
-        }
-
-        $attributes = array(
-            'name' => $select->name,
-            'id' => $select->id,
-            'class' => $select->get_classes_string()
-        );
-        if ($select->disabled) {
-            $attributes['disabled'] = 'disabled';
-        }
-        if ($select->tabindex) {
-            $attributes['tabindex'] = $tabindex;
-        }
-
-        if ($select->rendertype == 'menu' && $select->listbox) {
-            if (is_integer($select->listbox)) {
-                $size = $select->listbox;
-            } else {
-                $size = min($select->maxautosize, count($select->options));
-            }
-            $attributes['size'] = $size;
-            if ($select->multiple) {
-                $attributes['multiple'] = 'multiple';
-            }
-        }
-
-        $html = '';
-
-        if (!empty($select->label)) {
-            $html .= $this->label($select->label);
-        }
-
-        if (!empty($select->helpicon) && $select->helpicon instanceof help_icon) {
-            $html .= $this->help_icon($select->helpicon);
-        }
-
-        if ($select->rendertype == 'menu') {
-            $html .= $this->output_start_tag('select', $attributes) . "\n";
-
-            foreach ($select->options as $option) {
-                // $OUTPUT->select_option detects if $option is an option or an optgroup
-                $html .= $this->select_option($option);
-            }
-
-            $html .= $this->output_end_tag('select') . "\n";
-        } else if ($select->rendertype == 'radio') {
-            $currentradio = 0;
-            foreach ($select->options as $option) {
-                $html .= $this->radio($option, $select->name);
-                $currentradio++;
-            }
-        } else if ($select->rendertype == 'checkbox') {
-            $currentcheckbox = 0;
-            foreach ($select->options as $option) {
-                $html .= $this->checkbox($option, $select->name);
-                $currentcheckbox++;
-            }
-        }
-
-        if (!empty($select->form) && $select->form instanceof html_form) {
-            $html = $this->form($select->form, $html);
-        }
-
-        return $html;
-    }
-
-    /**
-     * Outputs a <input type="radio" /> element. Optgroups are ignored, so do not
-     * pass a html_select_optgroup as a param to this function.
-     *
-     * @param html_select_option $option a html_select_option
-     * @return string the HTML for the <input type="radio">
-     */
-    public function radio($option, $name='unnamed') {
-        if ($option instanceof html_select_optgroup) {
-            throw new coding_exception('$OUTPUT->radio($option) does not support a html_select_optgroup object as param.');
-        } else if (!($option instanceof html_select_option)) {
-            throw new coding_exception('$OUTPUT->radio($option) only accepts a html_select_option object as param.');
-        }
-        $option = clone($option);
-        $option->prepare();
-        $option->label->for = $option->id;
-        $this->prepare_event_handlers($option);
-
-        $output = $this->output_start_tag('span', array('class' => "radiogroup $select->name rb$currentradio")) . "\n";
-        $output .= $this->label($option->label);
-
-        if ($option->selected == 'selected') {
-            $option->selected = 'checked';
-        }
-
-        $output .= $this->output_empty_tag('input', array(
-                'type' => 'radio',
-                'value' => $option->value,
-                'name' => $name,
-                'alt' => $option->alt,
-                'id' => $option->id,
-                'class' => $option->get_classes_string(),
-                'checked' => $option->selected));
-
-        $output .= $this->output_end_tag('span');
-
-        return $output;
-    }
-
-    /**
-     * Outputs a <input type="checkbox" /> element. Optgroups are ignored, so do not
-     * pass a html_select_optgroup as a param to this function.
-     *
-     * @param html_select_option $option a html_select_option
-     * @return string the HTML for the <input type="checkbox">
-     */
-    public function checkbox($option, $name='unnamed') {
-        if ($option instanceof html_select_optgroup) {
-            throw new coding_exception('$OUTPUT->checkbox($option) does not support a html_select_optgroup object as param.');
-        } else if (!($option instanceof html_select_option)) {
-            throw new coding_exception('$OUTPUT->checkbox($option) only accepts a html_select_option object as param.');
-        }
-        $option = clone($option);
-        $option->prepare();
-
-        $option->label->for = $option->id;
-        $this->prepare_event_handlers($option);
-
-        $output = $this->output_start_tag('span', array('class' => "checkbox $name")) . "\n";
-
-        if ($option->selected == 'selected') {
-            $option->selected = 'checked';
-        }
-
-        $output .= $this->output_empty_tag('input', array(
-                'type' => 'checkbox',
-                'value' => $option->value,
-                'name' => $name,
-                'id' => $option->id,
-                'alt' => $option->alt,
-                'class' => $option->get_classes_string(),
-                'checked' => $option->selected));
-        $output .= $this->label($option->label);
-
-        $output .= $this->output_end_tag('span');
-
-        return $output;
-    }
-
-    /**
-     * Output an <option> or <optgroup> element. If an optgroup element is detected,
-     * this will recursively output its options as well.
-     *
-     * @param mixed $option a html_select_option or moodle_select_optgroup
-     * @return string the HTML for the <option> or <optgroup>
-     */
-    public function select_option($option) {
-        $option = clone($option);
-        $option->prepare();
-        $this->prepare_event_handlers($option);
-
-        if ($option instanceof html_select_option) {
-            return $this->output_tag('option', array(
-                    'value' => $option->value,
-                    'class' => $option->get_classes_string(),
-                    'selected' => $option->selected), $option->text);
-        } else if ($option instanceof html_select_optgroup) {
-            $output = $this->output_start_tag('optgroup', array('label' => $option->text, 'class' => $option->get_classes_string()));
-            foreach ($option->options as $selectoption) {
-                $output .= $this->select_option($selectoption);
-            }
-            $output .= $this->output_end_tag('optgroup');
-            return $output;
-        }
-    }
-
-    /**
-     * Output an <input type="text"> element
-     *
-     * @param html_field $field a html_field object
-     * @return string the HTML for the <input>
-     */
-    public function textfield($field) {
-        $field = clone($field);
-        $field->prepare();
-        $this->prepare_event_handlers($field);
-        $output = $this->output_start_tag('span', array('class' => "textfield $field->name"));
-        $output .= $this->output_empty_tag('input', array(
-                'type' => 'text',
-                'name' => $field->name,
-                'id' => $field->id,
-                'value' => $field->value,
-                'style' => $field->style,
-                'alt' => $field->alt,
-                'maxlength' => $field->maxlength));
-        $output .= $this->output_end_tag('span');
-        return $output;
-    }
-
-    /**
-     * Outputs a <label> element.
-     * @param html_label $label A html_label object
-     * @return HTML fragment
-     */
-    public function label($label) {
-        $label = clone($label);
-        $label->prepare();
-        $this->prepare_event_handlers($label);
-        return $this->output_tag('label', array('for' => $label->for, 'class' => $label->get_classes_string()), $label->text);
-    }
-
-    /**
-     * Output an error message. By default wraps the error message in <span class="error">.
-     * If the error message is blank, nothing is output.
-     * @param string $message the error message.
-     * @return string the HTML to output.
-     */
-    public function error_text($message) {
-        if (empty($message)) {
-            return '';
-        }
-        return $this->output_tag('span', array('class' => 'error'), $message);
-    }
-
-    /**
-     * Do not call this function directly.
-     *
-     * To terminate the current script with a fatal error, call the {@link print_error}
-     * function, or throw an exception. Doing either of those things will then call this
-     * function to display the error, before terminating the execution.
-     *
-     * @param string $message The message to output
-     * @param string $moreinfourl URL where more info can be found about the error
-     * @param string $link Link for the Continue button
-     * @param array $backtrace The execution backtrace
-     * @param string $debuginfo Debugging information
-     * @param bool $showerrordebugwarning Whether or not to show a debugging warning
-     * @return string the HTML to output.
-     */
-    public function fatal_error($message, $moreinfourl, $link, $backtrace,
-                $debuginfo = null, $showerrordebugwarning = false) {
-
-        $output = '';
-
-        if ($this->has_started()) {
-            $output .= $this->opencontainers->pop_all_but_last();
-        } else {
-            // Header not yet printed
-            @header('HTTP/1.0 404 Not Found');
-            $this->page->set_title(get_string('error'));
-            $output .= $this->header();
-        }
-
-        $message = '<p class="errormessage">' . $message . '</p>'.
-                '<p class="errorcode"><a href="' . $moreinfourl . '">' .
-                get_string('moreinformation') . '</a></p>';
-        $output .= $this->box($message, 'errorbox');
-
-        if (debugging('', DEBUG_DEVELOPER)) {
-            if ($showerrordebugwarning) {
-                $output .= $this->notification('error() is a deprecated function. ' .
-                        'Please call print_error() instead of error()', 'notifytiny');
-            }
-            if (!empty($debuginfo)) {
-                $output .= $this->notification($debuginfo, 'notifytiny');
-            }
-            if (!empty($backtrace)) {
-                $output .= $this->notification('Stack trace: ' .
-                        format_backtrace($backtrace), 'notifytiny');
-            }
-        }
-
-        if (!empty($link)) {
-            $output .= $this->continue_button($link);
-        }
-
-        $output .= $this->footer();
-
-        // Padding to encourage IE to display our error page, rather than its own.
-        $output .= str_repeat(' ', 512);
-
-        return $output;
-    }
-
-    /**
-     * Output a notification (that is, a status message about something that has
-     * just happened).
-     *
-     * @param string $message the message to print out
-     * @param string $classes normally 'notifyproblem' or 'notifysuccess'.
-     * @return string the HTML to output.
-     */
-    public function notification($message, $classes = 'notifyproblem') {
-        return $this->output_tag('div', array('class' =>
-                moodle_renderer_base::prepare_classes($classes)), clean_text($message));
-    }
-
-    /**
-     * Print a continue button that goes to a particular URL.
-     *
-     * @param string|moodle_url $link The url the button goes to.
-     * @return string the HTML to output.
-     */
-    public function continue_button($link) {
-        if (!is_a($link, 'moodle_url')) {
-            $link = new moodle_url($link);
-        }
-        $form = new html_form();
-        $form->url = $link;
-        $form->values = $link->params();
-        $form->button->text = get_string('continue');
-        $form->method = 'get';
-
-        return $this->output_tag('div', array('class' => 'continuebutton') , $this->button($form));
-    }
-
-    /**
-     * Prints a single paging bar to provide access to other pages  (usually in a search)
-     *
-     * @param string|moodle_url $link The url the button goes to.
-     * @return string the HTML to output.
-     */
-    public function paging_bar($pagingbar) {
-        $output = '';
-        $pagingbar = clone($pagingbar);
-        $pagingbar->prepare();
-
-        if ($pagingbar->totalcount > $pagingbar->perpage) {
-            $output .= get_string('page') . ':';
-
-            if (!empty($pagingbar->previouslink)) {
-                $output .= '&nbsp;(' . $this->link($pagingbar->previouslink) . ')&nbsp;';
-            }
-
-            if (!empty($pagingbar->firstlink)) {
-                $output .= '&nbsp;' . $this->link($pagingbar->firstlink) . '&nbsp;...';
-            }
-
-            foreach ($pagingbar->pagelinks as $link) {
-                if ($link instanceof html_link) {
-                    $output .= '&nbsp;&nbsp;' . $this->link($link);
-                } else {
-                    $output .= "&nbsp;&nbsp;$link";
-                }
-            }
-
-            if (!empty($pagingbar->lastlink)) {
-                $output .= '&nbsp;...' . $this->link($pagingbar->lastlink) . '&nbsp;';
-            }
-
-            if (!empty($pagingbar->nextlink)) {
-                $output .= '&nbsp;&nbsp;(' . $this->link($pagingbar->nextlink) . ')';
-            }
-        }
-
-        return $this->output_tag('div', array('class' => 'paging'), $output);
-    }
-
-    /**
-     * Render a HTML table
-     *
-     * @param object $table {@link html_table} instance containing all the information needed
-     * @return string the HTML to output.
-     */
-    public function table(html_table $table) {
-        $table = clone($table);
-        $table->prepare();
-        $attributes = array(
-                'id'            => $table->id,
-                'width'         => $table->width,
-                'summary'       => $table->summary,
-                'cellpadding'   => $table->cellpadding,
-                'cellspacing'   => $table->cellspacing,
-                'class'         => $table->get_classes_string());
-        $output = $this->output_start_tag('table', $attributes) . "\n";
-
-        $countcols = 0;
-
-        if (!empty($table->head)) {
-            $countcols = count($table->head);
-            $output .= $this->output_start_tag('thead', array()) . "\n";
-            $output .= $this->output_start_tag('tr', array()) . "\n";
-            $keys = array_keys($table->head);
-            $lastkey = end($keys);
-            foreach ($table->head as $key => $heading) {
-                $classes = array('header', 'c' . $key);
-                if (isset($table->headspan[$key]) && $table->headspan[$key] > 1) {
-                    $colspan = $table->headspan[$key];
-                    $countcols += $table->headspan[$key] - 1;
-                } else {
-                    $colspan = '';
-                }
-                if ($key == $lastkey) {
-                    $classes[] = 'lastcol';
-                }
-                if (isset($table->colclasses[$key])) {
-                    $classes[] = $table->colclasses[$key];
-                }
-                if ($table->rotateheaders) {
-                    // we need to wrap the heading content
-                    $heading = $this->output_tag('span', '', $heading);
-                }
-                $attributes = array(
-                        'style'     => $table->align[$key] . $table->size[$key] . 'white-space:nowrap;',
-                        'class'     => moodle_renderer_base::prepare_classes($classes),
-                        'scope'     => 'col',
-                        'colspan'   => $colspan);
-                $output .= $this->output_tag('th', $attributes, $heading) . "\n";
-            }
-            $output .= $this->output_end_tag('tr') . "\n";
-            $output .= $this->output_end_tag('thead') . "\n";
-        }
-
-        if (!empty($table->data)) {
-            $oddeven    = 1;
-            $keys       = array_keys($table->data);
-            $lastrowkey = end($keys);
-            $output .= $this->output_start_tag('tbody', array()) . "\n";
-
-            foreach ($table->data as $key => $row) {
-                if (($row === 'hr') && ($countcols)) {
-                    $output .= $this->output_tag('td', array('colspan' => $countcols),
-                                                 $this->output_tag('div', array('class' => 'tabledivider'), '')) . "\n";
-                } else {
-                    // Convert array rows to html_table_rows and cell strings to html_table_cell objects
-                    if (!($row instanceof html_table_row)) {
-                        $newrow = new html_table_row();
-
-                        foreach ($row as $unused => $item) {
-                            $cell = new html_table_cell();
-                            $cell->text = $item;
-                            $newrow->cells[] = $cell;
-                        }
-                        $row = $newrow;
-                    }
-
-                    $oddeven = $oddeven ? 0 : 1;
-                    if (isset($table->rowclasses[$key])) {
-                        $row->add_classes(array_unique(moodle_html_component::clean_classes($table->rowclasses[$key])));
-                    }
-
-                    $row->add_class('r' . $oddeven);
-                    if ($key == $lastrowkey) {
-                        $row->add_class('lastrow');
-                    }
-
-                    $output .= $this->output_start_tag('tr', array('class' => $row->get_classes_string(), 'style' => $row->style, 'id' => $row->id)) . "\n";
-                    $keys2 = array_keys($row->cells);
-                    $lastkey = end($keys2);
-
-                    foreach ($row->cells as $key => $cell) {
-                        if (isset($table->colclasses[$key])) {
-                            $cell->add_classes(array_unique(moodle_html_component::clean_classes($table->colclasses[$key])));
-                        }
-
-                        $cell->add_classes('cell');
-                        $cell->add_classes('c' . $key);
-                        if ($key == $lastkey) {
-                            $cell->add_classes('lastcol');
-                        }
-                        $tdstyle = '';
-                        $tdstyle .= isset($table->align[$key]) ? $table->align[$key] : '';
-                        $tdstyle .= isset($table->size[$key]) ? $table->size[$key] : '';
-                        $tdstyle .= isset($table->wrap[$key]) ? $table->wrap[$key] : '';
-                        $tdattributes = array(
-                                'style' => $tdstyle . $cell->style,
-                                'colspan' => $cell->colspan,
-                                'rowspan' => $cell->rowspan,
-                                'id' => $cell->id,
-                                'class' => $cell->get_classes_string(),
-                                'abbr' => $cell->abbr,
-                                'scope' => $cell->scope);
-
-                        $output .= $this->output_tag('td', $tdattributes, $cell->text) . "\n";
-                    }
-                }
-                $output .= $this->output_end_tag('tr') . "\n";
-            }
-            $output .= $this->output_end_tag('tbody') . "\n";
-        }
-        $output .= $this->output_end_tag('table') . "\n";
-
-        if ($table->rotateheaders && can_use_rotated_text()) {
-            $this->page->requires->yui_lib('event');
-            $this->page->requires->js('course/report/progress/textrotate.js');
-        }
-
-        return $output;
-    }
-
-    /**
-     * Output the place a skip link goes to.
-     * @param string $id The target name from the corresponding $PAGE->requires->skip_link_to($target) call.
-     * @return string the HTML to output.
-     */
-    public function skip_link_target($id = '') {
-        return $this->output_tag('span', array('id' => $id), '');
-    }
-
-    /**
-     * Outputs a heading
-     * @param string $text The text of the heading
-     * @param int $level The level of importance of the heading. Defaulting to 2
-     * @param string $classes A space-separated list of CSS classes
-     * @param string $id An optional ID
-     * @return string the HTML to output.
-     */
-    public function heading($text, $level = 2, $classes = 'main', $id = '') {
-        $level = (integer) $level;
-        if ($level < 1 or $level > 6) {
-            throw new coding_exception('Heading level must be an integer between 1 and 6.');
-        }
-        return $this->output_tag('h' . $level,
-                array('id' => $id, 'class' => moodle_renderer_base::prepare_classes($classes)), $text);
-    }
-
-    /**
-     * Outputs a box.
-     * @param string $contents The contents of the box
-     * @param string $classes A space-separated list of CSS classes
-     * @param string $id An optional ID
-     * @return string the HTML to output.
-     */
-    public function box($contents, $classes = 'generalbox', $id = '') {
-        return $this->box_start($classes, $id) . $contents . $this->box_end();
-    }
-
-    /**
-     * Outputs the opening section of a box.
-     * @param string $classes A space-separated list of CSS classes
-     * @param string $id An optional ID
-     * @return string the HTML to output.
-     */
-    public function box_start($classes = 'generalbox', $id = '') {
-        $this->opencontainers->push('box', $this->output_end_tag('div'));
-        return $this->output_start_tag('div', array('id' => $id,
-                'class' => 'box ' . moodle_renderer_base::prepare_classes($classes)));
-    }
-
-    /**
-     * Outputs the closing section of a box.
-     * @return string the HTML to output.
-     */
-    public function box_end() {
-        return $this->opencontainers->pop('box');
-    }
-
-    /**
-     * Outputs a container.
-     * @param string $contents The contents of the box
-     * @param string $classes A space-separated list of CSS classes
-     * @param string $id An optional ID
-     * @return string the HTML to output.
-     */
-    public function container($contents, $classes = '', $id = '') {
-        return $this->container_start($classes, $id) . $contents . $this->container_end();
-    }
-
-    /**
-     * Outputs the opening section of a container.
-     * @param string $classes A space-separated list of CSS classes
-     * @param string $id An optional ID
-     * @return string the HTML to output.
-     */
-    public function container_start($classes = '', $id = '') {
-        $this->opencontainers->push('container', $this->output_end_tag('div'));
-        return $this->output_start_tag('div', array('id' => $id,
-                'class' => moodle_renderer_base::prepare_classes($classes)));
-    }
-
-    /**
-     * Outputs the closing section of a container.
-     * @return string the HTML to output.
-     */
-    public function container_end() {
-        return $this->opencontainers->pop('container');
-    }
-}
-
-/// COMPONENTS
-
-/**
- * Base class for classes representing HTML elements, like moodle_select.
- *
- * Handles the id and class attributes.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class moodle_html_component {
-    /**
-     * @var string value to use for the id attribute of this HTML tag.
-     */
-    public $id = '';
-    /**
-     * @var string $alt value to use for the alt attribute of this HTML tag.
-     */
-    public $alt = '';
-    /**
-     * @var string $style value to use for the style attribute of this HTML tag.
-     */
-    public $style = '';
-    /**
-     * @var array class names to add to this HTML element.
-     */
-    public $classes = array();
-    /**
-     * @var string $title The title attributes applicable to any XHTML element
-     */
-    public $title = '';
-    /**
-     * An optional array of component_action objects handling the action part of this component.
-     * @var array $actions
-     */
-    protected $actions = array();
-    /**
-     * This array of generated ids is kept static to avoid id collisions
-     * @var array $generated_ids
-     */
-    public static $generated_ids = array();
-
-    /**
-     * Ensure some class names are an array.
-     * @param mixed $classes either an array of class names or a space-separated
-     *      string containing class names.
-     * @return array the class names as an array.
-     */
-    public static function clean_classes($classes) {
-        if (is_array($classes)) {
-            return $classes;
-        } else {
-            return explode(' ', trim($classes));
-        }
-    }
-
-    /**
-     * Set the class name array.
-     * @param mixed $classes either an array of class names or a space-separated
-     *      string containing class names.
-     * @return void
-     */
-    public function set_classes($classes) {
-        $this->classes = self::clean_classes($classes);
-    }
-
-    /**
-     * Add a class name to the class names array.
-     * @param string $class the new class name to add.
-     * @return void
-     */
-    public function add_class($class) {
-        $this->classes[] = $class;
-    }
-
-    /**
-     * Add a whole lot of class names to the class names array.
-     * @param mixed $classes either an array of class names or a space-separated
-     *      string containing class names.
-     * @return void
-     */
-    public function add_classes($classes) {
-        $this->classes += self::clean_classes($classes);
-    }
-
-    /**
-     * Get the class names as a string.
-     * @return string the class names as a space-separated string. Ready to be put in the class="" attribute.
-     */
-    public function get_classes_string() {
-        return implode(' ', $this->classes);
-    }
-
-    /**
-     * Perform any cleanup or final processing that should be done before an
-     * instance of this class is output.
-     * @return void
-     */
-    public function prepare() {
-        $this->classes = array_unique(self::clean_classes($this->classes));
-    }
-
-    /**
-     * This checks developer do not try to assign a property directly
-     * if we have a setter for it. Otherwise, the property is set as expected.
-     * @param string $name The name of the variable to set
-     * @param mixed $value The value to assign to the variable
-     * @return void
-     */
-    public function __set($name, $value) {
-        if ($name == 'class') {
-            debugging('this way of setting css class has been deprecated. use set_classes() method instead.');
-            $this->set_classes($value);
-        } else {
-            $this->{$name} = $value;
-        }
-    }
-
-    /**
-     * Adds a JS action to this component.
-     * Note: the JS function you write must have only two arguments: (string)event and (object|array)args
-     * If you want to add an instantiated component_action (or one of its subclasses), give the object as the only parameter
-     *
-     * @param mixed  $event a DOM event (click, mouseover etc.) or a component_action object
-     * @param string $jsfunction The name of the JS function to call. required if argument 1 is a string (event)
-     * @param array  $jsfunctionargs An optional array of JS arguments to pass to the function
-     */
-    public function add_action($event, $jsfunction=null, $jsfunctionargs=array()) {
-        if (empty($this->id)) {
-            $this->generate_id();
-        }
-
-        if ($event instanceof component_action) {
-            $this->actions[] = $event;
-        } else {
-            if (empty($jsfunction)) {
-                throw new coding_exception('moodle_html_component::add_action requires a JS function argument if the first argument is a string event');
-            }
-            $this->actions[] = new component_action($event, $jsfunction, $jsfunctionargs);
-        }
-    }
-
-    /**
-     * Internal method for generating a unique ID for the purpose of event handlers.
-     */
-    protected function generate_id() {
-        // Generate an id that is not already used.
-        do {
-            $newid = get_class($this) . '-' . substr(sha1(microtime() * rand(0, 500)), 0, 6);
-        } while (in_array($this->id, moodle_html_component::$generated_ids));
-        $this->id = $newid;
-        moodle_html_component::$generated_ids[] = $newid;
-    }
-
-    /**
-     * Returns the array of component_actions.
-     * @return array Component actions
-     */
-    public function get_actions() {
-        return $this->actions;
-    }
-
-    /**
-     * Adds a descriptive label to the component.
-     *
-     * This can be used in two ways:
-     *
-     * <pre>
-     * $component->set_label($elementlabel, $elementid);
-     * // OR
-     * $label = new html_label();
-     * $label->for = $elementid;
-     * $label->text = $elementlabel;
-     * $component->set_label($label);
-     * </pre>
-     *
-     * Use the second form when you need to add additional HTML attributes
-     * to the label and/or JS actions.
-     *
-     * @param mixed $text Either the text of the label or a html_label object
-     * @param text  $for The value of the "for" attribute (the associated element's id)
-     * @return void
-     */
-    public function set_label($text, $for=null) {
-        if ($text instanceof html_label) {
-            $this->label = $text;
-        } else if (!empty($text)) {
-            $this->label = new html_label();
-            $this->label->for = $for;
-            if (empty($for) && !empty($this->id)) {
-                $this->label->for = $this->id;
-            }
-            $this->label->text = $text;
-        }
-    }
-}
-
-
-/**
- * This class hold all the information required to describe a <select> menu that
- * will be printed by {@link moodle_core_renderer::select()}. (Or by an overridden
- * version of that method in a subclass.)
- *
- * This component can also hold enough metadata to be used as a popup form. It just
- * needs a bit more setting up than for a simple menu. See the shortcut methods for
- * developer-friendly usage.
- *
- * All the fields that are not set by the constructor have sensible defaults, so
- * you only need to set the properties where you want non-default behaviour.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class moodle_select extends moodle_html_component {
-    /**
-     * The moodle_select object parses an array of options into component objects
-     * @see nested attribute
-     * @var mixed $options the choices to show in the menu. An array $value => $display, of html_select_option or of html_select_optgroup objects.
-     */
-    public $options;
-    /**
-     * @var string $name the name of this form control. That is, the name of the GET/POST
-     * variable that will be set if this select is submitted as part of a form.
-     */
-    public $name;
-    /**
-     * @var mixed $label The label for that component. String or html_label object
-     */
-    public $label;
-    /**
-     * @var string $selectedvalue the option to select initially. Should match one
-     * of the $options array keys. Default none.
-     */
-    public $selectedvalue;
-    /**
-     * Defaults to get_string('choosedots').
-     * Set this to '' if you do not want a 'nothing is selected' option.
-     * This is ignored if the rendertype is 'radio' or 'checkbox'
-     * @var string The label for the 'nothing is selected' option.
-     */
-    public $nothinglabel = null;
-    /**
-     * @var string The value returned by the 'nothing is selected' option. Defaults to 0.
-     */
-    public $nothingvalue = 0;
-    /**
-     * @var boolean set this to true if you want the control to appear disabled.
-     */
-    public $disabled = false;
-    /**
-     * @var integer if non-zero, sets the tabindex attribute on the <select> element. Default 0.
-     */
-    public $tabindex = 0;
-    /**
-     * @var mixed Defaults to false, which means display the select as a dropdown menu.
-     * If true, display this select as a list box whose size is chosen automatically.
-     * If an integer, display as list box of that size.
-     */
-    public $listbox = false;
-    /**
-     * @var integer if you are using $listbox === true to get an automatically
-     * sized list box, the size of the list box will be the number of options,
-     * or this number, whichever is smaller.
-     */
-    public $maxautosize = 10;
-    /**
-     * @var boolean if true, allow multiple selection. Only used if $listbox is true, or if
-     *      the select is to be output as checkboxes.
-     */
-    public $multiple = false;
-    /**
-     * Another way to use nested menu is to prefix optgroup labels with -- and end the optgroup with --
-     * Leave this setting to false if you are using the latter method.
-     * @var boolean $nested if true, uses $options' keys as option headings (optgroup)
-     */
-    public $nested = false;
-    /**
-     * @var html_form $form An optional html_form component
-     */
-    public $form;
-    /**
-     * @var help_icon $form An optional help_icon component
-     */
-    public $helpicon;
-    /**
-     * @var boolean $rendertype How the select element should be rendered: menu or radio (checkbox is just radio + multiple)
-     */
-    public $rendertype = 'menu';
-
-    /**
-     * @see moodle_html_component::prepare()
-     * @return void
-     */
-    public function prepare() {
-        global $CFG;
-
-        // name may contain [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading
-        if (empty($this->id)) {
-            $this->id = 'menu' . str_replace(array('[', ']'), '', $this->name);
-        }
-
-        if (empty($this->classes)) {
-            $this->set_classes(array('menu' . str_replace(array('[', ']'), '', $this->name)));
-        }
-
-        if (is_null($this->nothinglabel)) {
-            $this->nothinglabel = get_string('choosedots');
-        }
-
-        if (!empty($this->label) && !($this->label instanceof html_label)) {
-            $label = new html_label();
-            $label->text = $this->label;
-            $label->for = $this->name;
-            $this->label = $label;
-        }
-
-        $this->add_class('select');
-
-        $this->initialise_options();
-        parent::prepare();
-    }
-
-    /**
-     * This is a shortcut for making a simple select menu. It lets you specify
-     * the options, name and selected option in one line of code.
-     * @param array $options used to initialise {@link $options}.
-     * @param string $name used to initialise {@link $name}.
-     * @param string $selected  used to initialise {@link $selected}.
-     * @return moodle_select A moodle_select object with the three common fields initialised.
-     */
-    public static function make($options, $name, $selected = '') {
-        $menu = new moodle_select();
-        $menu->options = $options;
-        $menu->name = $name;
-        $menu->selectedvalue = $selected;
-        return $menu;
-    }
-
-    /**
-     * This is a shortcut for making a yes/no select menu.
-     * @param string $name used to initialise {@link $name}.
-     * @param string $selected  used to initialise {@link $selected}.
-     * @return moodle_select A menu initialised with yes/no options.
-     */
-    public static function make_yes_no($name, $selected) {
-        return self::make(array(0 => get_string('no'), 1 => get_string('yes')), $name, $selected);
-    }
-
-    /**
-     * This is a shortcut for making an hour selector menu.
-     * @param string $type The type of selector (years, months, days, hours, minutes)
-     * @param string $name fieldname
-     * @param int $currenttime A default timestamp in GMT
-     * @param int $step minute spacing
-     * @return moodle_select A menu initialised with hour options.
-     */
-    public static function make_time_selector($type, $name, $currenttime=0, $step=5) {
-
-        if (!$currenttime) {
-            $currenttime = time();
-        }
-        $currentdate = usergetdate($currenttime);
-        $userdatetype = $type;
-
-        switch ($type) {
-            case 'years':
-                for ($i=1970; $i<=2020; $i++) {
-                    $timeunits[$i] = $i;
-                }
-                $userdatetype = 'year';
-                break;
-            case 'months':
-                for ($i=1; $i<=12; $i++) {
-                    $timeunits[$i] = userdate(gmmktime(12,0,0,$i,15,2000), "%B");
-                }
-                $userdatetype = 'month';
-                break;
-            case 'days':
-                for ($i=1; $i<=31; $i++) {
-                    $timeunits[$i] = $i;
-                }
-                $userdatetype = 'mday';
-                break;
-            case 'hours':
-                for ($i=0; $i<=23; $i++) {
-                    $timeunits[$i] = sprintf("%02d",$i);
-                }
-                break;
-            case 'minutes':
-                if ($step != 1) {
-                    $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
-                }
-
-                for ($i=0; $i<=59; $i+=$step) {
-                    $timeunits[$i] = sprintf("%02d",$i);
-                }
-                break;
-            default:
-                throw new coding_exception("Time type $type is not supported by moodle_select::make_time_selector().");
-        }
-
-        $timerselector = self::make($timeunits, $name, $currentdate[$userdatetype]);
-        $timerselector->label = new html_label();
-        $timerselector->label->text = get_string(substr($type, -1), 'form');
-        $timerselector->label->for = "menu$timerselector->name";
-        $timerselector->label->add_class('accesshide');
-        $timerselector->nothinglabel = '';
-
-        return $timerselector;
-    }
-
-    /**
-     * Given an associative array of type => fieldname and an optional timestamp,
-     * returns an array of moodle_select components representing date/time selectors.
-     * @param array $selectors Arrays of type => fieldname. Selectors will be returned in the order of the types given
-     * @param int $currenttime A UNIX timestamp
-     * @param int $step minute spacing
-     * @return array Instantiated date/time selectors
-     */
-    public function make_time_selectors($selectors, $currenttime=0, $step=5) {
-        $selects = array();
-        foreach ($selectors as $type => $name) {
-            $selects[] = moodle_select::make_time_selector($type, $name, $currenttime, $step);
-        }
-        return $selects;
-    }
-
-    /**
-     * This is a shortcut for making a select popup form.
-     * @param mixed $baseurl The target URL, string or moodle_url
-     * @param string $name The variable which this select's options are changing in the URL
-     * @param array $options A list of value-label pairs for the popup list
-     * @param string $formid id for the control. Must be unique on the page. Used in the HTML.
-     * @param string $selected The option that is initially selected
-     * @return moodle_select A menu initialised as a popup form.
-     */
-    public function make_popup_form($baseurl, $name, $options, $formid, $selected=null) {
-        global $CFG;
-
-        $selectedurl = null;
-
-        if (!($baseurl instanceof moodle_url)) {
-            $baseurl = new moodle_url($baseurl);
-        }
-
-        if (!empty($selected)) {
-            $selectedurl = $baseurl->out(false, array($name => $selected), false);
-        }
-
-        if (!($baseurl instanceof moodle_url)) {
-            $baseurl = new moodle_url($baseurl);
-        }
-
-        // Replace real value by formatted URLs
-        foreach ($options as $value => $label) {
-            $options[$baseurl->out(false, array($name => $value), false)] = $label;
-            unset($options[$value]);
-        }
-
-        $select = self::make($options, 'jump', $selectedurl);
-
-        $select->form = new html_form();
-        $select->form->id = $formid;
-        $select->form->method = 'get';
-        $select->form->add_class('popupform');
-        $select->form->url = new moodle_url($CFG->wwwroot . '/course/jumpto.php', array('sesskey' => sesskey()));
-        $select->form->button->text = get_string('go');
-
-        $select->id = $formid . '_jump';
-
-        $select->add_action('change', 'submit_form_by_id', array('id' => $formid, 'selectid' => $select->id));
-
-        return $select;
-    }
-
-    /**
-     * Override the URLs of the default popup_form, which only supports one base URL
-     * @param array $options value=>label pairs representing select options
-     * @return void
-     */
-    public function override_option_values($options) {
-        global $PAGE;
-
-        $this->initialise_options();
-
-        reset($options);
-
-        foreach ($this->options as $optkey => $optgroup) {
-            if ($optgroup instanceof html_select_optgroup) {
-                foreach ($optgroup->options as $key => $option) {
-                    next($options);
-                    $this->options[$optkey]->options[$key]->value = key($options);
-                    
-                    $optionurl = new moodle_url(key($options));
-
-                    if ($optionurl->compare($PAGE->url, URL_MATCH_PARAMS)) {
-                        $this->options[$optkey]->options[$key]->selected = 'selected';
-                    }
-                }
-                next($options);
-            } else if ($optgroup instanceof html_select_option) {
-                next($options);
-                $this->options[$optkey]->value = key($options);
-                $optionurl = new moodle_url(key($options));
-
-                if ($optionurl->compare($PAGE->url, URL_MATCH_PARAMS)) {
-                    $this->options[$optkey]->selected = 'selected';
-                }
-            }
-        }
-    }
-
-    /**
-     * Adds a help icon next to the select menu.
-     *
-     * This can be used in two ways:
-     *
-     * <pre>
-     * $select->set_help_icon($page, $text, $linktext);
-     * // OR
-     * $helpicon = new help_icon();
-     * $helpicon->page = $page;
-     * $helpicon->text = $text;
-     * $helpicon->linktext = $linktext;
-     * $select->set_help_icon($helpicon);
-     * </pre>
-     *
-     * Use the second form when you need to add additional HTML attributes
-     * to the label and/or JS actions.
-     *
-     * @param mixed $page Either the keyword that defines a help page or a help_icon object
-     * @param text  $text The text of the help icon
-     * @param boolean $linktext Whether or not to show text next to the icon
-     * @return void
-     */
-    public function set_help_icon($page, $text, $linktext=false) {
-        if ($page instanceof help_icon) {
-            $this->helpicon = $page;
-        } else if (!empty($page)) {
-            $this->helpicon = new help_icon();
-            $this->helpicon->page = $page;
-            $this->helpicon->text = $text;
-            $this->helpicon->linktext = $linktext;
-        }
-    }
-
-    /**
-     * Parses the $options array and instantiates html_select_option objects in
-     * the place of the original value => label pairs. This is useful for when you
-     * need to setup extra html attributes and actions on individual options before
-     * the component is sent to the renderer
-     * @return void;
-     */
-    public function initialise_options() {
-        // If options are already instantiated objects, stop here
-        $firstoption = reset($this->options);
-        if ($firstoption instanceof html_select_option || $firstoption instanceof html_select_optgroup) {
-            return;
-        }
-
-        if ($this->rendertype == 'radio' && $this->multiple) {
-            $this->rendertype = 'checkbox';
-        }
-
-        // If nested is on, or if radio/checkbox rendertype is set, remove the default Choose option
-        if ($this->nested || $this->rendertype == 'radio' || $this->rendertype == 'checkbox') {
-            $this->nothinglabel = '';
-        }
-
-        $options = $this->options;
-
-        $this->options = array();
-
-        if ($this->nested && $this->rendertype != 'menu') {
-            throw new coding_exception('moodle_select cannot render nested options as radio buttons or checkboxes.');
-        } else if ($this->nested) {
-            foreach ($options as $section => $values) {
-                $optgroup = new html_select_optgroup();
-                $optgroup->text = $section;
-
-                foreach ($values as $value => $display) {
-                    $option = new html_select_option();
-                    $option->value = s($value);
-                    $option->text = $display;
-                    if ($display === '') {
-                        $option->text = $value;
-                    }
-
-                    if ((string) $value == (string) $this->selectedvalue ||
-                            (is_array($this->selectedvalue) && in_array($value, $this->selectedvalue))) {
-                        $option->selected = 'selected';
-                    }
-
-                    $optgroup->options[] = $option;
-                }
-
-                $this->options[] = $optgroup;
-            }
-        } else {
-            $inoptgroup = false;
-            $optgroup = false;
-
-            foreach ($options as $value => $display) {
-                if ($display == '--') { /// we are ending previous optgroup
-                    // $this->options[] = $optgroup;
-                    $inoptgroup = false;
-                    continue;
-                } else if (substr($display,0,2) == '--') { /// we are starting a new optgroup
-                    if (!empty($optgroup->options)) {
-                        $this->options[] = $optgroup;
-                    }
-
-                    $optgroup = new html_select_optgroup();
-                    $optgroup->text = substr($display,2); // stripping the --
-
-                    $inoptgroup = true; /// everything following will be in an optgroup
-                    continue;
-
-                } else {
-                    // Add $nothing option if there are not optgroups
-                    if ($this->nothinglabel && empty($this->options[0]) && !$inoptgroup) {
-                        $nothingoption = new html_select_option();
-                        $nothingoption->value = 0;
-                        if (!empty($this->nothingvalue)) {
-                            $nothingoption->value = $this->nothingvalue;
-                        }
-                        $nothingoption->text = $this->nothinglabel;
-                        $this->options = array($nothingoption) + $this->options;
-                    }
-
-                    $option = new html_select_option();
-                    $option->text = $display;
-
-                    if ($display === '') {
-                        $option->text = $value;
-                    }
-
-                    if ((string) $value == (string) $this->selectedvalue ||
-                            (is_array($this->selectedvalue) && in_array($value, $this->selectedvalue))) {
-                        $option->selected = 'selected';
-                    }
-
-                    $option->value = s($value);
-
-                    if ($inoptgroup) {
-                        $optgroup->options[] = $option;
-                    } else {
-                        $this->options[] = $option;
-                    }
-                }
-            }
-
-            if ($optgroup) {
-                $this->options[] = $optgroup;
-            }
-        }
-    }
-}
-
-/**
- * This class represents a label element
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class html_label extends moodle_html_component {
-    /**
-     * @var string $text The text to display in the label
-     */
-    public $text;
-    /**
-     * @var string $for The name of the form field this label is associated with
-     */
-    public $for;
-
-    /**
-     * @see moodle_html_component::prepare()
-     * @return void
-     */
-    public function prepare() {
-        if (empty($this->text)) {
-            throw new coding_exception('html_label must have a $text value.');
-        }
-        parent::prepare();
-    }
-}
-
-/**
- * This class represents a select option element
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class html_select_option extends moodle_html_component {
-    /**
-     * @var string $value The value of this option (will be sent with form)
-     */
-    public $value;
-    /**
-     * @var string $text The display value of the option
-     */
-    public $text;
-    /**
-     * @var boolean $selected Whether or not this option is selected
-     */
-    public $selected = false;
-    /**
-     * @var mixed $label The label for that component. String or html_label object
-     */
-    public $label;
-
-    public function __construct() {
-        $this->label = new html_label();
-    }
-
-    /**
-     * @see moodle_html_component::prepare()
-     * @return void
-     */
-    public function prepare() {
-        if (empty($this->text)) {
-            throw new coding_exception('html_select_option requires a $text value.');
-        }
-
-        if (empty($this->label->text)) {
-            $this->set_label($this->text);
-        } else if (!($this->label instanceof html_label)) {
-            $this->set_label($this->label);
-        }
-        if (empty($this->id)) {
-            $this->generate_id();
-        }
-
-        parent::prepare();
-    }
-
-    /**
-     * Shortcut for making a checkbox-ready option
-     * @param string $value The value of the checkbox
-     * @param boolean $checked
-     * @param string $label
-     * @param string $alt
-     * @return html_select_option A component ready for $OUTPUT->checkbox()
-     */
-    public function make_checkbox($value, $checked, $label='', $alt='') {
-        $checkbox = new html_select_option();
-        $checkbox->value = $value;
-        $checkbox->selected = $checked;
-        $checkbox->text = $label;
-        $checkbox->label->text = $label;
-        $checkbox->alt = $alt;
-        return $checkbox;
-    }
-}
-
-/**
- * This class represents a select optgroup element
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class html_select_optgroup extends moodle_html_component {
-    /**
-     * @var string $text The display value of the optgroup
-     */
-    public $text;
-    /**
-     * @var array $options An array of html_select_option objects
-     */
-    public $options = array();
-
-    public function prepare() {
-        if (empty($this->text)) {
-            throw new coding_exception('html_select_optgroup requires a $text value.');
-        }
-        if (empty($this->options)) {
-            throw new coding_exception('html_select_optgroup requires at least one html_select_option object');
-        }
-        parent::prepare();
-    }
-}
-
-/**
- * This class represents an input field
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class html_field extends moodle_html_component {
-    /**
-     * @var string $name The name attribute of the field
-     */
-    public $name;
-    /**
-     * @var string $value The value attribute of the field
-     */
-    public $value;
-    /**
-     * @var string $type The type attribute of the field (text, submit, checkbox etc)
-     */
-    public $type;
-    /**
-     * @var string $maxlength The maxlength attribute of the field (only applies to text type)
-     */
-    public $maxlength;
-    /**
-     * @var mixed $label The label for that component. String or html_label object
-     */
-    public $label;
-
-    public function __construct() {
-        $this->label = new html_label();
-    }
-
-    /**
-     * @see moodle_html_component::prepare()
-     * @return void
-     */
-    public function prepare() {
-        if (empty($this->style)) {
-            $this->style = 'width: 4em;';
-        }
-        if (empty($this->id)) {
-            $this->generate_id();
-        }
-        parent::prepare();
-    }
-
-    /**
-     * Shortcut for creating a text input component.
-     * @param string $name    The name of the text field
-     * @param string $value   The value of the text field
-     * @param string $alt     The info to be inserted in the alt tag
-     * @param int $maxlength Sets the maxlength attribute of the field. Not set by default
-     * @return html_field The field component
-     */
-    public static function make_text($name='unnamed', $value, $alt, $maxlength=0) {
-        $field = new html_field();
-        if (empty($alt)) {
-            $alt = get_string('textfield');
-        }
-        $field->type = 'text';
-        $field->name = $name;
-        $field->value = $value;
-        $field->alt = $alt;
-        $field->maxlength = $maxlength;
-        return $field;
-    }
-}
-
-/**
- * This class represents how a block appears on a page.
- *
- * During output, each block instance is asked to return a block_contents object,
- * those are then passed to the $OUTPUT->block function for display.
- *
- * {@link $contents} should probably be generated using a moodle_block_..._renderer.
- *
- * Other block-like things that need to appear on the page, for example the
- * add new block UI, are also represented as block_contents objects.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class block_contents extends moodle_html_component {
-    /** @var int used to set $skipid. */
-    protected static $idcounter = 1;
-
-    const NOT_HIDEABLE = 0;
-    const VISIBLE = 1;
-    const HIDDEN = 2;
-
-    /**
-     * @param integer $skipid All the blocks (or things that look like blocks)
-     * printed on a page are given a unique number that can be used to construct
-     * id="" attributes. This is set automatically be the {@link prepare()} method.
-     * Do not try to set it manually.
-     */
-    public $skipid;
-
-    /**
-     * @var integer If this is the contents of a real block, this should be set to
-     * the block_instance.id. Otherwise this should be set to 0.
-     */
-    public $blockinstanceid = 0;
-
-    /**
-     * @var integer if this is a real block instance, and there is a corresponding
-     * block_position.id for the block on this page, this should be set to that id.
-     * Otherwise it should be 0.
-     */
-    public $blockpositionid = 0;
-
-    /**
-     * @param array $attributes an array of attribute => value pairs that are put on the
-     * outer div of this block. {@link $id} and {@link $classes} attributes should be set separately.
-     */
-    public $attributes = array();
-
-    /**
-     * @param string $title The title of this block. If this came from user input,
-     * it should already have had format_string() processing done on it. This will
-     * be output inside <h2> tags. Please do not cause invalid XHTML.
-     */
-    public $title = '';
-
-    /**
-     * @param string $content HTML for the content
-     */
-    public $content = '';
-
-    /**
-     * @param array $list an alternative to $content, it you want a list of things with optional icons.
-     */
-    public $footer = '';
-
-    /**
-     * Any small print that should appear under the block to explain to the
-     * teacher about the block, for example 'This is a sticky block that was
-     * added in the system context.'
-     * @var string
-     */
-    public $annotation = '';
-
-    /**
-     * @var integer one of the constants NOT_HIDEABLE, VISIBLE, HIDDEN. Whether
-     * the user can toggle whether this block is visible.
-     */
-    public $collapsible = self::NOT_HIDEABLE;
-
-    /**
-     * A (possibly empty) array of editing controls. Each element of this array
-     * should be an array('url' => $url, 'icon' => $icon, 'caption' => $caption).
-     * $icon is the icon name. Fed to $OUTPUT->old_icon_url.
-     * @var array
-     */
-    public $controls = array();
-
-    /**
-     * @see moodle_html_component::prepare()
-     * @return void
-     */
-    public function prepare() {
-        $this->skipid = self::$idcounter;
-        self::$idcounter += 1;
-        $this->add_class('sideblock');
-        if (empty($this->blockinstanceid) || !strip_tags($this->title)) {
-            $this->collapsible = self::NOT_HIDEABLE;
-        }
-        if ($this->collapsible == self::HIDDEN) {
-            $this->add_class('hidden');
-        }
-        if (!empty($this->controls)) {
-            $this->add_class('block_with_controls');
-        }
-        parent::prepare();
-    }
-}
-
-
-/**
- * This class represents a target for where a block can go when it is being moved.
- *
- * This needs to be rendered as a form with the given hidden from fields, and
- * clicking anywhere in the form should submit it. The form action should be
- * $PAGE->url.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class block_move_target extends moodle_html_component {
-    /**
-     * List of hidden form fields.
-     * @var array
-     */
-    public $url = array();
-    /**
-     * List of hidden form fields.
-     * @var array
-     */
-    public $text = '';
-}
-
-
-/**
- * Holds all the information required to render a <table> by
- * {@see moodle_core_renderer::table()} or by an overridden version of that
- * method in a subclass.
- *
- * Example of usage:
- * $t = new html_table();
- * ... // set various properties of the object $t as described below
- * echo $OUTPUT->table($t);
- *
- * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class html_table extends moodle_html_component {
-    /**
-     * @var array of headings. The n-th array item is used as a heading of the n-th column.
-     *
-     * Example of usage:
-     * $t->head = array('Student', 'Grade');
-     */
-    public $head;
-    /**
-     * @var array can be used to make a heading span multiple columns
-     *
-     * Example of usage:
-     * $t->headspan = array(2,1);
-     *
-     * In this example, {@see html_table:$data} is supposed to have three columns. For the first two columns,
-     * the same heading is used. Therefore, {@see html_table::$head} should consist of two items.
-     */
-    public $headspan;
-    /**
-     * @var array of column alignments. The value is used as CSS 'text-align' property. Therefore, possible
-     * values are 'left', 'right', 'center' and 'justify'. Specify 'right' or 'left' from the perspective
-     * of a left-to-right (LTR) language. For RTL, the values are flipped automatically.
-     *
-     * Examples of usage:
-     * $t->align = array(null, 'right');
-     * or
-     * $t->align[1] = 'right';
-     *
-     */
-    public $align;
-    /**
-     * @var array of column sizes. The value is used as CSS 'size' property.
-     *
-     * Examples of usage:
-     * $t->size = array('50%', '50%');
-     * or
-     * $t->size[1] = '120px';
-     */
-    public $size;
-    /**
-     * @var array of wrapping information. The only possible value is 'nowrap' that sets the
-     * CSS property 'white-space' to the value 'nowrap' in the given column.
-     *
-     * Example of usage:
-     * $t->wrap = array(null, 'nowrap');
-     */
-    public $wrap;
-    /**
-     * @var array of arrays or html_table_row objects containing the data. Alternatively, if you have
-     * $head specified, the string 'hr' (for horizontal ruler) can be used
-     * instead of an array of cells data resulting in a divider rendered.
-     *
-     * Example of usage with array of arrays:
-     * $row1 = array('Harry Potter', '76 %');
-     * $row2 = array('Hermione Granger', '100 %');
-     * $t->data = array($row1, $row2);
-     *
-     * Example with array of html_table_row objects: (used for more fine-grained control)
-     * $cell1 = new html_table_cell();
-     * $cell1->text = 'Harry Potter';
-     * $cell1->colspan = 2;
-     * $row1 = new html_table_row();
-     * $row1->cells[] = $cell1;
-     * $cell2 = new html_table_cell();
-     * $cell2->text = 'Hermione Granger';
-     * $cell3 = new html_table_cell();
-     * $cell3->text = '100 %';
-     * $row2 = new html_table_row();
-     * $row2->cells = array($cell2, $cell3);
-     * $t->data = array($row1, $row2);
-     */
-    public $data;
-    /**
-     * @var string width of the table, percentage of the page preferred. Defaults to 80% of the page width.
-     * @deprecated since Moodle 2.0. Styling should be in the CSS.
-     */
-    public $width = null;
-    /**
-     * @var string alignment the whole table. Can be 'right', 'left' or 'center' (default).
-     * @deprecated since Moodle 2.0. Styling should be in the CSS.
-     */
-    public $tablealign = null;
-    /**
-     * @var int padding on each cell, in pixels
-     * @deprecated since Moodle 2.0. Styling should be in the CSS.
-     */
-    public $cellpadding = null;
-    /**
-     * @var int spacing between cells, in pixels
-     * @deprecated since Moodle 2.0. Styling should be in the CSS.
-     */
-    public $cellspacing = null;
-    /**
-     * @var array classes to add to particular rows, space-separated string.
-     * Classes 'r0' or 'r1' are added automatically for every odd or even row,
-     * respectively. Class 'lastrow' is added automatically for the last row
-     * in the table.
-     *
-     * Example of usage:
-     * $t->rowclasses[9] = 'tenth'
-     */
-    public $rowclasses;
-    /**
-     * @var array classes to add to every cell in a particular column,
-     * space-separated string. Class 'cell' is added automatically by the renderer.
-     * Classes 'c0' or 'c1' are added automatically for every odd or even column,
-     * respectively. Class 'lastcol' is added automatically for all last cells
-     * in a row.
-     *
-     * Example of usage:
-     * $t->colclasses = array(null, 'grade');
-     */
-    public $colclasses;
-    /**
-     * @var string description of the contents for screen readers.
-     */
-    public $summary;
-    /**
-     * @var bool true causes the contents of the heading cells to be rotated 90 degrees.
-     */
-    public $rotateheaders = false;
-
-    /**
-     * @see moodle_html_component::prepare()
-     * @return void
-     */
-    public function prepare() {
-        if (!empty($this->align)) {
-            foreach ($this->align as $key => $aa) {
-                if ($aa) {
-                    $this->align[$key] = 'text-align:'. fix_align_rtl($aa) .';';  // Fix for RTL languages
-                } else {
-                    $this->align[$key] = '';
-                }
-            }
-        }
-        if (!empty($this->size)) {
-            foreach ($this->size as $key => $ss) {
-                if ($ss) {
-                    $this->size[$key] = 'width:'. $ss .';';
-                } else {
-                    $this->size[$key] = '';
-                }
-            }
-        }
-        if (!empty($this->wrap)) {
-            foreach ($this->wrap as $key => $ww) {
-                if ($ww) {
-                    $this->wrap[$key] = 'white-space:nowrap;';
-                } else {
-                    $this->wrap[$key] = '';
-                }
-            }
-        }
-        if (!empty($this->head)) {
-            foreach ($this->head as $key => $val) {
-                if (!isset($this->align[$key])) {
-                    $this->align[$key] = '';
-                }
-                if (!isset($this->size[$key])) {
-                    $this->size[$key] = '';
-                }
-                if (!isset($this->wrap[$key])) {
-                    $this->wrap[$key] = '';
-                }
-
-            }
-        }
-        if (empty($this->classes)) { // must be done before align
-            $this->set_classes(array('generaltable'));
-        }
-        if (!empty($this->tablealign)) {
-            $this->add_class('boxalign' . $this->tablealign);
-        }
-        if (!empty($this->rotateheaders)) {
-            $this->add_class('rotateheaders');
-        } else {
-            $this->rotateheaders = false; // Makes life easier later.
-        }
-        parent::prepare();
-    }
-    /**
-     * @param string $name The name of the variable to set
-     * @param mixed $value The value to assign to the variable
-     * @return void
-     */
-    public function __set($name, $value) {
-        if ($name == 'rowclass') {
-            debugging('rowclass[] has been deprecated for html_table ' .
-                      'and should be replaced with rowclasses[]. please fix the code.');
-            $this->rowclasses = $value;
-        } else {
-            parent::__set($name, $value);
-        }
-    }
-}
-
-/**
- * Component representing a table row.
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class html_table_row extends moodle_html_component {
-    /**
-     * @var array $cells Array of html_table_cell objects
-     */
-    public $cells = array();
-
-    /**
-     * @see lib/moodle_html_component#prepare()
-     * @return void
-     */
-    public function prepare() {
-        parent::prepare();
-    }
-}
-
-/**
- * Component representing a table cell.
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class html_table_cell extends moodle_html_component {
-    /**
-     * @var string $text The contents of the cell
-     */
-    public $text;
-    /**
-     * @var string $abbr Abbreviated version of the contents of the cell
-     */
-    public $abbr = '';
-    /**
-     * @var int $colspan Number of columns this cell should span
-     */
-    public $colspan = '';
-    /**
-     * @var int $rowspan Number of rows this cell should span
-     */
-    public $rowspan = '';
-    /**
-     * @var string $scope Defines a way to associate header cells and data cells in a table
-     */
-    public $scope = '';
-
-    /**
-     * @see lib/moodle_html_component#prepare()
-     * @return void
-     */
-    public function prepare() {
-        parent::prepare();
-    }
-}
-
-/**
- * Component representing a XHTML link.
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class html_link extends moodle_html_component {
-    /**
-     * URL can be simple text or a moodle_url object
-     * @var mixed $url
-     */
-    public $url;
-
-    /**
-     * @var string $text The text that will appear between the link tags
-     */
-    public $text;
-
-    /**
-     * @see lib/moodle_html_component#prepare()
-     * @return void
-     */
-    public function prepare() {
-        // We can't accept an empty text value
-        if (empty($this->text)) {
-            throw new coding_exception('A html_link must have a descriptive text value!');
-        }
-
-        parent::prepare();
-    }
-
-    /**
-     * Shortcut for creating a link component.
-     * @param mixed  $url String or moodle_url
-     * @param string $text The text of the link
-     * @return html_link The link component
-     */
-    public function make($url, $text) {
-        $link = new html_link();
-        $link->url = $url;
-        $link->text = $text;
-        return $link;
-    }
-}
-
-/**
- * Component representing a help icon.
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class help_icon extends moodle_html_component {
-    /**
-     * @var html_link $link A html_link object that will hold the URL info
-     */
-    public $link;
-    /**
-     * @var string $text A descriptive text
-     */
-    public $text;
-    /**
-     * @var string $page  The keyword that defines a help page
-     */
-    public $page;
-    /**
-     * @var string $module Which module is the page defined in
-     */
-    public $module = 'moodle';
-    /**
-     * @var boolean $linktext Whether or not to show text next to the icon
-     */
-    public $linktext = false;
-    /**
-     * @var mixed $image The help icon. Can be set to true (will use default help icon),
-     *                   false (will not use any icon), the URL to an image, or a full
-     *                   html_image object.
-     */
-    public $image;
-
-    /**
-     * Constructor: sets up the other components in case they are needed
-     * @return void
-     */
-    public function __construct() {
-        $this->link = new html_link();
-        $this->image = new html_image();
-    }
-
-    /**
-     * @see lib/moodle_html_component#prepare()
-     * @return void
-     */
-    public function prepare() {
-        global $COURSE, $OUTPUT;
-
-        if (empty($this->page)) {
-            throw new coding_exception('A help_icon object requires a $page parameter');
-        }
-
-        if (empty($this->text)) {
-            throw new coding_exception('A help_icon object requires a $text parameter');
-        }
-
-        $this->link->text = $this->text;
-
-        // fix for MDL-7734
-        $this->link->url = new moodle_url('/help.php', array('module' => $this->module, 'file' => $this->page .'.html'));
-
-        // fix for MDL-7734
-        if (!empty($COURSE->lang)) {
-            $this->link->url->param('forcelang', $COURSE->lang);
-        }
-
-        // Catch references to the old text.html and emoticons.html help files that
-        // were renamed in MDL-13233.
-        if (in_array($this->page, array('text', 'emoticons', 'richtext'))) {
-            $oldname = $this->page;
-            $this->page .= '2';
-            debugging("You are referring to the old help file '$oldname'. " .
-                    "This was renamed to '$this->page' because of MDL-13233. " .
-                    "Please update your code.", DEBUG_DEVELOPER);
-        }
-
-        if ($this->module == '') {
-            $this->module = 'moodle';
-        }
-
-        // Warn users about new window for Accessibility
-        $this->title = get_string('helpprefix2', '', trim($this->text, ". \t")) .' ('.get_string('newwindow').')';
-
-        // Prepare image and linktext
-        if ($this->image && !($this->image instanceof html_image)) {
-            $image = fullclone($this->image);
-            $this->image = new html_image();
-
-            if ($image instanceof moodle_url) {
-                $this->image->src = $image->out();
-            } else if ($image === true) {
-                $this->image->src = $OUTPUT->old_icon_url('help');
-            } else if (is_string($image)) {
-                $this->image->src = $image;
-            }
-            $this->image->alt = $this->text;
-
-            if ($this->linktext) {
-                $this->image->alt = get_string('helpwiththis');
-            } else {
-                $this->image->alt = $this->title;
-            }
-            $this->image->add_class('iconhelp');
-        } else if (empty($this->image->src)) {
-            if (!($this->image instanceof html_image)) {
-                $this->image = new html_image();
-            }
-            $this->image->src = $OUTPUT->old_icon_url('help');
-        }
-
-        parent::prepare();
-    }
-
-    public static function make_scale_menu($courseid, $scale) {
-        $helpbutton = new help_icon();
-        $strscales = get_string('scales');
-        $helpbutton->image->alt = $scale->name;
-        $helpbutton->link->url = new moodle_url('/course/scales.php', array('id' => $courseid, 'list' => true, 'scaleid' => $scale->id));
-        $popupaction = new popup_action('click', $helpbutton->url, 'ratingscale', $popupparams);
-        $popupaction->width = 500;
-        $popupaction->height = 400;
-        $helpbutton->link->add_action($popupaction);
-        $helpbutton->link->title = $scale->name;
-        return $helpbutton;
-    }
-}
-
-
-/**
- * Component representing a XHTML button (input of type 'button').
- * The renderer will either output it as a button with an onclick event,
- * or as a form with hidden inputs.
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class html_button extends moodle_html_component {
-    /**
-     * @var string $text
-     */
-    public $text;
-
-    /**
-     * @var boolean $disabled Whether or not this button is disabled
-     */
-    public $disabled = false;
-
-    /**
-     * @see lib/moodle_html_component#prepare()
-     * @return void
-     */
-    public function prepare() {
-        $this->add_class('singlebutton');
-
-        if (empty($this->text)) {
-            throw new coding_exception('A html_button must have a text value!');
-        }
-
-        if ($this->disabled) {
-            $this->disabled = 'disabled';
-        }
-
-        parent::prepare();
-    }
-}
-
-/**
- * Component representing an icon linking to a Moodle page.
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class action_icon extends moodle_html_component {
-    /**
-     * @var string $linktext Optional text to display next to the icon
-     */
-    public $linktext;
-    /**
-     * @var html_image $image The icon
-     */
-    public $image;
-    /**
-     * @var html_link $link The link
-     */
-    public $link;
-
-    /**
-     * Constructor: sets up the other components in case they are needed
-     * @return void
-     */
-    public function __construct() {
-        $this->image = new html_image();
-        $this->link = new html_link();
-    }
-
-    /**
-     * @see lib/moodle_html_component#prepare()
-     * @return void
-     */
-    public function prepare() {
-        $this->image->add_class('action-icon');
-
-        parent::prepare();
-
-        if (empty($this->image->src)) {
-            throw new coding_exception('action_icon->image->src must not be empty');
-        }
-
-        if (empty($this->image->alt) && !empty($this->linktext)) {
-            $this->image->alt = $this->linktext;
-        } else if (empty($this->image->alt)) {
-            debugging('action_icon->image->alt should not be empty.', DEBUG_DEVELOPER);
-        }
-    }
-}
-
-/**
- * Component representing an image.
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class html_image extends moodle_html_component {
-    /**
-     * @var string $alt A descriptive text
-     */
-    public $alt = HTML_ATTR_EMPTY;
-    /**
-     * @var string $src The path to the image being used
-     */
-    public $src;
-
-    /**
-     * @see lib/moodle_html_component#prepare()
-     * @return void
-     */
-    public function prepare() {
-        $this->add_class('image');
-        parent::prepare();
-    }
-}
-
-/**
- * Component representing a user picture.
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class user_picture extends moodle_html_component {
-    /**
-     * @var mixed $user A userid or a user object with at least fields id, picture, imagealrt, firstname and lastname set.
-     */
-    public $user;
-    /**
-     * @var int $courseid The course id. Used when constructing the link to the user's profile.
-     */
-    public $courseid;
-    /**
-     * @var html_image $image A custom image used as the user picture.
-     */
-    public $image;
-    /**
-     * @var mixed $url False: picture not enclosed in a link. True: default link. moodle_url: custom link.
-     */
-    public $url;
-    /**
-     * @var int $size Size in pixels. Special values are (true/1 = 100px) and (false/0 = 35px) for backward compatibility
-     */
-    public $size;
-    /**
-     * @var boolean $alttext add non-blank alt-text to the image. (Default true, set to false for purely
-     */
-    public $alttext = true;
-    /**
-     * @var boolean $popup Whether or not to open the link in a popup window
-     */
-    public $popup = false;
-
-    /**
-     * Constructor: sets up the other components in case they are needed
-     * @return void
-     */
-    public function __construct() {
-        $this->image = new html_image();
-    }
-
-    /**
-     * @see lib/moodle_html_component#prepare()
-     * @return void
-     */
-    public function prepare() {
-        global $CFG, $DB, $OUTPUT;
-
-        if (empty($this->user)) {
-            throw new coding_exception('A user_picture object must have a $user object before being rendered.');
-        }
-
-        if (empty($this->courseid)) {
-            throw new coding_exception('A user_picture object must have a courseid value before being rendered.');
-        }
-
-        if (!($this->image instanceof html_image)) {
-            debugging('user_picture::image must be an instance of html_image', DEBUG_DEVELOPER);
-        }
-
-        $needrec = false;
-        // only touch the DB if we are missing data...
-        if (is_object($this->user)) {
-            // Note - both picture and imagealt _can_ be empty
-            // what we are trying to see here is if they have been fetched
-            // from the DB. We should use isset() _except_ that some installs
-            // have those fields as nullable, and isset() will return false
-            // on null. The only safe thing is to ask array_key_exists()
-            // which works on objects. property_exists() isn't quite
-            // what we want here...
-            if (! (array_key_exists('picture', $this->user)
-                   && ($this->alttext && array_key_exists('imagealt', $this->user)
-                       || (isset($this->user->firstname) && isset($this->user->lastname)))) ) {
-                $needrec = true;
-                $this->user = $this->user->id;
-            }
-        } else {
-            if ($this->alttext) {
-                // we need firstname, lastname, imagealt, can't escape...
-                $needrec = true;
-            } else {
-                $userobj = new StdClass; // fake it to save DB traffic
-                $userobj->id = $this->user;
-                $userobj->picture = $this->image->src;
-                $this->user = clone($userobj);
-                unset($userobj);
-            }
-        }
-        if ($needrec) {
-            $this->user = $DB->get_record('user', array('id' => $this->user), 'id,firstname,lastname,imagealt');
-        }
-
-        if ($this->url === true) {
-            $this->url = new moodle_url('/user/view.php', array('id' => $this->user->id, 'course' => $this->courseid));
-        }
-
-        if (!empty($this->url) && $this->popup) {
-            $this->add_action(new popup_action('click', $this->url));
-        }
-
-        if (empty($this->size)) {
-            $file = 'f2';
-            $this->size = 35;
-        } else if ($this->size === true or $this->size == 1) {
-            $file = 'f1';
-            $this->size = 100;
-        } else if ($this->size >= 50) {
-            $file = 'f1';
-        } else {
-            $file = 'f2';
-        }
-
-        if (!empty($this->size)) {
-            $this->image->width = $this->size;
-            $this->image->height = $this->size;
-        }
-
-        $this->add_class('userpicture');
-
-        if (empty($this->image->src) && !empty($this->user->picture)) {
-            $this->image->src = $this->user->picture;
-        }
-
-        if (!empty($this->image->src)) {
-            require_once($CFG->libdir.'/filelib.php');
-            $this->image->src = new moodle_url(get_file_url($this->user->id.'/'.$file.'.jpg', null, 'user'));
-        } else { // Print default user pictures (use theme version if available)
-            $this->add_class('defaultuserpic');
-            $this->image->src = $OUTPUT->old_icon_url('u/' . $file);
-        }
-
-        if ($this->alttext) {
-            if (!empty($this->user->imagealt)) {
-                $this->image->alt = $this->user->imagealt;
-            } else {
-                $this->image->alt = get_string('pictureof','',fullname($this->user));
-            }
-        }
-
-        parent::prepare();
-    }
-}
-
-/**
- * Component representing a textarea.
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class html_textarea extends moodle_html_component {
-    /**
-     * @param string $name Name to use for the textarea element.
-     */
-    public $name;
-    /**
-     * @param string $value Initial content to display in the textarea.
-     */
-    public $value;
-    /**
-     * @param int $rows Number of rows to display  (minimum of 10 when $height is non-null)
-     */
-    public $rows;
-    /**
-     * @param int $cols Number of columns to display (minimum of 65 when $width is non-null)
-     */
-    public $cols;
-    /**
-     * @param bool $usehtmleditor Enables the use of the htmleditor for this field.
-     */
-    public $usehtmleditor;
-
-    /**
-     * @see lib/moodle_html_component#prepare()
-     * @return void
-     */
-    public function prepare() {
-        $this->add_class('form-textarea');
-
-        if (empty($this->id)) {
-            $this->id = "edit-$this->name";
-        }
-
-        if ($this->usehtmleditor) {
-            editors_head_setup();
-            $editor = get_preferred_texteditor(FORMAT_HTML);
-            $editor->use_editor($this->id, array('legacy'=>true));
-            $this->value = htmlspecialchars($value);
-        }
-
-        parent::prepare();
-    }
-}
-
-/**
- * Component representing a simple form wrapper. Its purpose is mainly to enclose
- * a submit input with the appropriate action and hidden inputs.
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class html_form extends moodle_html_component {
-    /**
-     * @var string $method post or get
-     */
-    public $method = 'post';
-    /**
-     * If a string is given, it will be converted to a moodle_url during prepare()
-     * @var mixed $url A moodle_url including params or a string
-     */
-    public $url;
-    /**
-     * @var array $params Optional array of parameters. Ignored if $url instanceof moodle_url
-     */
-    public $params = array();
-    /**
-     * @var boolean $showbutton If true, the submit button will always be shown even if JavaScript is available
-     */
-    public $showbutton = false;
-    /**
-     * @var string $targetwindow The name of the target page to open the linked page in.
-     */
-    public $targetwindow = 'self';
-    /**
-     * @var html_button $button A submit button
-     */
-    public $button;
-
-    /**
-     * Constructor: sets up the other components in case they are needed
-     * @return void
-     */
-    public function __construct() {
-        static $yes;
-        $this->button = new html_button();
-        if (!isset($yes)) {
-            $yes = get_string('yes');
-            $this->button->text = $yes;
-        }
-    }
-
-    /**
-     * @see lib/moodle_html_component#prepare()
-     * @return void
-     */
-    public function prepare() {
-
-        if (empty($this->url)) {
-            throw new coding_exception('A html_form must have a $url value (string or moodle_url).');
-        }
-
-        if (!($this->url instanceof moodle_url)) {
-            $this->url = new moodle_url($this->url, $this->params);
-        }
-
-        if ($this->method == 'post') {
-            $this->url->param('sesskey', sesskey());
-        }
-
-        parent::prepare();
-    }
-}
-
-/**
- * Component representing a paging bar.
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class moodle_paging_bar extends moodle_html_component {
-    /**
-     * @var int $maxdisplay The maximum number of pagelinks to display
-     */
-    public $maxdisplay = 18;
-    /**
-     * @var int $totalcount post or get
-     */
-    public $totalcount;
-    /**
-     * @var int $page The page you are currently viewing
-     */
-    public $page = 0;
-    /**
-     * @var int $perpage The number of entries that should be shown per page
-     */
-    public $perpage;
-    /**
-     * @var string $baseurl If this  is a string then it is the url which will be appended with $pagevar, an equals sign and the page number.
-     *      If this is a moodle_url object then the pagevar param will be replaced by the page no, for each page.
-     */
-    public $baseurl;
-    /**
-     * @var string $pagevar This is the variable name that you use for the page number in your code (ie. 'tablepage', 'blogpage', etc)
-     */
-    public $pagevar = 'page';
-    /**
-     * @var bool $nocurr do not display the current page as a link
-     */
-    public $nocurr;
-    /**
-     * @var html_link $previouslink A HTML link representing the "previous" page
-     */
-    public $previouslink = null;
-    /**
-     * @var html_link $nextlink A HTML link representing the "next" page
-     */
-    public $nextlink = null;
-    /**
-     * @var html_link $firstlink A HTML link representing the first page
-     */
-    public $firstlink = null;
-    /**
-     * @var html_link $lastlink A HTML link representing the last page
-     */
-    public $lastlink = null;
-    /**
-     * @var array $pagelinks An array of html_links. One of them is just a string: the current page
-     */
-    public $pagelinks = array();
-
-    /**
-     * @see lib/moodle_html_component#prepare()
-     * @return void
-     */
-    public function prepare() {
-        if (empty($this->totalcount)) {
-            throw new coding_exception('moodle_paging_bar requires a totalcount value.');
-        }
-        if (!isset($this->page) || is_null($this->page)) {
-            throw new coding_exception('moodle_paging_bar requires a page value.');
-        }
-        if (empty($this->perpage)) {
-            throw new coding_exception('moodle_paging_bar requires a perpage value.');
-        }
-        if (empty($this->baseurl)) {
-            throw new coding_exception('moodle_paging_bar requires a baseurl value.');
-        }
-        if (!($this->baseurl instanceof moodle_url)) {
-            $this->baseurl = new moodle_url($this->baseurl);
-        }
-
-        if ($this->totalcount > $this->perpage) {
-            $pagenum = $this->page - 1;
-
-            if ($this->page > 0) {
-                $this->previouslink = new html_link();
-                $this->previouslink->add_class('previous');
-                $this->previouslink->url = clone($this->baseurl);
-                $this->previouslink->url->param($this->pagevar, $pagenum);
-                $this->previouslink->text = get_string('previous');
-            }
-
-            if ($this->perpage > 0) {
-                $lastpage = ceil($this->totalcount / $this->perpage);
-            } else {
-                $lastpage = 1;
-            }
-
-            if ($this->page > 15) {
-                $startpage = $this->page - 10;
-
-                $this->firstlink = new html_link();
-                $this->firstlink->url = clone($this->baseurl);
-                $this->firstlink->url->param($this->pagevar, 0);
-                $this->firstlink->text = 1;
-                $this->firstlink->add_class('first');
-            } else {
-                $startpage = 0;
-            }
-
-            $currpage = $startpage;
-            $displaycount = $displaypage = 0;
-
-            while ($displaycount < $this->maxdisplay and $currpage < $lastpage) {
-                $displaypage = $currpage + 1;
-
-                if ($this->page == $currpage && empty($this->nocurr)) {
-                    $this->pagelinks[] = $displaypage;
-                } else {
-                    $pagelink = new html_link();
-                    $pagelink->url = clone($this->baseurl);
-                    $pagelink->url->param($this->pagevar, $currpage);
-                    $pagelink->text = $displaypage;
-                    $this->pagelinks[] = $pagelink;
-                }
-
-                $displaycount++;
-                $currpage++;
-            }
-
-            if ($currpage < $lastpage) {
-                $lastpageactual = $lastpage - 1;
-                $this->lastlink = new html_link();
-                $this->lastlink->url = clone($this->baseurl);
-                $this->lastlink->url->param($this->pagevar, $lastpageactual);
-                $this->lastlink->text = $lastpage;
-                $this->lastlink->add_class('last');
-            }
-
-            $pagenum = $this->page + 1;
-
-            if ($pagenum != $displaypage) {
-                $this->nextlink = new html_link();
-                $this->nextlink->url = clone($this->baseurl);
-                $this->nextlink->url->param($this->pagevar, $pagenum);
-                $this->nextlink->text = get_string('next');
-                $this->nextlink->add_class('next');
-            }
-        }
-    }
-
-    /**
-     * Shortcut for initialising a moodle_paging_bar with only the required params.
-     *
-     * @param int $totalcount Thetotal number of entries available to be paged through
-     * @param int $page The page you are currently viewing
-     * @param int $perpage The number of entries that should be shown per page
-     * @param mixed $baseurl If this  is a string then it is the url which will be appended with $pagevar, an equals sign and the page number.
-     *                          If this is a moodle_url object then the pagevar param will be replaced by the page no, for each page.
-     */
-    public function make($totalcount, $page, $perpage, $baseurl) {
-        $pagingbar = new moodle_paging_bar();
-        $pagingbar->totalcount = $totalcount;
-        $pagingbar->page = $page;
-        $pagingbar->perpage = $perpage;
-        $pagingbar->baseurl = $baseurl;
-        return $pagingbar;
-    }
-}
-
-/**
- * Component representing a list.
- *
- * The advantage of using this object instead of a flat array is that you can load it
- * with metadata (CSS classes, event handlers etc.) which can be used by the renderers.
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class html_list extends moodle_html_component {
-
-    /**
-     * @var array $items An array of html_list_item or html_list objects
-     */
-    public $items = array();
-
-    /**
-     * @var string $type The type of list (ordered|unordered), definition type not yet supported
-     */
-    public $type = 'unordered';
-
-    /**
-     * @see lib/moodle_html_component#prepare()
-     * @return void
-     */
-    public function prepare() {
-        parent::prepare();
-    }
-
-    /**
-     * This function takes a nested array of data and maps it into this list's $items array
-     * as proper html_list_item and html_list objects, with appropriate metadata.
-     *
-     * @param array $tree A nested array (array keys are ignored);
-     * @param int $row Used in identifying the iteration level and in ul classes
-     * @return void
-     */
-    public function load_data($tree, $level=0) {
-
-        $this->add_class("list-$level");
-
-        foreach ($tree as $key => $element) {
-            if (is_array($element)) {
-                $newhtmllist = new html_list();
-                $newhtmllist->load_data($element, $level + 1);
-                $this->items[] = $newhtmllist;
-            } else {
-                $listitem = new html_list_item();
-                $listitem->value = $element;
-                $listitem->add_class("list-item-$level-$key");
-                $this->items[] = $listitem;
-            }
-        }
-    }
-
-    /**
-     * Adds a html_list_item or html_list to this list.
-     * If the param is a string, a html_list_item will be added.
-     * @param mixed $item String, html_list or html_list_item object
-     * @return void
-     */
-    public function add_item($item) {
-        if ($item instanceof html_list_item || $item instanceof html_list) {
-            $this->items[] = $item;
-        } else {
-            $listitem = new html_list_item();
-            $listitem->value = $item;
-            $this->items[] = $item;
-        }
-    }
-}
-
-/**
- * Component representing a list item.
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class html_list_item extends moodle_html_component {
-    /**
-     * @var string $value The value of the list item
-     */
-    public $value;
-
-    /**
-     * @see lib/moodle_html_component#prepare()
-     * @return void
-     */
-    public function prepare() {
-        parent::prepare();
-    }
-}
-
-/// ACTIONS
-
-/**
- * Helper class used by other components that involve an action on the page (URL or JS).
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class component_action {
-
-    /**
-     * The DOM event that will trigger this action when caught
-     * @var string $event DOM event
-     */
-    public $event;
-
-    /**
-     * The JS function you create must have two arguments:
-     *      1. The event object
-     *      2. An object/array of arguments ($jsfunctionargs)
-     * @var string $jsfunction A function name to call when the button is clicked
-     */
-    public $jsfunction = false;
-
-    /**
-     * @var array $jsfunctionargs An array of arguments to pass to the JS function
-     */
-    public $jsfunctionargs = array();
-
-    /**
-     * Constructor
-     * @param string $event DOM event
-     * @param moodle_url $url A moodle_url object, required if no jsfunction is given
-     * @param string $method 'post' or 'get'
-     * @param string $jsfunction An optional JS function. Required if jsfunctionargs is given
-     * @param array  $jsfunctionargs An array of arguments to pass to the jsfunction
-     * @return void
-     */
-    public function __construct($event, $jsfunction, $jsfunctionargs=array()) {
-        $this->event = $event;
-
-        $this->jsfunction = $jsfunction;
-        $this->jsfunctionargs = $jsfunctionargs;
-
-        if (!empty($this->jsfunctionargs)) {
-            if (empty($this->jsfunction)) {
-                throw new coding_exception('The component_action object needs a jsfunction value to pass the jsfunctionargs to.');
-            }
-        }
-    }
-}
-
-/**
- * Component action for a popup window.
- *
- * @copyright 2009 Nicolas Connault
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class popup_action extends component_action {
-    /**
-     * @var array $params An array of parameters that will be passed to the openpopup JS function
-     */
-    public $params = array(
-            'height' =>  400,
-            'width' => 500,
-            'top' => 0,
-            'left' => 0,
-            'menubar' => false,
-            'location' => false,
-            'scrollbars' => true,
-            'resizable' => true,
-            'toolbar' => true,
-            'status' => true,
-            'directories' => false,
-            'fullscreen' => false,
-            'dependent' => true);
-
-    /**
-     * Constructor
-     * @param string $event DOM event
-     * @param moodle_url $url A moodle_url object, required if no jsfunction is given
-     * @param string $method 'post' or 'get'
-     * @param array  $params An array of popup parameters
-     * @return void
-     */
-    public function __construct($event, $url, $name='popup', $params=array()) {
-        global $CFG;
-        $this->name = $name;
-
-        $url = new moodle_url($url);
-
-        if ($this->name) {
-            $_name = $this->name;
-            if (($_name = preg_replace("/\s/", '_', $_name)) != $this->name) {
-                throw new coding_exception('The $name of a popup window shouldn\'t contain spaces - string modified. '. $this->name .' changed to '. $_name);
-                $this->name = $_name;
-            }
-        } else {
-            $this->name = 'popup';
-        }
-
-        foreach ($this->params as $var => $val) {
-            if (array_key_exists($var, $params)) {
-                $this->params[$var] = $params[$var];
-            }
-        }
-        parent::__construct($event, 'openpopup', array('url' => $url->out(false, array(), false), 'name' => $name, 'options' => $this->get_js_options($params)));
-    }
-
-    /**
-     * Returns a string of concatenated option->value pairs used by JS to call the popup window,
-     * based on this object's variables
-     *
-     * @return string String of option->value pairs for JS popup function.
-     */
-    public function get_js_options() {
-        $jsoptions = '';
-
-        foreach ($this->params as $var => $val) {
-            if (is_string($val) || is_int($val)) {
-                $jsoptions .= "$var=$val,";
-            } elseif (is_bool($val)) {
-                $jsoptions .= ($val) ? "$var," : "$var=0,";
-            }
-        }
-
-        $jsoptions = substr($jsoptions, 0, strlen($jsoptions) - 1);
-
-        return $jsoptions;
-    }
-}
-
-/// RENDERERS
-
-/**
- * A renderer that generates output for command-line scripts.
- *
- * The implementation of this renderer is probably incomplete.
- *
- * @copyright 2009 Tim Hunt
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since     Moodle 2.0
- */
-class cli_core_renderer extends moodle_core_renderer {
-    /**
-     * Returns the page header.
-     * @return string HTML fragment
-     */
-    public function header() {
-        output_starting_hook();
-        return $this->page->heading . "\n";
-    }
-
-    /**
-     * Returns a template fragment representing a Heading.
-     * @param string $text The text of the heading
-     * @param int $level The level of importance of the heading
-     * @param string $classes A space-separated list of CSS classes
-     * @param string $id An optional ID
-     * @return string A template fragment for a heading
-     */
-    public function heading($text, $level, $classes = 'main', $id = '') {
-        $text .= "\n";
-        switch ($level) {
-            case 1:
-                return '=>' . $text;
-            case 2:
-                return '-->' . $text;
-            default:
-                return $text;
-        }
-    }
-
-    /**
-     * Returns a template fragment representing a fatal error.
-     * @param string $message The message to output
-     * @param string $moreinfourl URL where more info can be found about the error
-     * @param string $link Link for the Continue button
-     * @param array $backtrace The execution backtrace
-     * @param string $debuginfo Debugging information
-     * @param bool $showerrordebugwarning Whether or not to show a debugging warning
-     * @return string A template fragment for a fatal error
-     */
-    public function fatal_error($message, $moreinfourl, $link, $backtrace,
-                $debuginfo = null, $showerrordebugwarning = false) {
-        $output = "!!! $message !!!\n";
-
-        if (debugging('', DEBUG_DEVELOPER)) {
-            if (!empty($debuginfo)) {
-                $this->notification($debuginfo, 'notifytiny');
-            }
-            if (!empty($backtrace)) {
-                $this->notification('Stack trace: ' . format_backtrace($backtrace, true), 'notifytiny');
-            }
-        }
-    }
-
-    /**
-     * Returns a template fragment representing a notification.
-     * @param string $message The message to include
-     * @param string $classes A space-separated list of CSS classes
-     * @return string A template fragment for a notification
-     */
-    public function notification($message, $classes = 'notifyproblem') {
-        $message = clean_text($message);
-        if ($classes === 'notifysuccess') {
-            return "++ $message ++\n";
-        }
-        return "!! $message !!\n";
+    protected function output_log() {
+        return '<ul>' . implode("\n", $this->log) . '</ul>';
     }
 }
 
diff --git a/lib/outputpixfinders.php b/lib/outputpixfinders.php
new file mode 100644 (file)
index 0000000..decae9b
--- /dev/null
@@ -0,0 +1,221 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Interface and classes for icon finders.
+ *
+ * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML
+ * for an overview.
+ *
+ * @package   moodlecore
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * An icon finder is responsible for working out the correct URL for an icon.
+ *
+ * A icon finder must also have a constructor that takes a theme object.
+ * (See {@link standard_icon_finder::__construct} for an example.)
+ *
+ * Note that we are planning to change the Moodle icon naming convention before
+ * the Moodle 2.0 release. Therefore, this API will probably change.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+interface icon_finder {
+    /**
+     * Return the URL for an icon identified as in pre-Moodle 2.0 code.
+     *
+     * Suppose you have old code like $url = "$CFG->pixpath/i/course.gif";
+     * then old_icon_url('i/course'); will return the equivalent URL that is correct now.
+     *
+     * @param string $iconname the name of the icon.
+     * @return string the URL for that icon.
+     */
+    public function old_icon_url($iconname);
+
+    /**
+     * Return the URL for an icon identified as in pre-Moodle 2.0 code.
+     *
+     * Suppose you have old code like $url = "$CFG->modpixpath/$mod/icon.gif";
+     * then mod_icon_url('icon', $mod); will return the equivalent URL that is correct now.
+     *
+     * @param string $iconname the name of the icon.
+     * @param string $module the module the icon belongs to.
+     * @return string the URL for that icon.
+     */
+    public function mod_icon_url($iconname, $module);
+}
+
+/**
+ * This icon finder implements the old scheme that was used when themes that had
+ * $THEME->custompix = false.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class pix_icon_finder implements icon_finder {
+    /**
+     * Constructor
+     * @param theme_config $theme the theme we are finding icons for (which is irrelevant).
+     */
+    public function __construct($theme) {
+    }
+
+    /**
+     * Implement interface method.
+     * @param string $iconname the name of the icon.
+     * @return string the URL for that icon.
+     */
+    public function old_icon_url($iconname) {
+        global $CFG;
+        if (file_exists($CFG->dirroot . '/pix/' . $iconname . '.png')) {
+            return $CFG->httpswwwroot . '/pix/' . $iconname . '.png';
+        } else {
+            return $CFG->httpswwwroot . '/pix/' . $iconname . '.gif';
+        }
+    }
+
+    /**
+     * Implement interface method.
+     * @param string $iconname the name of the icon.
+     * @param string $module the module the icon belongs to.
+     * @return string the URL for that icon.
+     */
+    public function mod_icon_url($iconname, $module) {
+        global $CFG;
+        if (file_exists($CFG->dirroot . '/mod/' . $module . '/' . $iconname . '.png')) {
+            return $CFG->httpswwwroot . '/mod/' . $module . '/' . $iconname . '.png';
+        } else {
+            return $CFG->httpswwwroot . '/mod/' . $module . '/' . $iconname . '.gif';
+        }
+    }
+}
+
+
+/**
+ * This icon finder implements the old scheme that was used for themes that had
+ * $THEME->custompix = true.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class theme_icon_finder implements icon_finder {
+    protected $themename;
+    /**
+     * Constructor
+     * @param theme_config $theme the theme we are finding icons for.
+     */
+    public function __construct($theme) {
+        $this->themename = $theme->name;
+    }
+
+    /**
+     * Implement interface method.
+     * @param string $iconname the name of the icon.
+     * @return string the URL for that icon.
+     */
+    public function old_icon_url($iconname) {
+        global $CFG;
+        if (file_exists($CFG->themedir . '/' . $this->themename . '/pix/' . $iconname . '.png')) {
+            return $CFG->httpsthemewww . '/' . $this->themename . '/pix/' . $iconname . '.png';
+        } else {
+            return $CFG->httpsthemewww . '/' . $this->themename . '/pix/' . $iconname . '.gif';
+        }
+    }
+
+    /**
+     * Implement interface method.
+     * @param string $iconname the name of the icon.
+     * @param string $module the module the icon belongs to.
+     * @return string the URL for that icon.
+     */
+    public function mod_icon_url($iconname, $module) {
+        global $CFG;
+        if (file_exists($CFG->themedir . '/' . $this->themename . '/pix/mod/' . $module . '/' . $iconname . '.png')) {
+            return $CFG->httpsthemewww . '/' . $this->themename . '/pix/mod/' . $module . '/' . $iconname . '.png';
+        } else {
+            return $CFG->httpsthemewww . '/' . $this->themename . '/pix/mod/' . $module . '/' . $iconname . '.gif';
+        }
+    }
+}
+
+
+/**
+ * This icon finder implements the algorithm in pix/smartpix.php.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class smartpix_icon_finder extends pix_icon_finder {
+    protected $places = array();
+
+    /**
+     * Constructor
+     * @param theme_config $theme the theme we are finding icons for.
+     */
+    public function __construct($theme) {
+        global $CFG;
+        $this->places[$CFG->themedir . '/' . $theme->name . '/pix/'] =
+                $CFG->httpsthemewww . '/' . $theme->name . '/pix/';
+        if (!empty($theme->parent)) {
+            $this->places[$CFG->themedir . '/' . $theme->parent . '/pix/'] =
+                    $CFG->httpsthemewww . '/' . $theme->parent . '/pix/';
+        }
+    }
+
+    /**
+     * Implement interface method.
+     * @param string $iconname the name of the icon.
+     * @return string the URL for that icon.
+     */
+    public function old_icon_url($iconname) {
+        foreach ($this->places as $dirroot => $urlroot) {
+            if (file_exists($dirroot . $iconname . '.png')) {
+                return $dirroot . $iconname . '.png';
+            } else if (file_exists($dirroot . $iconname . '.gif')) {
+                return $dirroot . $iconname . '.gif';
+            }
+        }
+        return parent::old_icon_url($iconname);
+    }
+
+    /**
+     * Implement interface method.
+     * @param string $iconname the name of the icon.
+     * @param string $module the module the icon belongs to.
+     * @return string the URL for that icon.
+     */
+    public function mod_icon_url($iconname, $module) {
+        foreach ($this->places as $dirroot => $urlroot) {
+            if (file_exists($dirroot . 'mod/' . $iconname . '.png')) {
+                return $dirroot . 'mod/' . $iconname . '.png';
+            } else if (file_exists($dirroot . 'mod/' . $iconname . '.gif')) {
+                return $dirroot . 'mod/' . $iconname . '.gif';
+            }
+        }
+        return parent::old_icon_url($iconname, $module);
+    }
+}
+
+
diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php
new file mode 100644 (file)
index 0000000..c12040b
--- /dev/null
@@ -0,0 +1,2254 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Classes for rendering HTML output for Moodle.
+ *
+ * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML
+ * for an overview.
+ *
+ * @package   moodlecore
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Simple base class for Moodle renderers.
+ *
+ * Tracks the xhtml_container_stack to use, which is passed in in the constructor.
+ *
+ * Also has methods to facilitate generating HTML output.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class moodle_renderer_base {
+    /** @var xhtml_container_stack the xhtml_container_stack to use. */
+    protected $opencontainers;
+    /** @var moodle_page the page we are rendering for. */
+    protected $page;
+
+    /**
+     * Constructor
+     * @param moodle_page $page the page we are doing output for.
+     */
+    public function __construct($page) {
+        $this->opencontainers = $page->opencontainers;
+        $this->page = $page;
+    }
+
+    /**
+     * Have we started output yet?
+     * @return boolean true if the header has been printed.
+     */
+    public function has_started() {
+        return $this->page->state >= moodle_page::STATE_IN_BODY;
+    }
+
+    /**
+     * Outputs a tag with attributes and contents
+     * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
+     * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
+     * @param string $contents What goes between the opening and closing tags
+     * @return string HTML fragment
+     */
+    protected function output_tag($tagname, $attributes, $contents) {
+        return $this->output_start_tag($tagname, $attributes) . $contents .
+                $this->output_end_tag($tagname);
+    }
+
+    /**
+     * Outputs an opening tag with attributes
+     * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
+     * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
+     * @return string HTML fragment
+     */
+    protected function output_start_tag($tagname, $attributes) {
+        return '<' . $tagname . $this->output_attributes($attributes) . '>';
+    }
+
+    /**
+     * Outputs a closing tag
+     * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
+     * @return string HTML fragment
+     */
+    protected function output_end_tag($tagname) {
+        return '</' . $tagname . '>';
+    }
+
+    /**
+     * Outputs an empty tag with attributes
+     * @param string $tagname The name of tag ('input', 'img', 'br' etc.)
+     * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
+     * @return string HTML fragment
+     */
+    protected function output_empty_tag($tagname, $attributes) {
+        return '<' . $tagname . $this->output_attributes($attributes) . ' />';
+    }
+
+    /**
+     * Outputs a HTML attribute and value
+     * @param string $name The name of the attribute ('src', 'href', 'class' etc.)
+     * @param string $value The value of the attribute. The value will be escaped with {@link s()}
+     * @return string HTML fragment
+     */
+    protected function output_attribute($name, $value) {
+        if (is_array($value)) {
+            debugging("Passed an array for the HTML attribute $name", DEBUG_DEVELOPER);
+        }
+
+        $value = trim($value);
+        if ($value == HTML_ATTR_EMPTY) {
+            return ' ' . $name . '=""';
+        } else if ($value || is_numeric($value)) { // We want 0 to be output.
+            return ' ' . $name . '="' . s($value) . '"';
+        }
+    }
+
+    /**
+     * Outputs a list of HTML attributes and values
+     * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
+     *       The values will be escaped with {@link s()}
+     * @return string HTML fragment
+     */
+    protected function output_attributes($attributes) {
+        if (empty($attributes)) {
+            $attributes = array();
+        }
+        $output = '';
+        foreach ($attributes as $name => $value) {
+            $output .= $this->output_attribute($name, $value);
+        }
+        return $output;
+    }
+
+    /**
+     * Given an array or space-separated list of classes, prepares and returns the HTML class attribute value
+     * @param mixed $classes Space-separated string or array of classes
+     * @return string HTML class attribute value
+     */
+    public static function prepare_classes($classes) {
+        if (is_array($classes)) {
+            return implode(' ', array_unique($classes));
+        }
+        return $classes;
+    }
+
+    /**
+     * Return the URL for an icon identified as in pre-Moodle 2.0 code.
+     *
+     * Suppose you have old code like $url = "$CFG->pixpath/i/course.gif";
+     * then old_icon_url('i/course'); will return the equivalent URL that is correct now.
+     *
+     * @param string $iconname the name of the icon.
+     * @return string the URL for that icon.
+     */
+    public function old_icon_url($iconname) {
+        return $this->page->theme->old_icon_url($iconname);
+    }
+
+    /**
+     * Return the URL for an icon identified as in pre-Moodle 2.0 code.
+     *
+     * Suppose you have old code like $url = "$CFG->modpixpath/$mod/icon.gif";
+     * then mod_icon_url('icon', $mod); will return the equivalent URL that is correct now.
+     *
+     * @param string $iconname the name of the icon.
+     * @param string $module the module the icon belongs to.
+     * @return string the URL for that icon.
+     */
+    public function mod_icon_url($iconname, $module) {
+        return $this->page->theme->mod_icon_url($iconname, $module);
+    }
+
+    /**
+     * A helper function that takes a moodle_html_component subclass as param.
+     * If that component has an id attribute and an array of valid component_action objects,
+     * it sets up the appropriate event handlers.
+     *
+     * @param moodle_html_component $component
+     * @return void;
+     */
+    protected function prepare_event_handlers(&$component) {
+        $actions = $component->get_actions();
+        if (!empty($actions) && is_array($actions) && $actions[0] instanceof component_action) {
+            foreach ($actions as $action) {
+                if (!empty($action->jsfunction)) {
+                    $this->page->requires->event_handler($component->id, $action->event, $action->jsfunction, $action->jsfunctionargs);
+                }
+            }
+        }
+    }
+
+    /**
+     * Given a moodle_html_component with height and/or width set, translates them
+     * to appropriate CSS rules.
+     *
+     * @param moodle_html_component $component
+     * @return string CSS rules
+     */
+    protected function prepare_legacy_width_and_height($component) {
+        $output = '';
+        if (!empty($component->height)) {
+            // We need a more intelligent way to handle these warnings. If $component->height have come from
+            // somewhere in deprecatedlib.php, then there is no point outputting a warning here.
+            // debugging('Explicit height given to moodle_html_component leads to inline css. Use a proper CSS class instead.', DEBUG_DEVELOPER);
+            $output .= "height: {$component->height}px;";
+        }
+        if (!empty($component->width)) {
+            // debugging('Explicit width given to moodle_html_component leads to inline css. Use a proper CSS class instead.', DEBUG_DEVELOPER);
+            $output .= "width: {$component->width}px;";
+        }
+        return $output;
+    }
+}
+
+
+/**
+ * This is the templated renderer which copies the API of another class, replacing
+ * all methods calls with instantiation of a template.
+ *
+ * When the method method_name is called, this class will search for a template
+ * called method_name.php in the folders in $searchpaths, taking the first one
+ * that it finds. Then it will set up variables for each of the arguments of that
+ * method, and render the template. This is implemented in the {@link __call()}
+ * PHP magic method.
+ *
+ * Methods like print_box_start and print_box_end are handles specially, and
+ * implemented in terms of the print_box.php method.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class template_renderer extends moodle_renderer_base {
+    /** @var ReflectionClass information about the class whose API we are copying. */
+    protected $copiedclass;
+    /** @var array of places to search for templates. */
+    protected $searchpaths;
+    protected $rendererfactory;
+
+    /**
+     * Magic word used when breaking apart container templates to implement
+     * _start and _end methods.
+     */
+    const CONTENTSTOKEN = '-@#-Contents-go-here-#@-';
+
+    /**
+     * Constructor
+     * @param string $copiedclass the name of a class whose API we should be copying.
+     * @param array $searchpaths a list of folders to search for templates in.
+     * @param moodle_page $page the page we are doing output for.
+     */
+    public function __construct($copiedclass, $searchpaths, $page) {
+        parent::__construct($page);
+        $this->copiedclass = new ReflectionClass($copiedclass);
+        $this->searchpaths = $searchpaths;
+    }
+
+    /**
+     * PHP magic method implementation. Do not use this method directly.
+     * @param string $method The method to call
+     * @param array $arguments The arguments to pass to the method
+     * @return mixed The return value of the called method
+     */
+    public function __call($method, $arguments) {
+        if (substr($method, -6) == '_start') {
+            return $this->process_start(substr($method, 0, -6), $arguments);
+        } else if (substr($method, -4) == '_end') {
+            return $this->process_end(substr($method, 0, -4), $arguments);
+        } else {
+            return $this->process_template($method, $arguments);
+        }
+    }
+
+    /**
+     * Render the template for a given method of the renderer class we are copying,
+     * using the arguments passed.
+     * @param string $method the method that was called.
+     * @param array $arguments the arguments that were passed to it.
+     * @return string the HTML to be output.
+     */
+    protected function process_template($method, $arguments) {
+        if (!$this->copiedclass->hasMethod($method) ||
+                !$this->copiedclass->getMethod($method)->isPublic()) {
+            throw new coding_exception('Unknown method ' . $method);
+        }
+
+        // Find the template file for this method.
+        $template = $this->find_template($method);
+
+        // Use the reflection API to find out what variable names the arguments
+        // should be stored in, and fill in any missing ones with the defaults.
+        $namedarguments = array();
+        $expectedparams = $this->copiedclass->getMethod($method)->getParameters();
+        foreach ($expectedparams as $param) {
+            $paramname = $param->getName();
+            if (!empty($arguments)) {
+                $namedarguments[$paramname] = array_shift($arguments);
+            } else if ($param->isDefaultValueAvailable()) {
+                $namedarguments[$paramname] = $param->getDefaultValue();
+            } else {
+                throw new coding_exception('Missing required argument ' . $paramname);
+            }
+        }
+
+        // Actually render the template.
+        return $this->render_template($template, $namedarguments);
+    }
+
+    /**
+     * Actually do the work of rendering the template.
+     * @param string $_template the full path to the template file.
+     * @param array $_namedarguments an array variable name => value, the variables
+     *      that should be available to the template.
+     * @return string the HTML to be output.
+     */
+    protected function render_template($_template, $_namedarguments) {
+        // Note, we intentionally break the coding guidelines with regards to
+        // local variable names used in this function, so that they do not clash
+        // with the names of any variables being passed to the template.
+
+        global $CFG, $SITE, $THEME, $USER;
+        // The next lines are a bit tricky. The point is, here we are in a method
+        // of a renderer class, and this object may, or may not, be the same as
+        // the global $OUTPUT object. When rendering the template, we want to use
+        // this object. However, people writing Moodle code expect the current
+        // renderer to be called $OUTPUT, not $this, so define a variable called
+        // $OUTPUT pointing at $this. The same comment applies to $PAGE and $COURSE.
+        $OUTPUT = $this;
+        $PAGE = $this->page;
+        $COURSE = $this->page->course;
+
+        // And the parameters from the function call.
+        extract($_namedarguments);
+
+        // Include the template, capturing the output.
+        ob_start();
+        include($_template);
+        $_result = ob_get_contents();
+        ob_end_clean();
+
+        return $_result;
+    }
+
+    /**
+     * Searches the folders in {@link $searchpaths} to try to find a template for
+     * this method name. Throws an exception if one cannot be found.
+     * @param string $method the method name.
+     * @return string the full path of the template to use.
+     */
+    protected function find_template($method) {
+        foreach ($this->searchpaths as $path) {
+            $filename = $path . '/' . $method . '.php';
+            if (file_exists($filename)) {
+                return $filename;
+            }
+        }
+        throw new coding_exception('Cannot find template for ' . $this->copiedclass->getName() . '::' . $method);
+    }
+
+    /**
+     * Handle methods like print_box_start by using the print_box template,
+     * splitting the result, pushing the end onto the stack, then returning the start.
+     * @param string $method the method that was called, with _start stripped off.
+     * @param array $arguments the arguments that were passed to it.
+     * @return string the HTML to be output.
+     */
+    protected function process_start($method, $arguments) {
+        array_unshift($arguments, self::CONTENTSTOKEN);
+        $html = $this->process_template($method, $arguments);
+        list($start, $end) = explode(self::CONTENTSTOKEN, $html, 2);
+        $this->opencontainers->push($method, $end);
+        return $start;
+    }
+
+    /**
+     * Handle methods like print_box_end, we just need to pop the end HTML from
+     * the stack.
+     * @param string $method the method that was called, with _end stripped off.
+     * @param array $arguments not used. Assumed to be irrelevant.
+     * @return string the HTML to be output.
+     */
+    protected function process_end($method, $arguments) {
+        return $this->opencontainers->pop($method);
+    }
+
+    /**
+     * @return array the list of paths where this class searches for templates.
+     */
+    public function get_search_paths() {
+        return $this->searchpaths;
+    }
+
+    /**
+     * @return string the name of the class whose API we are copying.
+     */
+    public function get_copied_class() {
+        return $this->copiedclass->getName();
+    }
+}
+
+/**
+ * The standard implementation of the moodle_core_renderer interface.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class moodle_core_renderer extends moodle_renderer_base {
+    /** @var string used in {@link header()}. */
+    const PERFORMANCE_INFO_TOKEN = '%%PERFORMANCEINFO%%';
+    /** @var string used in {@link header()}. */
+    const END_HTML_TOKEN = '%%ENDHTML%%';
+    /** @var string used in {@link header()}. */
+    const MAIN_CONTENT_TOKEN = '[MAIN CONTENT GOES HERE]';
+    /** @var string used to pass information from {@link doctype()} to {@link standard_head_html()}. */
+    protected $contenttype;
+    /** @var string used by {@link redirect_message()} method to communicate with {@link header()}. */
+    protected $metarefreshtag = '';
+
+    /**
+     * Get the DOCTYPE declaration that should be used with this page. Designed to
+     * be called in theme layout.php files.
+     * @return string the DOCTYPE declaration (and any XML prologue) that should be used.
+     */
+    public function doctype() {
+        global $CFG;
+
+        $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n";
+        $this->contenttype = 'text/html; charset=utf-8';
+
+        if (empty($CFG->xmlstrictheaders)) {
+            return $doctype;
+        }
+
+        // We want to serve the page with an XML content type, to force well-formedness errors to be reported.
+        $prolog = '<?xml version="1.0" encoding="utf-8"?>' . "\n";
+        if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'application/xhtml+xml') !== false) {
+            // Firefox and other browsers that can cope natively with XHTML.
+            $this->contenttype = 'application/xhtml+xml; charset=utf-8';
+
+        } else if (preg_match('/MSIE.*Windows NT/', $_SERVER['HTTP_USER_AGENT'])) {
+            // IE can't cope with application/xhtml+xml, but it will cope if we send application/xml with an XSL stylesheet.
+            $this->contenttype = 'application/xml; charset=utf-8';
+            $prolog .= '<?xml-stylesheet type="text/xsl" href="' . $CFG->httpswwwroot . '/lib/xhtml.xsl"?>' . "\n";
+
+        } else {
+            $prolog = '';
+        }
+
+        return $prolog . $doctype;
+    }
+
+    /**
+     * The attributes that should be added to the <html> tag. Designed to
+     * be called in theme layout.php files.
+     * @return string HTML fragment.
+     */
+    public function htmlattributes() {
+        return get_html_lang(true) . ' xmlns="http://www.w3.org/1999/xhtml"';
+    }
+
+    /**
+     * The standard tags (meta tags, links to stylesheets and JavaScript, etc.)
+     * that should be included in the <head> tag. Designed to be called in theme
+     * layout.php files.
+     * @return string HTML fragment.
+     */
+    public function standard_head_html() {
+        global $CFG;
+        $output = '';
+        $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . "\n";
+        $output .= '<meta name="keywords" content="moodle, ' . $this->page->title . '" />' . "\n";
+        if (!$this->page->cacheable) {
+            $output .= '<meta http-equiv="pragma" content="no-cache" />' . "\n";
+            $output .= '<meta http-equiv="expires" content="0" />' . "\n";
+        }
+        // This is only set by the {@link redirect()} method
+        $output .= $this->metarefreshtag;
+
+        // Check if a periodic refresh delay has been set and make sure we arn't
+        // already meta refreshing
+        if ($this->metarefreshtag=='' && $this->page->periodicrefreshdelay!==null) {
+            $output .= '<meta http-equiv="refresh" content="'.$this->page->periodicrefreshdelay.';url='.$this->page->url->out().'" />';
+        }
+
+        $this->page->requires->js('lib/javascript-static.js')->in_head();
+        $this->page->requires->js('lib/javascript-deprecated.js')->in_head();
+        $this->page->requires->js('lib/javascript-mod.php')->in_head();
+        $this->page->requires->js('lib/overlib/overlib.js')->in_head();
+        $this->page->requires->js('lib/overlib/overlib_cssstyle.js')->in_head();
+        $this->page->requires->js('lib/cookies.js')->in_head();
+        $this->page->requires->js_function_call('setTimeout', Array('fix_column_widths()', 20));
+
+        $focus = $this->page->focuscontrol;
+        if (!empty($focus)) {
+            if (preg_match("#forms\['([a-zA-Z0-9]+)'\].elements\['([a-zA-Z0-9]+)'\]#", $focus, $matches)) {
+                // This is a horrifically bad way to handle focus but it is passed in
+                // through messy formslib::moodleform
+                $this->page->requires->js_function_call('old_onload_focus', Array($matches[1], $matches[2]));
+            } else if (strpos($focus, '.')!==false) {
+                // Old style of focus, bad way to do it
+                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);
+                $this->page->requires->js_function_call('old_onload_focus', explode('.', $focus, 2));
+            } else {
+                // Focus element with given id
+                $this->page->requires->js_function_call('focuscontrol', Array($focus));
+            }
+        }
+
+        // Add the meta tags from the themes if any were requested.
+        $output .= $this->page->theme->get_meta_tags($this->page);
+
+        // Get any HTML from the page_requirements_manager.
+        $output .= $this->page->requires->get_head_code();
+
+        // List alternate versions.
+        foreach ($this->page->alternateversions as $type => $alt) {
+            $output .= $this->output_empty_tag('link', array('rel' => 'alternate',
+                    'type' => $type, 'title' => $alt->title, 'href' => $alt->url));
+        }
+
+        return $output;
+    }
+
+    /**
+     * The standard tags (typically skip links) that should be output just inside
+     * the start of the <body> tag. Designed to be called in theme layout.php files.
+     * @return string HTML fragment.
+     */
+    public function standard_top_of_body_html() {
+        return  $this->page->requires->get_top_of_body_code();
+    }
+
+    /**
+     * The standard tags (typically performance information and validation links,
+     * if we are in developer debug mode) that should be output in the footer area
+     * of the page. Designed to be called in theme layout.php files.
+     * @return string HTML fragment.
+     */
+    public function standard_footer_html() {
+        global $CFG;
+
+        // This function is normally called from a layout.php file in {@link header()}
+        // but some of the content won't be known until later, so we return a placeholder
+        // for now. This will be replaced with the real content in {@link footer()}.
+        $output = self::PERFORMANCE_INFO_TOKEN;
+        if (!empty($CFG->debugpageinfo)) {
+            $output .= '<div class="performanceinfo">This page is: ' . $this->page->debug_summary() . '</div>';
+        }
+        if (!empty($CFG->debugvalidators)) {
+            $output .= '<div class="validators"><ul>
+              <li><a href="http://validator.w3.org/check?verbose=1&amp;ss=1&amp;uri=' . urlencode(qualified_me()) . '">Validate HTML</a></li>
+              <li><a href="http://www.contentquality.com/mynewtester/cynthia.exe?rptmode=-1&amp;url1=' . urlencode(qualified_me()) . '">Section 508 Check</a></li>
+              <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>
+            </ul></div>';
+        }
+        return $output;
+    }
+
+    /**
+     * The standard tags (typically script tags that are not needed earlier) that
+     * should be output after everything else, . Designed to be called in theme layout.php files.
+     * @return string HTML fragment.
+     */
+    public function standard_end_of_body_html() {
+        // This function is normally called from a layout.php file in {@link header()}
+        // but some of the content won't be known until later, so we return a placeholder
+        // for now. This will be replaced with the real content in {@link footer()}.
+        echo self::END_HTML_TOKEN;
+    }
+
+    /**
+     * Return the standard string that says whether you are logged in (and switched
+     * roles/logged in as another user).
+     * @return string HTML fragment.
+     */
+    public function login_info() {
+        global $USER;
+        return user_login_string($this->page->course, $USER);
+    }
+
+    /**
+     * Return the 'back' link that normally appears in the footer.
+     * @return string HTML fragment.
+     */
+    public function home_link() {
+        global $CFG, $SITE;
+
+        if ($this->page->pagetype == 'site-index') {
+            // Special case for site home page - please do not remove
+            return '<div class="sitelink">' .
+                   '<a title="Moodle ' . $CFG->release . '" href="http://moodle.org/">' .
+                   '<img style="width:100px;height:30px" src="' . $CFG->httpswwwroot . '/pix/moodlelogo.gif" alt="moodlelogo" /></a></div>';
+
+        } else if (!empty($CFG->target_release) && $CFG->target_release != $CFG->release) {
+            // Special case for during install/upgrade.
+            return '<div class="sitelink">'.
+                   '<a title="Moodle ' . $CFG->target_release . '" href="http://docs.moodle.org/en/Administrator_documentation" onclick="this.target=\'_blank\'">' .
+                   '<img style="width:100px;height:30px" src="' . $CFG->httpswwwroot . '/pix/moodlelogo.gif" alt="moodlelogo" /></a></div>';
+
+        } else if ($this->page->course->id == $SITE->id || strpos($this->page->pagetype, 'course-view') === 0) {
+            return '<div class="homelink"><a href="' . $CFG->wwwroot . '/">' .
+                    get_string('home') . '</a></div>';
+
+        } else {
+            return '<div class="homelink"><a href="' . $CFG->wwwroot . '/course/view.php?id=' . $this->page->course->id . '">' .
+                    format_string($this->page->course->shortname) . '</a></div>';
+        }
+    }
+
+    /**
+     * Redirects the user by any means possible given the current state
+     *
+     * This function should not be called directly, it should always be called using
+     * the redirect function in lib/weblib.php
+     *
+     * The redirect function should really only be called before page output has started
+     * however it will allow itself to be called during the state STATE_IN_BODY
+     *
+     * @param string $encodedurl The URL to send to encoded if required
+     * @param string $message The message to display to the user if any
+     * @param int $delay The delay before redirecting a user, if $message has been
+     *         set this is a requirement and defaults to 3, set to 0 no delay
+     * @param boolean $debugdisableredirect this redirect has been disabled for
+     *         debugging purposes. Display a message that explains, and don't
+     *         trigger the redirect.
+     * @return string The HTML to display to the user before dying, may contain
+     *         meta refresh, javascript refresh, and may have set header redirects
+     */
+    public function redirect_message($encodedurl, $message, $delay, $debugdisableredirect) {
+        global $CFG;
+        $url = str_replace('&amp;', '&', $encodedurl);
+
+        switch ($this->page->state) {
+            case moodle_page::STATE_BEFORE_HEADER :
+                // No output yet it is safe to delivery the full arsenal of redirect methods
+                if (!$debugdisableredirect) {
+                    // Don't use exactly the same time here, it can cause problems when both redirects fire at the same time.
+                    $this->metarefreshtag = '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />'."\n";
+                    $this->page->requires->js_function_call('document.location.replace', array($url))->after_delay($delay + 3);
+                }
+                $output = $this->header();
+                break;
+            case moodle_page::STATE_PRINTING_HEADER :
+                // We should hopefully never get here
+                throw new coding_exception('You cannot redirect while printing the page header');
+                break;
+            case moodle_page::STATE_IN_BODY :
+                // We really shouldn't be here but we can deal with this
+                debugging("You should really redirect before you start page output");
+                if (!$debugdisableredirect) {
+                    $this->page->requires->js_function_call('document.location.replace', array($url))->after_delay($delay);
+                }
+                $output = $this->opencontainers->pop_all_but_last();
+                break;
+            case moodle_page::STATE_DONE :
+                // Too late to be calling redirect now
+                throw new coding_exception('You cannot redirect after the entire page has been generated');
+                break;
+        }
+        $output .= $this->notification($message, 'redirectmessage');
+        $output .= '<a href="'. $encodedurl .'">'. get_string('continue') .'</a>';
+        if ($debugdisableredirect) {
+            $output .= '<p><strong>Error output, so disabling automatic redirect.</strong></p>';
+        }
+        $output .= $this->footer();
+        return $output;
+    }
+
+    /**
+     * Start output by sending the HTTP headers, and printing the HTML <head>
+     * and the start of the <body>.
+     *
+     * To control what is printed, you should set properties on $PAGE. If you
+     * are familiar with the old {@link print_header()} function from Moodle 1.9
+     * you will find that there are properties on $PAGE that correspond to most
+     * of the old parameters to could be passed to print_header.
+     *
+     * Not that, in due course, the remaining $navigation, $menu parameters here
+     * will be replaced by more properties of $PAGE, but that is still to do.
+     *
+     * @param string $navigation legacy, like the old parameter to print_header. Will be
+     *      removed when there is a $PAGE->... replacement.
+     * @param string $menu legacy, like the old parameter to print_header. Will be
+     *      removed when there is a $PAGE->... replacement.
+     * @return string HTML that you must output this, preferably immediately.
+     */
+    public function header($navigation = '', $menu='') {
+        // TODO remove $navigation and $menu arguments - replace with $PAGE->navigation
+        global $USER, $CFG;
+
+        $this->page->set_state(moodle_page::STATE_PRINTING_HEADER);
+
+        // Find the appropriate page template, based on $this->page->generaltype.
+        $templatefile = $this->page->theme->template_for_page($this->page->generaltype);
+        if ($templatefile) {
+            // Render the template.
+            $template = $this->render_page_template($templatefile, $menu, $navigation);
+        } else {
+            // New style template not found, fall back to using header.html and footer.html.
+            $template = $this->handle_legacy_theme($navigation, $menu);
+        }
+
+        // Slice the template output into header and footer.
+        $cutpos = strpos($template, self::MAIN_CONTENT_TOKEN);
+        if ($cutpos === false) {
+            throw new coding_exception('Layout template ' . $templatefile .
+                    ' does not contain the string "' . self::MAIN_CONTENT_TOKEN . '".');
+        }
+        $header = substr($template, 0, $cutpos);
+        $footer = substr($template, $cutpos + strlen(self::MAIN_CONTENT_TOKEN));
+
+        if (empty($this->contenttype)) {
+            debugging('The layout template did not call $OUTPUT->doctype()');
+            $this->doctype();
+        }
+
+        send_headers($this->contenttype, $this->page->cacheable);
+        $this->opencontainers->push('header/footer', $footer);
+        $this->page->set_state(moodle_page::STATE_IN_BODY);
+        return $header . $this->skip_link_target();
+    }
+
+    /**
+     * Renders and outputs the page template.
+     * @param string $templatefile The name of the template's file
+     * @param array $menu The menu that will be used in the included file
+     * @param array $navigation The navigation that will be used in the included file
+     * @return string HTML code
+     */
+    protected function render_page_template($templatefile, $menu, $navigation) {
+        global $CFG, $SITE, $THEME, $USER;
+        // The next lines are a bit tricky. The point is, here we are in a method
+        // of a renderer class, and this object may, or may not, be the same as
+        // the global $OUTPUT object. When rendering the template, we want to use
+        // this object. However, people writing Moodle code expect the current
+        // renderer to be called $OUTPUT, not $this, so define a variable called
+        // $OUTPUT pointing at $this. The same comment applies to $PAGE and $COURSE.
+        $OUTPUT = $this;
+        $PAGE = $this->page;
+        $COURSE = $this->page->course;
+
+        ob_start();
+        include($templatefile);
+        $template = ob_get_contents();
+        ob_end_clean();
+        return $template;
+    }
+
+    /**
+     * Renders and outputs a legacy template.
+     * @param array $navigation The navigation that will be used in the included file
+     * @param array $menu The menu that will be used in the included file
+     * @return string HTML code
+     */
+    protected function handle_legacy_theme($navigation, $menu) {
+        global $CFG, $SITE, $USER;
+        // Set a pretend global from the properties of this class.
+        // See the comment in render_page_template for a fuller explanation.
+        $COURSE = $this->page->course;
+        $THEME = $this->page->theme;
+
+        // Set up local variables that header.html expects.
+        $direction = $this->htmlattributes();
+        $title = $this->page->title;
+        $heading = $this->page->heading;
+        $focus = $this->page->focuscontrol;
+        $button = $this->page->button;
+        $pageid = $this->page->pagetype;
+        $pageclass = $this->page->bodyclasses;
+        $bodytags = ' class="' . $pageclass . '" id="' . $pageid . '"';
+        $home = $this->page->generaltype == 'home';
+
+        $meta = $this->standard_head_html();
+        // The next line is a nasty hack. having set $meta to standard_head_html, we have already
+        // got the contents of include($CFG->javascript). However, legacy themes are going to
+        // include($CFG->javascript) again. We want to make sure that when they do, nothing is output.
+        $CFG->javascript = $CFG->libdir . '/emptyfile.php';
+
+        // Set up local variables that footer.html expects.
+        $homelink = $this->home_link();
+        $loggedinas = $this->login_info();
+        $course = $this->page->course;
+        $performanceinfo = self::PERFORMANCE_INFO_TOKEN;
+
+        if (!$menu && $navigation) {
+            $menu = $loggedinas;
+        }
+
+        if (!empty($this->page->theme->layouttable)) {
+            $lt = $this->page->theme->layouttable;
+        } else {
+            $lt = array('left', 'middle', 'right');
+        }
+
+        if (!empty($this->page->theme->block_l_max_width)) {
+            $preferredwidthleft = $this->page->theme->block_l_max_width;
+        } else {
+            $preferredwidthleft = 210;
+        }
+        if (!empty($this->page->theme->block_r_max_width)) {
+            $preferredwidthright = $this->page->theme->block_r_max_width;
+        } else {
+            $preferredwidthright = 210;
+        }
+
+        ob_start();
+        include($this->page->theme->dir . '/header.html');
+
+        echo '<table id="layout-table"><tr>';
+        foreach ($lt as $column) {
+            if ($column == 'left' && $this->page->blocks->region_has_content(BLOCK_POS_LEFT, $this)) {
+                echo '<td id="left-column" class="block-region" style="width: ' . $preferredwidthright . 'px; vertical-align: top;">';
+                echo $this->container_start();
+                echo $this->blocks_for_region(BLOCK_POS_LEFT);
+                echo $this->container_end();
+                echo '</td>';
+
+            } else if ($column == 'middle') {
+                echo '<td id="middle-column" style="vertical-align: top;">';
+                echo $this->container_start();
+                echo $this->skip_link_target();
+                echo self::MAIN_CONTENT_TOKEN;
+                echo $this->container_end();
+                echo '</td>';
+
+            } else if ($column == 'right' && $this->page->blocks->region_has_content(BLOCK_POS_RIGHT, $this)) {
+                echo '<td id="right-column" class="block-region" style="width: ' . $preferredwidthright . 'px; vertical-align: top;">';
+                echo $this->container_start();
+                echo $this->blocks_for_region(BLOCK_POS_RIGHT);
+                echo $this->container_end();
+                echo '</td>';
+            }
+        }
+        echo '</tr></table>';
+
+        $menu = str_replace('navmenu', 'navmenufooter', $menu);
+        include($THEME->dir . '/footer.html');
+
+        $output = ob_get_contents();
+        ob_end_clean();
+
+        // Put in the start of body code. Bit of a hack, put it in before the first
+        // <div or <table.
+        $divpos = strpos($output, '<div');
+        $tablepos = strpos($output, '<table');
+        if ($divpos === false || ($tablepos !== false && $tablepos < $divpos)) {
+            $pos = $tablepos;
+        } else {
+            $pos = $divpos;
+        }
+        $output = substr($output, 0, $divpos) . $this->standard_top_of_body_html() .
+                substr($output, $divpos);
+
+        // Put in the end token before the end of body.
+        $output = str_replace('</body>', self::END_HTML_TOKEN . '</body>', $output);
+
+        // Make sure we use the correct doctype.
+        $output = preg_replace('/(<!DOCTYPE.+?>)/s', $this->doctype(), $output);
+
+        return $output;
+    }
+
+    /**
+     * Outputs the page's footer
+     * @return string HTML fragment
+     */
+    public function footer() {
+        $output = $this->opencontainers->pop_all_but_last(true);
+
+        $footer = $this->opencontainers->pop('header/footer');
+
+        // Provide some performance info if required
+        $performanceinfo = '';
+        if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) {
+            $perf = get_performance_info();
+            if (defined('MDL_PERFTOLOG') && !function_exists('register_shutdown_function')) {
+                error_log("PERF: " . $perf['txt']);
+            }
+            if (defined('MDL_PERFTOFOOT') || debugging() || $CFG->perfdebug > 7) {
+                $performanceinfo = $perf['html'];
+            }
+        }
+        $footer = str_replace(self::PERFORMANCE_INFO_TOKEN, $performanceinfo, $footer);
+
+        $footer = str_replace(self::END_HTML_TOKEN, $this->page->requires->get_end_code(), $footer);
+
+        $this->page->set_state(moodle_page::STATE_DONE);
+
+
+        return $output . $footer;
+    }
+
+    /**
+     * Output the row of editing icons for a block, as defined by the controls array.
+     * @param array $controls an array like {@link block_contents::$controls}.
+     * @return HTML fragment.
+     */
+    public function block_controls($controls) {
+        if (empty($controls)) {
+            return '';
+        }
+        $controlshtml = array();
+        foreach ($controls as $control) {
+            $controlshtml[] = $this->output_tag('a', array('class' => 'icon',
+                    'title' => $control['caption'], 'href' => $control['url']),
+                    $this->output_empty_tag('img',  array('src' => $this->old_icon_url($control['icon']),
+                    'alt' => $control['caption'])));
+        }
+        return $this->output_tag('div', array('class' => 'commands'), implode('', $controlshtml));
+    }
+
+    /**
+     * Prints a nice side block with an optional header.
+     *
+     * The content is described
+     * by a {@link block_contents} object.
+     *
+     * @param block_contents $bc HTML for the content
+     * @param string $region the region the block is appearing in.
+     * @return string the HTML to be output.
+     */
+    function block($bc, $region) {
+        $bc = clone($bc); // Avoid messing up the object passed in.
+        $bc->prepare();
+
+        $skiptitle = strip_tags($bc->title);
+        if (empty($skiptitle)) {
+            $output = '';
+            $skipdest = '';
+        } else {
+            $output = $this->output_tag('a', array('href' => '#sb-' . $bc->skipid, 'class' => 'skip-block'),
+                    get_string('skipa', 'access', $skiptitle));
+            $skipdest = $this->output_tag('span', array('id' => 'sb-' . $bc->skipid, 'class' => 'skip-block-to'), '');
+        }
+
+        $bc->attributes['id'] = $bc->id;
+        $bc->attributes['class'] = $bc->get_classes_string();
+        $output .= $this->output_start_tag('div', $bc->attributes);
+
+        $controlshtml = $this->block_controls($bc->controls);
+
+        $title = '';
+        if ($bc->title) {
+            $title = $this->output_tag('h2', null, $bc->title);
+        }
+
+        if ($title || $controlshtml) {
+            $output .= $this->output_tag('div', array('class' => 'header'),
+                    $this->output_tag('div', array('class' => 'title'),
+                    $title . $controlshtml));
+        }
+
+        $output .= $this->output_start_tag('div', array('class' => 'content'));
+        $output .= $bc->content;
+
+        if ($bc->footer) {
+            $output .= $this->output_tag('div', array('class' => 'footer'), $bc->footer);
+        }
+
+        $output .= $this->output_end_tag('div');
+        $output .= $this->output_end_tag('div');
+
+        if ($bc->annotation) {
+            $output .= $this->output_tag('div', array('class' => 'blockannotation'), $bc->annotation);
+        }
+        $output .= $skipdest;
+
+        $this->init_block_hider_js($bc);
+        return $output;
+    }
+
+    /**
+     * Calls the JS require function to hide a block.
+     * @param block_contents $bc A block_contents object
+     * @return void
+     */
+    protected function init_block_hider_js($bc) {
+        if ($bc->collapsible != block_contents::NOT_HIDEABLE) {
+            $userpref = 'block' . $bc->blockinstanceid . 'hidden';
+            user_preference_allow_ajax_update($userpref, PARAM_BOOL);
+            $this->page->requires->yui_lib('dom');
+            $this->page->requires->yui_lib('event');
+            $plaintitle = strip_tags($bc->title);
+            $this->page->requires->js_function_call('new block_hider', array($bc->id, $userpref,
+                    get_string('hideblocka', 'access', $plaintitle), get_string('showblocka', 'access', $plaintitle),
+                    $this->old_icon_url('t/switch_minus'), $this->old_icon_url('t/switch_plus')));
+        }
+    }
+
+    /**
+     * Render the contents of a block_list.
+     * @param array $icons the icon for each item.
+     * @param array $items the content of each item.
+     * @return string HTML
+     */
+    public function list_block_contents($icons, $items) {
+        $row = 0;
+        $lis = array();
+        foreach ($items as $key => $string) {
+            $item = $this->output_start_tag('li', array('class' => 'r' . $row));
+            if ($icons) {
+                $item .= $this->output_tag('div', array('class' => 'icon column c0'), $icons[$key]);
+            }
+            $item .= $this->output_tag('div', array('class' => 'column c1'), $string);
+            $item .= $this->output_end_tag('li');
+            $lis[] = $item;
+            $row = 1 - $row; // Flip even/odd.
+        }
+        return $this->output_tag('ul', array('class' => 'list'), implode("\n", $lis));
+    }
+
+    /**
+     * Output all the blocks in a particular region.
+     * @param string $region the name of a region on this page.
+     * @return string the HTML to be output.
+     */
+    public function blocks_for_region($region) {
+        $blockcontents = $this->page->blocks->get_content_for_region($region, $this);
+
+        $output = '';
+        foreach ($blockcontents as $bc) {
+            if ($bc instanceof block_contents) {
+                $output .= $this->block($bc, $region);
+            } else if ($bc instanceof block_move_target) {
+                $output .= $this->block_move_target($bc);
+            } else {
+                throw new coding_exception('Unexpected type of thing (' . get_class($bc) . ') found in list of block contents.');
+            }
+        }
+        return $output;
+    }
+
+    /**
+     * Output a place where the block that is currently being moved can be dropped.
+     * @param block_move_target $target with the necessary details.
+     * @return string the HTML to be output.
+     */
+    public function block_move_target($target) {
+        return $this->output_tag('a', array('href' => $target->url, 'class' => 'blockmovetarget'),
+                $this->output_tag('span', array('class' => 'accesshide'), $target->text));
+    }
+
+    /**
+     * Given a html_link object, outputs an <a> tag that uses the object's attributes.
+     *
+     * @param mixed $link A html_link object or a string URL (text param required in second case)
+     * @param string $text A descriptive text for the link. If $link is a html_link, this is not required.
+     * @return string HTML fragment
+     */
+    public function link($link, $text=null) {
+        $attributes = array();
+
+        if (is_a($link, 'html_link')) {
+            $link = clone($link);
+            $link->prepare();
+            $this->prepare_event_handlers($link);
+            $attributes['href'] = prepare_url($link->url);
+            $attributes['class'] = $link->get_classes_string();
+            $attributes['title'] = $link->title;
+            $attributes['id'] = $link->id;
+
+            $text = $link->text;
+
+        } else if (empty($text)) {
+            throw new coding_exception('$OUTPUT->link() must have a string as second parameter if the first param ($link) is a string');
+
+        } else {
+            $attributes['href'] = prepare_url($link);
+        }
+
+        return $this->output_tag('a', $attributes, $text);
+    }
+
+   /**
+    * Print a message along with button choices for Continue/Cancel. Labels default to Yes(Continue)/No(Cancel).
+    * If a string or moodle_url is given instead of a html_button, method defaults to post and text to Yes/No
+    * @param string $message The question to ask the user
+    * @param mixed $continue The html_form component representing the Continue answer. Can also be a moodle_url or string URL
+    * @param mixed $cancel The html_form component representing the Cancel answer. Can also be a moodle_url or string URL
+    * @return string HTML fragment
+    */
+    public function confirm($message, $continue, $cancel) {
+        if ($continue instanceof html_form) {
+            $continue = clone($continue);
+        } else if (is_string($continue)) {
+            $continueform = new html_form();
+            $continueform->url = new moodle_url($continue);
+            $continue = $continueform;
+        } else if ($continue instanceof moodle_url) {
+            $continueform = new html_form();
+            $continueform->url = $continue;
+            $continue = $continueform;
+        } else {
+            throw new coding_exception('The continue param to $OUTPUT->confirm must be either a URL (string/moodle_url) or a html_form object.');
+        }
+
+        if ($cancel instanceof html_form) {
+            $cancel = clone($cancel);
+        } else if (is_string($cancel)) {
+            $cancelform = new html_form();
+            $cancelform->url = new moodle_url($cancel);
+            $cancel = $cancelform;
+        } else if ($cancel instanceof moodle_url) {
+            $cancelform = new html_form();
+            $cancelform->url = $cancel;
+            $cancel = $cancelform;
+        } else {
+            throw new coding_exception('The cancel param to $OUTPUT->confirm must be either a URL (string/moodle_url) or a html_form object.');
+        }
+
+        if (empty($continue->button->text)) {
+            $continue->button->text = get_string('yes');
+        }
+        if (empty($cancel->button->text)) {
+            $cancel->button->text = get_string('no');
+        }
+
+        $output = $this->box_start('generalbox', 'notice');
+        $output .= $this->output_tag('p', array(), $message);
+        $output .= $this->output_tag('div', array('class' => 'buttons'), $this->button($continue) . $this->button($cancel));
+        $output .= $this->box_end();
+        return $output;
+    }
+
+    /**
+     * Given a html_form object, outputs an <input> tag within a form that uses the object's attributes.
+     *
+     * @param html_form $form A html_form object
+     * @return string HTML fragment
+     */
+    public function button($form) {
+        if (empty($form->button) or !($form->button instanceof html_button)) {
+            throw new coding_exception('$OUTPUT->button($form) requires $form to have a button (html_button) value');
+        }
+        $form = clone($form);
+        $form->button->prepare();
+
+        $this->prepare_event_handlers($form->button);
+
+        $buttonattributes = array('class' => $form->button->get_classes_string(),
+                                  'type' => 'submit',
+                                  'value' => $form->button->text,
+                                  'disabled' => $form->button->disabled,
+                                  'id' => $form->button->id);
+
+        $buttonoutput = $this->output_empty_tag('input', $buttonattributes);
+
+        // Removing the button so it doesn't get output again
+        unset($form->button);
+
+        return $this->form($form, $buttonoutput);
+    }
+
+    /**
+     * Given a html_form component and an optional rendered submit button,
+     * outputs a HTML form with correct divs and inputs and a single submit button.
+     * This doesn't render any other visible inputs. Use moodleforms for these.
+     * @param html_form $form A html_form instance
+     * @param string $contents HTML fragment to put inside the form. If given, must contain at least the submit button.
+     * @return string HTML fragment
+     */
+    public function form($form, $contents=null) {
+        $form = clone($form);
+        $form->prepare();
+        $this->prepare_event_handlers($form);
+        $buttonoutput = null;
+
+        if (empty($contents) && !empty($form->button)) {
+            debugging("You probably want to use \$OUTPUT->button(\$form), please read that function's documentation", DEBUG_DEVELOPER);
+        } else if (empty($contents)) {
+            $contents = $this->output_empty_tag('input', array('type' => 'submit', 'value' => get_string('ok')));
+        } else if (!empty($form->button)) {
+            $form->button->prepare();
+            $buttonoutput = $this->output_start_tag('div', array('id' => "noscript$form->id"));
+            $this->prepare_event_handlers($form->button);
+
+            $buttonattributes = array('class' => $form->button->get_classes_string(),
+                                      'type' => 'submit',
+                                      'value' => $form->button->text,
+                                      'disabled' => $form->button->disabled,
+                                      'id' => $form->button->id);
+
+            $buttonoutput .= $this->output_empty_tag('input', $buttonattributes);
+            $buttonoutput .= $this->output_end_tag('div');
+            $this->page->requires->js_function_call('hide_item', array("noscript$form->id"));
+
+        }
+
+        $hiddenoutput = '';
+
+        foreach ($form->url->params() as $var => $val) {
+            $hiddenoutput .= $this->output_empty_tag('input', array('type' => 'hidden', 'name' => $var, 'value' => $val));
+        }
+
+        $formattributes = array(
+                'method' => $form->method,
+                'action' => prepare_url($form->url, true),
+                'id' => $form->id,
+                'class' => $form->get_classes_string());
+
+        $divoutput = $this->output_tag('div', array(), $hiddenoutput . $contents . $buttonoutput);
+        $formoutput = $this->output_tag('form', $formattributes, $divoutput);
+        $output = $this->output_tag('div', array('class' => 'singlebutton'), $formoutput);
+
+        return $output;
+    }
+
+    /**
+     * Returns a string containing a link to the user documentation.
+     * Also contains an icon by default. Shown to teachers and admin only.
+     * @param string $path The page link after doc root and language, no leading slash.
+     * @param string $text The text to be displayed for the link
+     * @param string $iconpath The path to the icon to be displayed
+     */
+    public function doc_link($path, $text=false, $iconpath=false) {
+        global $CFG, $OUTPUT;
+        $icon = new action_icon();
+        $icon->linktext = $text;
+        $icon->image->alt = $text;
+        $icon->image->add_class('iconhelp');
+        $icon->link->url = new moodle_url(get_docs_url($path));
+
+        if (!empty($iconpath)) {
+            $icon->image->src = $iconpath;
+        } else {
+            $icon->image->src = $this->old_icon_url('docs');
+        }
+
+        if (!empty($CFG->doctonewwindow)) {
+            $icon->actions[] = new popup_action('click', $icon->link->url);
+        }
+
+        return $this->action_icon($icon);
+
+    }
+
+    /**
+     * Given a action_icon object, outputs an image linking to an action (URL or AJAX).
+     *
+     * @param action_icon $icon An action_icon object
+     * @return string HTML fragment
+     */
+    public function action_icon($icon) {
+        $icon = clone($icon);
+        $icon->prepare();
+        $imageoutput = $this->image($icon->image);
+
+        if ($icon->linktext) {
+            $imageoutput .= $icon->linktext;
+        }
+        $icon->link->text = $imageoutput;
+
+        return $this->link($icon->link);
+    }
+
+    /*
+     * Centered heading with attached help button (same title text)
+     * and optional icon attached
+     * @param help_icon $helpicon A help_icon object
+     * @param mixed $image An image URL or a html_image object
+     * @return string HTML fragment
+     */
+    public function heading_with_help($helpicon, $image=false) {
+        if (!($image instanceof html_image) && !empty($image)) {
+            $htmlimage = new html_image();
+            $htmlimage->src = $image;
+            $image = $htmlimage;
+        }
+        return $this->container($this->image($image) . $this->heading($helpicon->text, 2, 'main help') . $this->help_icon($helpicon), 'heading-with-help');
+    }
+
+    /**
+     * Print a help icon.
+     *
+     * @param help_icon $helpicon A help_icon object, subclass of html_link
+     *
+     * @return string  HTML fragment
+     */
+    public function help_icon($icon) {
+        global $COURSE;
+        $icon = clone($icon);
+        $icon->prepare();
+
+        $popup = new popup_action('click', $icon->link->url);
+        $icon->link->add_action($popup);
+
+        $image = null;
+
+        if (!empty($icon->image)) {
+            $image = $icon->image;
+            $image->add_class('iconhelp');
+        }
+
+        return $this->output_tag('span', array('class' => 'helplink'), $this->link_to_popup($icon->link, $image));
+    }
+
+    /**
+     * Creates and returns a button to a popup window
+     *
+     * @param html_link $link Subclass of moodle_html_component
+     * @param moodle_popup $popup A moodle_popup object
+     * @param html_image $image An optional image replacing the link text
+     *
+     * @return string HTML fragment
+     */
+    public function link_to_popup($link, $image=null) {
+        $link = clone($link);
+        $link->prepare();
+
+        $this->prepare_event_handlers($link);
+
+        if (empty($link->url)) {
+            throw new coding_exception('Called $OUTPUT->link_to_popup($link) method without $link->url set.');
+        }
+
+        $linkurl = prepare_url($link->url);
+
+        $tagoptions = array(
+                'title' => $link->title,
+                'id' => $link->id,
+                'href' => ($linkurl) ? $linkurl : prepare_url($popup->url),
+                'class' => $link->get_classes_string());
+
+        // Use image if one is given
+        if (!empty($image) && $image instanceof html_image) {
+
+            if (empty($image->alt)) {
+                $image->alt = $link->text;
+            }
+
+            $link->text = $this->image($image);
+
+            if (!empty($link->linktext)) {
+                $link->text = "$link->title &nbsp; $link->text";
+            }
+        }
+
+        return $this->output_tag('a', $tagoptions, $link->text);
+    }
+
+    /**
+     * Creates and returns a spacer image with optional line break.
+     *
+     * @param html_image $image Subclass of moodle_html_component
+     *
+     * @return string HTML fragment
+     */
+    public function spacer($image) {
+        $image = clone($image);
+        $image->prepare();
+        $image->add_class('spacer');
+
+        if (empty($image->src)) {
+            $image->src = $this->old_icon_url('spacer');
+        }
+
+        $output = $this->image($image);
+
+        return $output;
+    }
+
+    /**
+     * Creates and returns an image.
+     *
+     * @param html_image $image Subclass of moodle_html_component
+     *
+     * @return string HTML fragment
+     */
+    public function image($image) {
+        if ($image === false) {
+            return false;
+        }
+
+        $image = clone($image);
+        $image->prepare();
+
+        $this->prepare_event_handlers($image);
+
+        $attributes = array('class' => $image->get_classes_string(),
+                            'src' => prepare_url($image->src),
+                            'alt' => $image->alt,
+                            'style' => $image->style,
+                            'title' => $image->title,
+                            'id' => $image->id);
+
+        if (!empty($image->height) || !empty($image->width)) {
+            $attributes['style'] .= $this->prepare_legacy_width_and_height($image);
+        }
+        return $this->output_empty_tag('img', $attributes);
+    }
+
+    /**
+     * Print the specified user's avatar.
+     *
+     * This method can be used in two ways:
+     * <pre>
+     * // Option 1:
+     * $userpic = new user_picture();
+     * // Set properties of $userpic
+     * $OUTPUT->user_picture($userpic);
+     *
+     * // Option 2: (shortcut for simple cases)
+     * // $user has come from the DB and has fields id, picture, imagealt, firstname and lastname
+     * $OUTPUT->user_picture($user, $COURSE->id);
+     * </pre>
+     *
+     * @param object $userpic Object with at least fields id, picture, imagealt, firstname, lastname
+     *     If any of these are missing, or if a userid is passed, the database is queried. Avoid this
+     *     if at all possible, particularly for reports. It is very bad for performance.
+     *     A user_picture object is a better parameter.
+     * @param int $courseid courseid Used when constructing the link to the user's profile. Required if $userpic
+     *     is not a user_picture object
+     * @return string HTML fragment
+     */
+    public function user_picture($userpic, $courseid=null) {
+        // Instantiate a user_picture object if $user is not already one
+        if (!($userpic instanceof user_picture)) {
+            if (empty($courseid)) {
+                throw new coding_exception('Called $OUTPUT->user_picture with a $user object but no $courseid.');
+            }
+
+            $user = $userpic;
+            $userpic = new user_picture();
+            $userpic->user = $user;
+            $userpic->courseid = $courseid;
+        } else {
+            $userpic = clone($userpic);
+        }
+
+        $userpic->prepare();
+
+        $output = $this->image($userpic->image);
+
+        if (!empty($userpic->url)) {
+            $actions = $userpic->get_actions();
+            if ($userpic->popup && !empty($actions)) {
+                $link = new html_link();
+                $link->url = $userpic->url;
+                $link->text = fullname($userpic->user);
+                $link->title = fullname($userpic->user);
+
+                foreach ($actions as $action) {
+                    $link->add_action($action);
+                }
+                $output = $this->link_to_popup($link, $userpic->image);
+            } else {
+                $output = $this->link(prepare_url($userpic->url), $output);
+            }
+        }
+
+        return $output;
+    }
+
+    /**
+     * Prints the 'Update this Modulename' button that appears on module pages.
+     *
+     * @param string $cmid the course_module id.
+     * @param string $modulename the module name, eg. "forum", "quiz" or "workshop"
+     * @return string the HTML for the button, if this user has permission to edit it, else an empty string.
+     */
+    public function update_module_button($cmid, $modulename) {
+        global $CFG;
+        if (has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_MODULE, $cmid))) {
+            $modulename = get_string('modulename', $modulename);
+            $string = get_string('updatethis', '', $modulename);
+
+            $form = new html_form();
+            $form->url = new moodle_url("$CFG->wwwroot/course/mod.php", array('update' => $cmid, 'return' => true, 'sesskey' => sesskey()));
+            $form->button->text = $string;
+            return $this->button($form);
+        } else {
+            return '';
+        }
+    }
+
+    /**
+     * Prints a "Turn editing on/off" button in a form.
+     * @param moodle_url $url The URL + params to send through when clicking the button
+     * @return string HTML the button
+     */
+    public function edit_button(moodle_url $url) {
+        global $USER;
+        if (!empty($USER->editing)) {
+            $string = get_string('turneditingoff');
+            $edit = '0';
+        } else {
+            $string = get_string('turneditingon');
+            $edit = '1';
+        }
+
+        $form = new html_form();
+        $form->url = $url;
+        $form->url->param('edit', $edit);
+        $form->button->text = $string;
+
+        return $this->button($form);
+    }
+
+    /**
+     * Outputs a HTML nested list
+     *
+     * @param html_list $list A html_list object
+     * @return string HTML structure
+     */
+    public function htmllist($list) {
+        $list = clone($list);
+        $list->prepare();
+
+        $this->prepare_event_handlers($list);
+
+        if ($list->type == 'ordered') {
+            $tag = 'ol';
+        } else if ($list->type == 'unordered') {
+            $tag = 'ul';
+        }
+
+        $output = $this->output_start_tag($tag, array('class' => $list->get_classes_string()));
+
+        foreach ($list->items as $listitem) {
+            if ($listitem instanceof html_list) {
+                $output .= $this->output_start_tag('li', array());
+                $output .= $this->htmllist($listitem);
+                $output .= $this->output_end_tag('li');
+            } else if ($listitem instanceof html_list_item) {
+                $listitem->prepare();
+                $this->prepare_event_handlers($listitem);
+                $output .= $this->output_tag('li', array('class' => $listitem->get_classes_string()), $listitem->value);
+            }
+        }
+
+        return $output . $this->output_end_tag($tag);
+    }
+
+    /**
+     * Prints a simple button to close a window
+     *
+     * @global objec)t
+     * @param string $text The lang string for the button's label (already output from get_string())
+     * @return string|void if $return is true, void otherwise
+     */
+    public function close_window_button($text) {
+        if (empty($text)) {
+            $text = get_string('closewindow');
+        }
+        $closeform = new html_form();
+        $closeform->url = '#';
+        $closeform->button->text = $text;
+        $closeform->button->add_action('click', 'close_window');
+        $closeform->button->prepare();
+        return $this->container($this->button($closeform), 'closewindow');
+    }
+
+    /**
+     * Outputs a <select> menu or a list of radio/checkbox inputs.
+     *
+     * This method is extremely versatile, and can be used to output yes/no menus,
+     * form-enclosed menus with automatic redirects when an option is selected,
+     * descriptive labels and help icons. By default it just outputs a select
+     * menu.
+     *
+     * To add a descriptive label, use moodle_select::set_label($text, $for) or
+     * moodle_select::set_label($label) passing a html_label object
+     *
+     * To add a help icon, use moodle_select::set_help($page, $text, $linktext) or
+     * moodle_select::set_help($helpicon) passing a help_icon object
+     *
+     * If you moodle_select::$rendertype to "radio", it will render radio buttons
+     * instead of a <select> menu, unless $multiple is true, in which case it
+     * will render checkboxes.
+     *
+     * To surround the menu with a form, simply set moodle_select->form as a
+     * valid html_form object. Note that this function will NOT automatically
+     * add a form for non-JS browsers. If you do not set one up, it assumes
+     * that you are providing your own form in some other way.
+     *
+     * You can either call this function with a single moodle_select argument
+     * or, with a list of parameters, in which case those parameters are sent to
+     * the moodle_select constructor.
+     *
+     * @param moodle_select $select a moodle_select that describes
+     *      the select menu you want output.
+     * @return string the HTML for the <select>
+     */
+    public function select($select) {
+        $select = clone($select);
+        $select->prepare();
+
+        $this->prepare_event_handlers($select);
+
+        if (empty($select->id)) {
+            $select->id = 'menu' . str_replace(array('[', ']'), '', $select->name);
+        }
+
+        $attributes = array(
+            'name' => $select->name,
+            'id' => $select->id,
+            'class' => $select->get_classes_string()
+        );
+        if ($select->disabled) {
+            $attributes['disabled'] = 'disabled';
+        }
+        if ($select->tabindex) {
+            $attributes['tabindex'] = $tabindex;
+        }
+
+        if ($select->rendertype == 'menu' && $select->listbox) {
+            if (is_integer($select->listbox)) {
+                $size = $select->listbox;
+            } else {
+                $size = min($select->maxautosize, count($select->options));
+            }
+            $attributes['size'] = $size;
+            if ($select->multiple) {
+                $attributes['multiple'] = 'multiple';
+            }
+        }
+
+        $html = '';
+
+        if (!empty($select->label)) {
+            $html .= $this->label($select->label);
+        }
+
+        if (!empty($select->helpicon) && $select->helpicon instanceof help_icon) {
+            $html .= $this->help_icon($select->helpicon);
+        }
+
+        if ($select->rendertype == 'menu') {
+            $html .= $this->output_start_tag('select', $attributes) . "\n";
+
+            foreach ($select->options as $option) {
+                // $OUTPUT->select_option detects if $option is an option or an optgroup
+                $html .= $this->select_option($option);
+            }
+
+            $html .= $this->output_end_tag('select') . "\n";
+        } else if ($select->rendertype == 'radio') {
+            $currentradio = 0;
+            foreach ($select->options as $option) {
+                $html .= $this->radio($option, $select->name);
+                $currentradio++;
+            }
+        } else if ($select->rendertype == 'checkbox') {
+            $currentcheckbox = 0;
+            foreach ($select->options as $option) {
+                $html .= $this->checkbox($option, $select->name);
+                $currentcheckbox++;
+            }
+        }
+
+        if (!empty($select->form) && $select->form instanceof html_form) {
+            $html = $this->form($select->form, $html);
+        }
+
+        return $html;
+    }
+
+    /**
+     * Outputs a <input type="radio" /> element. Optgroups are ignored, so do not
+     * pass a html_select_optgroup as a param to this function.
+     *
+     * @param html_select_option $option a html_select_option
+     * @return string the HTML for the <input type="radio">
+     */
+    public function radio($option, $name='unnamed') {
+        if ($option instanceof html_select_optgroup) {
+            throw new coding_exception('$OUTPUT->radio($option) does not support a html_select_optgroup object as param.');
+        } else if (!($option instanceof html_select_option)) {
+            throw new coding_exception('$OUTPUT->radio($option) only accepts a html_select_option object as param.');
+        }
+        $option = clone($option);
+        $option->prepare();
+        $option->label->for = $option->id;
+        $this->prepare_event_handlers($option);
+
+        $output = $this->output_start_tag('span', array('class' => "radiogroup $select->name rb$currentradio")) . "\n";
+        $output .= $this->label($option->label);
+
+        if ($option->selected == 'selected') {
+            $option->selected = 'checked';
+        }
+
+        $output .= $this->output_empty_tag('input', array(
+                'type' => 'radio',
+                'value' => $option->value,
+                'name' => $name,
+                'alt' => $option->alt,
+                'id' => $option->id,
+                'class' => $option->get_classes_string(),
+                'checked' => $option->selected));
+
+        $output .= $this->output_end_tag('span');
+
+        return $output;
+    }
+
+    /**
+     * Outputs a <input type="checkbox" /> element. Optgroups are ignored, so do not
+     * pass a html_select_optgroup as a param to this function.
+     *
+     * @param html_select_option $option a html_select_option
+     * @return string the HTML for the <input type="checkbox">
+     */
+    public function checkbox($option, $name='unnamed') {
+        if ($option instanceof html_select_optgroup) {
+            throw new coding_exception('$OUTPUT->checkbox($option) does not support a html_select_optgroup object as param.');
+        } else if (!($option instanceof html_select_option)) {
+            throw new coding_exception('$OUTPUT->checkbox($option) only accepts a html_select_option object as param.');
+        }
+        $option = clone($option);
+        $option->prepare();
+
+        $option->label->for = $option->id;
+        $this->prepare_event_handlers($option);
+
+        $output = $this->output_start_tag('span', array('class' => "checkbox $name")) . "\n";
+
+        if ($option->selected == 'selected') {
+            $option->selected = 'checked';
+        }
+
+        $output .= $this->output_empty_tag('input', array(
+                'type' => 'checkbox',
+                'value' => $option->value,
+                'name' => $name,
+                'id' => $option->id,
+                'alt' => $option->alt,
+                'class' => $option->get_classes_string(),
+                'checked' => $option->selected));
+        $output .= $this->label($option->label);
+
+        $output .= $this->output_end_tag('span');
+
+        return $output;
+    }
+
+    /**
+     * Output an <option> or <optgroup> element. If an optgroup element is detected,
+     * this will recursively output its options as well.
+     *
+     * @param mixed $option a html_select_option or moodle_select_optgroup
+     * @return string the HTML for the <option> or <optgroup>
+     */
+    public function select_option($option) {
+        $option = clone($option);
+        $option->prepare();
+        $this->prepare_event_handlers($option);
+
+        if ($option instanceof html_select_option) {
+            return $this->output_tag('option', array(
+                    'value' => $option->value,
+                    'class' => $option->get_classes_string(),
+                    'selected' => $option->selected), $option->text);
+        } else if ($option instanceof html_select_optgroup) {
+            $output = $this->output_start_tag('optgroup', array('label' => $option->text, 'class' => $option->get_classes_string()));
+            foreach ($option->options as $selectoption) {
+                $output .= $this->select_option($selectoption);
+            }
+            $output .= $this->output_end_tag('optgroup');
+            return $output;
+        }
+    }
+
+    /**
+     * Output an <input type="text"> element
+     *
+     * @param html_field $field a html_field object
+     * @return string the HTML for the <input>
+     */
+    public function textfield($field) {
+        $field = clone($field);
+        $field->prepare();
+        $this->prepare_event_handlers($field);
+        $output = $this->output_start_tag('span', array('class' => "textfield $field->name"));
+        $output .= $this->output_empty_tag('input', array(
+                'type' => 'text',
+                'name' => $field->name,
+                'id' => $field->id,
+                'value' => $field->value,
+                'style' => $field->style,
+                'alt' => $field->alt,
+                'maxlength' => $field->maxlength));
+        $output .= $this->output_end_tag('span');
+        return $output;
+    }
+
+    /**
+     * Outputs a <label> element.
+     * @param html_label $label A html_label object
+     * @return HTML fragment
+     */
+    public function label($label) {
+        $label = clone($label);
+        $label->prepare();
+        $this->prepare_event_handlers($label);
+        return $this->output_tag('label', array('for' => $label->for, 'class' => $label->get_classes_string()), $label->text);
+    }
+
+    /**
+     * Output an error message. By default wraps the error message in <span class="error">.
+     * If the error message is blank, nothing is output.
+     * @param string $message the error message.
+     * @return string the HTML to output.
+     */
+    public function error_text($message) {
+        if (empty($message)) {
+            return '';
+        }
+        return $this->output_tag('span', array('class' => 'error'), $message);
+    }
+
+    /**
+     * Do not call this function directly.
+     *
+     * To terminate the current script with a fatal error, call the {@link print_error}
+     * function, or throw an exception. Doing either of those things will then call this
+     * function to display the error, before terminating the execution.
+     *
+     * @param string $message The message to output
+     * @param string $moreinfourl URL where more info can be found about the error
+     * @param string $link Link for the Continue button
+     * @param array $backtrace The execution backtrace
+     * @param string $debuginfo Debugging information
+     * @param bool $showerrordebugwarning Whether or not to show a debugging warning
+     * @return string the HTML to output.
+     */
+    public function fatal_error($message, $moreinfourl, $link, $backtrace,
+                $debuginfo = null, $showerrordebugwarning = false) {
+
+        $output = '';
+
+        if ($this->has_started()) {
+            $output .= $this->opencontainers->pop_all_but_last();
+        } else {
+            // Header not yet printed
+            @header('HTTP/1.0 404 Not Found');
+            $this->page->set_title(get_string('error'));
+            $output .= $this->header();
+        }
+
+        $message = '<p class="errormessage">' . $message . '</p>'.
+                '<p class="errorcode"><a href="' . $moreinfourl . '">' .
+                get_string('moreinformation') . '</a></p>';
+        $output .= $this->box($message, 'errorbox');
+
+        if (debugging('', DEBUG_DEVELOPER)) {
+            if ($showerrordebugwarning) {
+                $output .= $this->notification('error() is a deprecated function. ' .
+                        'Please call print_error() instead of error()', 'notifytiny');
+            }
+            if (!empty($debuginfo)) {
+                $output .= $this->notification($debuginfo, 'notifytiny');
+            }
+            if (!empty($backtrace)) {
+                $output .= $this->notification('Stack trace: ' .
+                        format_backtrace($backtrace), 'notifytiny');
+            }
+        }
+
+        if (!empty($link)) {
+            $output .= $this->continue_button($link);
+        }
+
+        $output .= $this->footer();
+
+        // Padding to encourage IE to display our error page, rather than its own.
+        $output .= str_repeat(' ', 512);
+
+        return $output;
+    }
+
+    /**
+     * Output a notification (that is, a status message about something that has
+     * just happened).
+     *
+     * @param string $message the message to print out
+     * @param string $classes normally 'notifyproblem' or 'notifysuccess'.
+     * @return string the HTML to output.
+     */
+    public function notification($message, $classes = 'notifyproblem') {
+        return $this->output_tag('div', array('class' =>
+                moodle_renderer_base::prepare_classes($classes)), clean_text($message));
+    }
+
+    /**
+     * Print a continue button that goes to a particular URL.
+     *
+     * @param string|moodle_url $link The url the button goes to.
+     * @return string the HTML to output.
+     */
+    public function continue_button($link) {
+        if (!is_a($link, 'moodle_url')) {
+            $link = new moodle_url($link);
+        }
+        $form = new html_form();
+        $form->url = $link;
+        $form->values = $link->params();
+        $form->button->text = get_string('continue');
+        $form->method = 'get';
+
+        return $this->output_tag('div', array('class' => 'continuebutton') , $this->button($form));
+    }
+
+    /**
+     * Prints a single paging bar to provide access to other pages  (usually in a search)
+     *
+     * @param string|moodle_url $link The url the button goes to.
+     * @return string the HTML to output.
+     */
+    public function paging_bar($pagingbar) {
+        $output = '';
+        $pagingbar = clone($pagingbar);
+        $pagingbar->prepare();
+
+        if ($pagingbar->totalcount > $pagingbar->perpage) {
+            $output .= get_string('page') . ':';
+
+            if (!empty($pagingbar->previouslink)) {
+                $output .= '&nbsp;(' . $this->link($pagingbar->previouslink) . ')&nbsp;';
+            }
+
+            if (!empty($pagingbar->firstlink)) {
+                $output .= '&nbsp;' . $this->link($pagingbar->firstlink) . '&nbsp;...';
+            }
+
+            foreach ($pagingbar->pagelinks as $link) {
+                if ($link instanceof html_link) {
+                    $output .= '&nbsp;&nbsp;' . $this->link($link);
+                } else {
+                    $output .= "&nbsp;&nbsp;$link";
+                }
+            }
+
+            if (!empty($pagingbar->lastlink)) {
+                $output .= '&nbsp;...' . $this->link($pagingbar->lastlink) . '&nbsp;';
+            }
+
+            if (!empty($pagingbar->nextlink)) {
+                $output .= '&nbsp;&nbsp;(' . $this->link($pagingbar->nextlink) . ')';
+            }
+        }
+
+        return $this->output_tag('div', array('class' => 'paging'), $output);
+    }
+
+    /**
+     * Render a HTML table
+     *
+     * @param object $table {@link html_table} instance containing all the information needed
+     * @return string the HTML to output.
+     */
+    public function table(html_table $table) {
+        $table = clone($table);
+        $table->prepare();
+        $attributes = array(
+                'id'            => $table->id,
+                'width'         => $table->width,
+                'summary'       => $table->summary,
+                'cellpadding'   => $table->cellpadding,
+                'cellspacing'   => $table->cellspacing,
+                'class'         => $table->get_classes_string());
+        $output = $this->output_start_tag('table', $attributes) . "\n";
+
+        $countcols = 0;
+
+        if (!empty($table->head)) {
+            $countcols = count($table->head);
+            $output .= $this->output_start_tag('thead', array()) . "\n";
+            $output .= $this->output_start_tag('tr', array()) . "\n";
+            $keys = array_keys($table->head);
+            $lastkey = end($keys);
+            foreach ($table->head as $key => $heading) {
+                $classes = array('header', 'c' . $key);
+                if (isset($table->headspan[$key]) && $table->headspan[$key] > 1) {
+                    $colspan = $table->headspan[$key];
+                    $countcols += $table->headspan[$key] - 1;
+                } else {
+                    $colspan = '';
+                }
+                if ($key == $lastkey) {
+                    $classes[] = 'lastcol';
+                }
+                if (isset($table->colclasses[$key])) {
+                    $classes[] = $table->colclasses[$key];
+                }
+                if ($table->rotateheaders) {
+                    // we need to wrap the heading content
+                    $heading = $this->output_tag('span', '', $heading);
+                }
+                $attributes = array(
+                        'style'     => $table->align[$key] . $table->size[$key] . 'white-space:nowrap;',
+                        'class'     => moodle_renderer_base::prepare_classes($classes),
+                        'scope'     => 'col',
+                        'colspan'   => $colspan);
+                $output .= $this->output_tag('th', $attributes, $heading) . "\n";
+            }
+            $output .= $this->output_end_tag('tr') . "\n";
+            $output .= $this->output_end_tag('thead') . "\n";
+        }
+
+        if (!empty($table->data)) {
+            $oddeven    = 1;
+            $keys       = array_keys($table->data);
+            $lastrowkey = end($keys);
+            $output .= $this->output_start_tag('tbody', array()) . "\n";
+
+            foreach ($table->data as $key => $row) {
+                if (($row === 'hr') && ($countcols)) {
+                    $output .= $this->output_tag('td', array('colspan' => $countcols),
+                                                 $this->output_tag('div', array('class' => 'tabledivider'), '')) . "\n";
+                } else {
+                    // Convert array rows to html_table_rows and cell strings to html_table_cell objects
+                    if (!($row instanceof html_table_row)) {
+                        $newrow = new html_table_row();
+
+                        foreach ($row as $unused => $item) {
+                            $cell = new html_table_cell();
+                            $cell->text = $item;
+                            $newrow->cells[] = $cell;
+                        }
+                        $row = $newrow;
+                    }
+
+                    $oddeven = $oddeven ? 0 : 1;
+                    if (isset($table->rowclasses[$key])) {
+                        $row->add_classes(array_unique(moodle_html_component::clean_classes($table->rowclasses[$key])));
+                    }
+
+                    $row->add_class('r' . $oddeven);
+                    if ($key == $lastrowkey) {
+                        $row->add_class('lastrow');
+                    }
+
+                    $output .= $this->output_start_tag('tr', array('class' => $row->get_classes_string(), 'style' => $row->style, 'id' => $row->id)) . "\n";
+                    $keys2 = array_keys($row->cells);
+                    $lastkey = end($keys2);
+
+                    foreach ($row->cells as $key => $cell) {
+                        if (isset($table->colclasses[$key])) {
+                            $cell->add_classes(array_unique(moodle_html_component::clean_classes($table->colclasses[$key])));
+                        }
+
+                        $cell->add_classes('cell');
+                        $cell->add_classes('c' . $key);
+                        if ($key == $lastkey) {
+                            $cell->add_classes('lastcol');
+                        }
+                        $tdstyle = '';
+                        $tdstyle .= isset($table->align[$key]) ? $table->align[$key] : '';
+                        $tdstyle .= isset($table->size[$key]) ? $table->size[$key] : '';
+                        $tdstyle .= isset($table->wrap[$key]) ? $table->wrap[$key] : '';
+                        $tdattributes = array(
+                                'style' => $tdstyle . $cell->style,
+                                'colspan' => $cell->colspan,
+                                'rowspan' => $cell->rowspan,
+                                'id' => $cell->id,
+                                'class' => $cell->get_classes_string(),
+                                'abbr' => $cell->abbr,
+                                'scope' => $cell->scope);
+
+                        $output .= $this->output_tag('td', $tdattributes, $cell->text) . "\n";
+                    }
+                }
+                $output .= $this->output_end_tag('tr') . "\n";
+            }
+            $output .= $this->output_end_tag('tbody') . "\n";
+        }
+        $output .= $this->output_end_tag('table') . "\n";
+
+        if ($table->rotateheaders && can_use_rotated_text()) {
+            $this->page->requires->yui_lib('event');
+            $this->page->requires->js('course/report/progress/textrotate.js');
+        }
+
+        return $output;
+    }
+
+    /**
+     * Output the place a skip link goes to.
+     * @param string $id The target name from the corresponding $PAGE->requires->skip_link_to($target) call.
+     * @return string the HTML to output.
+     */
+    public function skip_link_target($id = '') {
+        return $this->output_tag('span', array('id' => $id), '');
+    }
+
+    /**
+     * Outputs a heading
+     * @param string $text The text of the heading
+     * @param int $level The level of importance of the heading. Defaulting to 2
+     * @param string $classes A space-separated list of CSS classes
+     * @param string $id An optional ID
+     * @return string the HTML to output.
+     */
+    public function heading($text, $level = 2, $classes = 'main', $id = '') {
+        $level = (integer) $level;
+        if ($level < 1 or $level > 6) {
+            throw new coding_exception('Heading level must be an integer between 1 and 6.');
+        }
+        return $this->output_tag('h' . $level,
+                array('id' => $id, 'class' => moodle_renderer_base::prepare_classes($classes)), $text);
+    }
+
+    /**
+     * Outputs a box.
+     * @param string $contents The contents of the box
+     * @param string $classes A space-separated list of CSS classes
+     * @param string $id An optional ID
+     * @return string the HTML to output.
+     */
+    public function box($contents, $classes = 'generalbox', $id = '') {
+        return $this->box_start($classes, $id) . $contents . $this->box_end();
+    }
+
+    /**
+     * Outputs the opening section of a box.
+     * @param string $classes A space-separated list of CSS classes
+     * @param string $id An optional ID
+     * @return string the HTML to output.
+     */
+    public function box_start($classes = 'generalbox', $id = '') {
+        $this->opencontainers->push('box', $this->output_end_tag('div'));
+        return $this->output_start_tag('div', array('id' => $id,
+                'class' => 'box ' . moodle_renderer_base::prepare_classes($classes)));
+    }
+
+    /**
+     * Outputs the closing section of a box.
+     * @return string the HTML to output.
+     */
+    public function box_end() {
+        return $this->opencontainers->pop('box');
+    }
+
+    /**
+     * Outputs a container.
+     * @param string $contents The contents of the box
+     * @param string $classes A space-separated list of CSS classes
+     * @param string $id An optional ID
+     * @return string the HTML to output.
+     */
+    public function container($contents, $classes = '', $id = '') {
+        return $this->container_start($classes, $id) . $contents . $this->container_end();
+    }
+
+    /**
+     * Outputs the opening section of a container.
+     * @param string $classes A space-separated list of CSS classes
+     * @param string $id An optional ID
+     * @return string the HTML to output.
+     */
+    public function container_start($classes = '', $id = '') {
+        $this->opencontainers->push('container', $this->output_end_tag('div'));
+        return $this->output_start_tag('div', array('id' => $id,
+                'class' => moodle_renderer_base::prepare_classes($classes)));
+    }
+
+    /**
+     * Outputs the closing section of a container.
+     * @return string the HTML to output.
+     */
+    public function container_end() {
+        return $this->opencontainers->pop('container');
+    }
+}
+
+
+/// RENDERERS
+
+/**
+ * A renderer that generates output for command-line scripts.
+ *
+ * The implementation of this renderer is probably incomplete.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class cli_core_renderer extends moodle_core_renderer {
+    /**
+     * Returns the page header.
+     * @return string HTML fragment
+     */
+    public function header() {
+        output_starting_hook();
+        return $this->page->heading . "\n";
+    }
+
+    /**
+     * Returns a template fragment representing a Heading.
+     * @param string $text The text of the heading
+     * @param int $level The level of importance of the heading
+     * @param string $classes A space-separated list of CSS classes
+     * @param string $id An optional ID
+     * @return string A template fragment for a heading
+     */
+    public function heading($text, $level, $classes = 'main', $id = '') {
+        $text .= "\n";
+        switch ($level) {
+            case 1:
+                return '=>' . $text;
+            case 2:
+                return '-->' . $text;
+            default:
+                return $text;
+        }
+    }
+
+    /**
+     * Returns a template fragment representing a fatal error.
+     * @param string $message The message to output
+     * @param string $moreinfourl URL where more info can be found about the error
+     * @param string $link Link for the Continue button
+     * @param array $backtrace The execution backtrace
+     * @param string $debuginfo Debugging information
+     * @param bool $showerrordebugwarning Whether or not to show a debugging warning
+     * @return string A template fragment for a fatal error
+     */
+    public function fatal_error($message, $moreinfourl, $link, $backtrace,
+                $debuginfo = null, $showerrordebugwarning = false) {
+        $output = "!!! $message !!!\n";
+
+        if (debugging('', DEBUG_DEVELOPER)) {
+            if (!empty($debuginfo)) {
+                $this->notification($debuginfo, 'notifytiny');
+            }
+            if (!empty($backtrace)) {
+                $this->notification('Stack trace: ' . format_backtrace($backtrace, true), 'notifytiny');
+            }
+        }
+    }
+
+    /**
+     * Returns a template fragment representing a notification.
+     * @param string $message The message to include
+     * @param string $classes A space-separated list of CSS classes
+     * @return string A template fragment for a notification
+     */
+    public function notification($message, $classes = 'notifyproblem') {
+        $message = clean_text($message);
+        if ($classes === 'notifysuccess') {
+            return "++ $message ++\n";
+        }
+        return "!! $message !!\n";
+    }
+}
+