- * @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";
- }
-}
-
-
/**
* Output CSS while replacing constants/variables. See MDL-6798 for details
*
diff --git a/lib/outputpixfinders.php b/lib/outputpixfinders.php
new file mode 100644
index 00000000000..decae9b2e15
--- /dev/null
+++ b/lib/outputpixfinders.php
@@ -0,0 +1,221 @@
+.
+
+/**
+ * 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
index 00000000000..c12040bce6b
--- /dev/null
+++ b/lib/outputrenderers.php
@@ -0,0 +1,2254 @@
+.
+
+/**
+ * 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 = '' . "\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 = '' . "\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 .= 'httpswwwroot . '/lib/xhtml.xsl"?>' . "\n";
+
+ } else {
+ $prolog = '';
+ }
+
+ return $prolog . $doctype;
+ }
+
+ /**
+ * The attributes that should be added to the 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 tag. Designed to be called in theme
+ * layout.php files.
+ * @return string HTML fragment.
+ */
+ public function standard_head_html() {
+ global $CFG;
+ $output = '';
+ $output .= ' ' . "\n";
+ $output .= ' ' . "\n";
+ if (!$this->page->cacheable) {
+ $output .= ' ' . "\n";
+ $output .= ' ' . "\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 .= ' ';
+ }
+
+ $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 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 .= 'This page is: ' . $this->page->debug_summary() . '
';
+ }
+ if (!empty($CFG->debugvalidators)) {
+ $output .= '';
+ }
+ 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 '';
+
+ } else if (!empty($CFG->target_release) && $CFG->target_release != $CFG->release) {
+ // Special case for during install/upgrade.
+ return '';
+
+ } else if ($this->page->course->id == $SITE->id || strpos($this->page->pagetype, 'course-view') === 0) {
+ return '';
+
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * 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('&', '&', $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 = ' '."\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 .= ''. get_string('continue') .' ';
+ if ($debugdisableredirect) {
+ $output .= 'Error output, so disabling automatic redirect.
';
+ }
+ $output .= $this->footer();
+ return $output;
+ }
+
+ /**
+ * Start output by sending the HTTP headers, and printing the HTML
+ * and the start of the .
+ *
+ * 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 '';
+ foreach ($lt as $column) {
+ if ($column == 'left' && $this->page->blocks->region_has_content(BLOCK_POS_LEFT, $this)) {
+ echo '';
+ echo $this->container_start();
+ echo $this->blocks_for_region(BLOCK_POS_LEFT);
+ echo $this->container_end();
+ echo ' ';
+
+ } else if ($column == 'middle') {
+ echo '';
+ echo $this->container_start();
+ echo $this->skip_link_target();
+ echo self::MAIN_CONTENT_TOKEN;
+ echo $this->container_end();
+ echo ' ';
+
+ } else if ($column == 'right' && $this->page->blocks->region_has_content(BLOCK_POS_RIGHT, $this)) {
+ echo '';
+ echo $this->container_start();
+ echo $this->blocks_for_region(BLOCK_POS_RIGHT);
+ echo $this->container_end();
+ echo ' ';
+ }
+ }
+ echo '
';
+
+ $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
+ // standard_top_of_body_html() .
+ substr($output, $divpos);
+
+ // Put in the end token before the end of body.
+ $output = str_replace('', self::END_HTML_TOKEN . '', $output);
+
+ // Make sure we use the correct doctype.
+ $output = preg_replace('/()/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
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 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 $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:
+ *
+ * // 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);
+ *
+ *
+ * @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 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 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
+ */
+ 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 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
+ */
+ 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 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
+ */
+ 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 or 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 or
+ */
+ 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 element
+ *
+ * @param html_field $field a html_field object
+ * @return string the HTML for the
+ */
+ 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 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 .
+ * 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 = '' . $message . '
'.
+ '' .
+ get_string('moreinformation') . '
';
+ $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 .= ' (' . $this->link($pagingbar->previouslink) . ') ';
+ }
+
+ if (!empty($pagingbar->firstlink)) {
+ $output .= ' ' . $this->link($pagingbar->firstlink) . ' ...';
+ }
+
+ foreach ($pagingbar->pagelinks as $link) {
+ if ($link instanceof html_link) {
+ $output .= ' ' . $this->link($link);
+ } else {
+ $output .= " $link";
+ }
+ }
+
+ if (!empty($pagingbar->lastlink)) {
+ $output .= ' ...' . $this->link($pagingbar->lastlink) . ' ';
+ }
+
+ if (!empty($pagingbar->nextlink)) {
+ $output .= ' (' . $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";
+ }
+}
+