. /** * Classes representing HTML elements, used by $OUTPUT methods * * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML * for an overview. * * @package moodlecore * @copyright 2009 Tim Hunt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ /** * Interface marking other classes as suitable for renderer_base::render() * @author 2010 Petr Skoda (skodak) info@skodak.org */ interface renderable { // intentionally empty } /** * Data structure representing a user picture. * * @copyright 2009 Nicolas Connault, 2010 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 */ class user_picture implements renderable { /** * List of mandatory fields in user record here. * @var string */ const FIELDS = 'id,picture,firstname,lastname,imagealt'; /** * @var object $user A user object with at least fields id, picture, imagealt, firstname and lastname set. */ public $user; /** * @var int $courseid The course id. Used when constructing the link to the user's profile, * page course id used if not specified. */ public $courseid; /** * @var bool $link add course profile link to image */ public $link = true; /** * @var int $size Size in pixels. Special values are (true/1 = 100px) and (false/0 = 35px) for backward compatibility */ public $size = 35; /** * @var boolean $alttext add non-blank alt-text to the image. * Default true, set to false when image alt just duplicates text in screenreaders. */ public $alttext = true; /** * @var boolean $popup Whether or not to open the link in a popup window. */ public $popup = false; /** * @var string Image class attribute */ public $class = 'userpicture'; /** * User picture constructor. * * @param object $user user record with at least id, picture, imagealt, firstname and lastname set. * @param array $options such as link, size, link, ... */ public function __construct(stdClass $user) { global $DB; static $fields = null; if (is_null($fields)) { $fields = explode(',', self::FIELDS); } if (empty($user->id)) { throw new coding_exception('User id is required when printing user avatar image.'); } // only touch the DB if we are missing data and complain loudly... $needrec = false; foreach ($fields as $field) { if (!array_key_exists($field, $user)) { $needrec = true; debugging('Missing '.$field.' property in $user object, this is a performance problem that needs to be fixed by a developer. ' .'Please use user_picture::fields() to get the full list of required fields.', DEBUG_DEVELOPER); break; } } if ($needrec) { $this->user = $DB->get_record('user', array('id'=>$user->id), self::FIELDS, MUST_EXIST); } else { $this->user = clone($user); } } /** * Returns a list of required user fields, usefull when fetching required user info from db. * * In some cases we have to fetch the user data together with some other information, * the idalias is useful there because the id would otherwise override the main * id of the result record. Please note it has to be converted back to id before rendering. * * @param string $tableprefix name of database table prefix in query * @param string $idalias alias of id field * @return string */ public static function fields($tableprefix = '', $idalias = '') { if ($tableprefix === '' and $idalias === '') { return self::FIELDS; } $fields = explode(',', self::FIELDS); foreach ($fields as $key=>$field) { if ($field === 'id' and $idalias !== '') { $field = "$field AS $idalias"; } $fields[$key] = "$tableprefix.$field"; } return implode(',', $fields); } } /** * Data structure representing a help icon. * * @copyright 2009 Nicolas Connault, 2010 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 */ class help_icon implements renderable { /** * @var string $page name of help page */ public $helppage; /** * @var string $title A descriptive text for title tooltip */ public $title = null; /** * @var string $component Component name, the same as in get_string() */ public $component = 'moodle'; /** * @var string $linktext Extra descriptive text next to the icon */ public $linktext = null; /** * Constructor: sets up the other components in case they are needed * @param string $page The keyword that defines a help page * @param string $title A descriptive text for accesibility only * @param string $component * @param bool $linktext add extra text to icon * @return void */ public function __construct($helppage, $title, $component = 'moodle') { if (empty($title)) { throw new coding_exception('A help_icon object requires a $text parameter'); } if (empty($helppage)) { throw new coding_exception('A help_icon object requires a $helppage parameter'); } $this->helppage = $helppage; $this->title = $title; $this->component = $component; } } /** * Data structure representing an icon. * * @copyright 2010 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 */ class pix_icon implements renderable { var $pix; var $component; var $attributes = array(); /** * Constructor * @param string $pix short icon name * @param string $component component name * @param array $attributes html attributes */ public function __construct($pix, $alt, $component='moodle', array $attributes = null) { $this->pix = $pix; $this->component = $component; $this->attributes = (array)$attributes; $this->attributes['alt'] = $alt; if (empty($this->attributes['class'])) { $this->attributes['class'] = 'smallicon'; } if (!isset($this->attributes['title'])) { $this->attributes['title'] = $this->attributes['alt']; } } } /** * Data structure representing a simple form with only one button. * * @copyright 2009 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 */ class single_button implements renderable { /** * Target url * @var moodle_url */ var $url; /** * Button label * @var string */ var $label; /** * Form submit method * @var string post or get */ var $method = 'post'; /** * Wrapping div class * @var string * */ var $class = 'singlebutton'; /** * True if button disabled, false if normal * @var boolean */ var $disabled = false; /** * Button tooltip * @var string */ var $tooltip = null; /** * Form id * @var string */ var $formid; /** * List of attached actions * @var array of component_action */ var $actions = array(); /** * Constructor * @param string|moodle_url $url * @param string $label button text * @param string $method get or post submit method */ public function __construct(moodle_url $url, $label, $method='post') { $this->url = clone($url); $this->label = $label; $this->method = $method; } /** * Shortcut for adding a JS confirm dialog when the button is clicked. * The message must be a yes/no question. * @param string $message The yes/no confirmation question. If "Yes" is clicked, the original action will occur. * @return void */ public function add_confirm_action($confirmmessage) { $this->add_action(new component_action('click', 'M.util.show_confirm_dialog', array('message' => $confirmmessage))); } /** * Add action to the button. * @param component_action $action * @return void */ public function add_action(component_action $action) { $this->actions[] = $action; } } /** * Simple form with just one select field that gets submitted automatically. * If JS not enabled small go button is printed too. * * @copyright 2009 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 */ class single_select implements renderable { /** * Target url - includes hidden fields * @var moodle_url */ var $url; /** * Name of the select element. * @var string */ var $name; /** * @var array $options associative array value=>label ex.: * array(1=>'One, 2=>Two) * it is also possible to specify optgroup as complex label array ex.: * array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two'))) * array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three'))) */ var $options; /** * Selected option * @var string */ var $selected; /** * Nothing selected * @var array */ var $nothing; /** * Extra select field attributes * @var array */ var $attributes = array(); /** * Button label * @var string */ var $label = ''; /** * Form submit method * @var string post or get */ var $method = 'get'; /** * Wrapping div class * @var string * */ var $class = 'singleselect'; /** * True if button disabled, false if normal * @var boolean */ var $disabled = false; /** * Button tooltip * @var string */ var $tooltip = null; /** * Form id * @var string */ var $formid = null; /** * List of attached actions * @var array of component_action */ var $helpicon = null; /** * Constructor * @param moodle_url $url form action target, includes hidden fields * @param string $name name of selection field - the changing parameter in url * @param array $options list of options * @param string $selected selected element * @param array $nothing * @param string $formid */ public function __construct(moodle_url $url, $name, array $options, $selected='', $nothing=array(''=>'choosedots'), $formid=null) { $this->url = $url; $this->name = $name; $this->options = $options; $this->selected = $selected; $this->nothing = $nothing; $this->formid = $formid; } /** * Shortcut for adding a JS confirm dialog when the button is clicked. * The message must be a yes/no question. * @param string $message The yes/no confirmation question. If "Yes" is clicked, the original action will occur. * @return void */ public function add_confirm_action($confirmmessage) { $this->add_action(new component_action('submit', 'M.util.show_confirm_dialog', array('message' => $confirmmessage))); } /** * Add action to the button. * @param component_action $action * @return void */ public function add_action(component_action $action) { $this->actions[] = $action; } /** * Constructor: sets up the other components in case they are needed * @param string $page The keyword that defines a help page * @param string $title A descriptive text for accesibility only * @param string $component * @param bool $linktext add extra text to icon * @return void */ public function set_help_icon($helppage, $title, $component = 'moodle') { $this->helpicon = new help_icon($helppage, $title, $component); } /** * Set's select lable * @param string $label * @return void */ public function set_label($label) { $this->label = $label; } } /** * Simple URL selection widget description. * @copyright 2009 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 */ class url_select implements renderable { /** * @var array $urls associative array value=>label ex.: * array(1=>'One, 2=>Two) * it is also possible to specify optgroup as complex label array ex.: * array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two'))) * array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three'))) */ var $urls; /** * Selected option * @var string */ var $selected; /** * Nothing selected * @var array */ var $nothing; /** * Extra select field attributes * @var array */ var $attributes = array(); /** * Button label * @var string */ var $label = ''; /** * Wrapping div class * @var string * */ var $class = 'urlselect'; /** * True if button disabled, false if normal * @var boolean */ var $disabled = false; /** * Button tooltip * @var string */ var $tooltip = null; /** * Form id * @var string */ var $formid = null; /** * List of attached actions * @var array of component_action */ var $helpicon = null; /** * Constructor * @param array $urls list of options * @param string $selected selected element * @param array $nothing * @param string $formid */ public function __construct(array $urls, $selected='', $nothing=array(''=>'choosedots'), $formid=null) { $this->urls = $urls; $this->selected = $selected; $this->nothing = $nothing; $this->formid = $formid; } /** * Constructor: sets up the other components in case they are needed * @param string $page The keyword that defines a help page * @param string $title A descriptive text for accesibility only * @param string $component * @param bool $linktext add extra text to icon * @return void */ public function set_help_icon($helppage, $title, $component = 'moodle') { $this->helpicon = new help_icon($helppage, $title, $component); } /** * Set's select lable * @param string $label * @return void */ public function set_label($label) { $this->label = $label; } } /** * Data structure describing html link with special action attached. * @copyright 2010 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 */ class action_link implements renderable { /** * Href url * @var moodle_url */ var $url; /** * Link text * @var string HTML fragment */ var $text; /** * HTML attributes * @var array */ var $attributes; /** * List of actions attached to link * @var array of component_action */ var $actions; /** * Constructor * @param string|moodle_url $url * @param string $text HTML fragment * @param component_action $action * @param array $attributes associative array of html link attributes + disabled */ public function __construct(moodle_url $url, $text, component_action $action=null, array $attributes=null) { $this->url = clone($url); $this->text = $text; $this->attributes = (array)$attributes; if ($action) { $this->add_action($action); } } /** * Add action to the link. * @param component_action $action * @return void */ public function add_action(component_action $action) { $this->actions[] = $action; } public function add_class($class) { if (empty($this->attributes['class'])) { $this->attributes['class'] = $class; } else { $this->attributes['class'] .= ' ' . $class; } } } // ==== HTML writer and helper classes, will be probably moved elsewhere ====== /** * Simple html output class * @copyright 2009 Tim Hunt, 2010 Petr Skoda */ class html_writer { /** * Outputs a tag with attributes and contents * @param string $tagname The name of tag ('a', 'img', 'span' etc.) * @param string $contents What goes between the opening and closing tags * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.) * @return string HTML fragment */ public static function tag($tagname, $contents, array $attributes = null) { return self::start_tag($tagname, $attributes) . $contents . self::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 */ public static function start_tag($tagname, array $attributes = null) { return '<' . $tagname . self::attributes($attributes) . '>'; } /** * Outputs a closing tag * @param string $tagname The name of tag ('a', 'img', 'span' etc.) * @return string HTML fragment */ public static function end_tag($tagname) { return ''; } /** * 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 */ public static function empty_tag($tagname, array $attributes = null) { return '<' . $tagname . self::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 */ public static function attribute($name, $value) { if (is_array($value)) { debugging("Passed an array for the HTML attribute $name", DEBUG_DEVELOPER); } if ($value instanceof moodle_url) { return ' ' . $name . '="' . $value->out() . '"'; } // special case, we do not want these in output if ($value === null) { return ''; } // no sloppy trimming here! 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 */ public static function attributes(array $attributes = null) { $attributes = (array)$attributes; $output = ''; foreach ($attributes as $name => $value) { $output .= self::attribute($name, $value); } return $output; } /** * Generates random html element id. * @param string $base * @return string */ public static function random_id($base='random') { return uniqid($base); } /** * Generates a simple html link * @param string|moodle_url $url * @param string $text link txt * @param array $attributes extra html attributes * @return string HTML fragment */ public static function link($url, $text, array $attributes = null) { $attributes = (array)$attributes; $attributes['href'] = $url; return self::tag('a', $text, $attributes); } /** * generates a simple checkbox with optional label * @param string $name * @param string $value * @param bool $checked * @param string $label * @param array $attributes * @return string html fragment */ public static function checkbox($name, $value, $checked = true, $label = '', array $attributes = null) { $attributes = (array)$attributes; $output = ''; if ($label !== '' and !is_null($label)) { if (empty($attributes['id'])) { $attributes['id'] = self::random_id('checkbox_'); } } $attributes['type'] = 'checkbox'; $attributes['value'] = $value; $attributes['name'] = $name; $attributes['checked'] = $checked ? 'selected' : null; $output .= self::empty_tag('input', $attributes); if ($label !== '' and !is_null($label)) { $output .= self::tag('label', $label, array('for'=>$attributes['id'])); } return $output; } /** * Generates a simple select yes/no form field * @param string $name name of select element * @param bool $selected * @param array $attributes - html select element attributes * @return string HRML fragment */ public static function select_yes_no($name, $selected=true, array $attributes = null) { $options = array('1'=>get_string('yes'), '0'=>get_string('no')); return self::select($options, $name, $selected, null, $attributes); } /** * Generates a simple select form field * @param array $options associative array value=>label ex.: * array(1=>'One, 2=>Two) * it is also possible to specify optgroup as complex label array ex.: * array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two'))) * array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three'))) * @param string $name name of select element * @param string|array $selected value or arary of values depending on multiple attribute * @param array|bool $nothing, add nothing selected option, or false of not added * @param array $attributes - html select element attributes * @return string HTML fragment */ public static function select(array $options, $name, $selected = '', $nothing = array(''=>'choosedots'), array $attributes = null) { $attributes = (array)$attributes; if (is_array($nothing)) { foreach ($nothing as $k=>$v) { if ($v === 'choose' or $v === 'choosedots') { $nothing[$k] = get_string('choosedots'); } } $options = $nothing + $options; // keep keys, do not override } else if (is_string($nothing) and $nothing !== '') { // BC $options = array(''=>$nothing) + $options; } // we may accept more values if multiple attribute specified $selected = (array)$selected; foreach ($selected as $k=>$v) { $selected[$k] = (string)$v; } if (!isset($attributes['id'])) { $id = 'menu'.$name; // name may contaion [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading $id = str_replace('[', '', $id); $id = str_replace(']', '', $id); $attributes['id'] = $id; } if (!isset($attributes['class'])) { $class = 'menu'.$name; // name may contaion [], which would make an invalid class. e.g. numeric question type editing form, assignment quickgrading $class = str_replace('[', '', $class); $class = str_replace(']', '', $class); $attributes['class'] = $class; } $attributes['class'] = 'select ' . $attributes['class']; /// Add 'select' selector always $attributes['name'] = $name; $output = ''; foreach ($options as $value=>$label) { if (is_array($label)) { // ignore key, it just has to be unique $output .= self::select_optgroup(key($label), current($label), $selected); } else { $output .= self::select_option($label, $value, $selected); } } return self::tag('select', $output, $attributes); } private static function select_option($label, $value, array $selected) { $attributes = array(); $value = (string)$value; if (in_array($value, $selected, true)) { $attributes['selected'] = 'selected'; } $attributes['value'] = $value; return self::tag('option', $label, $attributes); } private static function select_optgroup($groupname, $options, array $selected) { if (empty($options)) { return ''; } $attributes = array('label'=>$groupname); $output = ''; foreach ($options as $value=>$label) { $output .= self::select_option($label, $value, $selected); } return self::tag('optgroup', $output, $attributes); } /** * This is a shortcut for making an hour selector menu. * @param string $type The type of selector (years, months, days, hours, minutes) * @param string $name fieldname * @param int $currenttime A default timestamp in GMT * @param int $step minute spacing * @param array $attributes - html select element attributes * @return HTML fragment */ public static function select_time($type, $name, $currenttime=0, $step=5, array $attributes=null) { if (!$currenttime) { $currenttime = time(); } $currentdate = usergetdate($currenttime); $userdatetype = $type; $timeunits = array(); switch ($type) { case 'years': for ($i=1970; $i<=2020; $i++) { $timeunits[$i] = $i; } $userdatetype = 'year'; break; case 'months': for ($i=1; $i<=12; $i++) { $timeunits[$i] = userdate(gmmktime(12,0,0,$i,15,2000), "%B"); } $userdatetype = 'month'; $currentdate['month'] = $currentdate['mon']; break; case 'days': for ($i=1; $i<=31; $i++) { $timeunits[$i] = $i; } $userdatetype = 'mday'; break; case 'hours': for ($i=0; $i<=23; $i++) { $timeunits[$i] = sprintf("%02d",$i); } break; case 'minutes': if ($step != 1) { $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step; } for ($i=0; $i<=59; $i+=$step) { $timeunits[$i] = sprintf("%02d",$i); } break; default: throw new coding_exception("Time type $type is not supported by html_writer::select_time()."); } if (empty($attributes['id'])) { $attributes['id'] = self::random_id('ts_'); } $timerselector = self::select($timeunits, $name, $currentdate[$userdatetype], null, array('id'=>$attributes['id'])); $label = self::tag('label', get_string(substr($type, 0, -1), 'form'), array('for'=>$attributes['id'], 'class'=>'accesshide')); return $label.$timerselector; } /** * Shortcut for quick making of lists * @param array $items * @param string $tag ul or ol * @param array $attributes * @return string */ public static function alist(array $items, array $attributes = null, $tag = 'ul') { //note: 'list' is a reserved keyword ;-) $output = ''; foreach ($items as $item) { $output .= html_writer::start_tag('li') . "\n"; $output .= $item . "\n"; $output .= html_writer::end_tag('li') . "\n"; } return html_writer::tag($tag, $output, $attributes); } /** * Returns hidden input fields created from url parameters. * @param moodle_url $url * @param array $exclude list of excluded parameters * @return string HTML fragment */ public static function input_hidden_params(moodle_url $url, array $exclude = null) { $exclude = (array)$exclude; $params = $url->params(); foreach ($exclude as $key) { unset($params[$key]); } $output = ''; foreach ($params as $key => $value) { $attributes = array('type'=>'hidden', 'name'=>$key, 'value'=>$value); $output .= self::empty_tag('input', $attributes)."\n"; } return $output; } /** * Generate a script tag containing the the specified code. * * @param string $js the JavaScript code * @param moodle_url|string optional url of the external script, $code ignored if specified * @return string HTML, the code wrapped in