. /** * Classes representing HTML elements, used by $OUTPUT methods * * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML * for an overview. * * @package core * @subpackage lib * @copyright 2009 Tim Hunt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * 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 file picker. * * @copyright 2010 Dongsheng Cai * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 */ class file_picker implements renderable { public $options; public function __construct(stdClass $options) { global $CFG, $USER, $PAGE; require_once($CFG->dirroot. '/repository/lib.php'); $defaults = array( 'accepted_types'=>'*', 'return_types'=>FILE_INTERNAL, 'env' => 'filepicker', 'client_id' => uniqid(), 'itemid' => 0, 'maxbytes'=>-1, 'maxfiles'=>1, 'buttonname'=>false ); foreach ($defaults as $key=>$value) { if (empty($options->$key)) { $options->$key = $value; } } $options->currentfile = ''; if (!empty($options->itemid)) { $fs = get_file_storage(); $usercontext = get_context_instance(CONTEXT_USER, $USER->id); if (empty($options->filename)) { if ($files = $fs->get_area_files($usercontext->id, 'user', 'draft', $options->itemid, 'id DESC', false)) { $file = reset($files); } } else { $file = $fs->get_file($usercontext->id, 'user', 'draft', $options->itemid, $options->filepath, $options->filename); } if (!empty($file)) { $options->currentfile = html_writer::link(moodle_url::make_draftfile_url($file->get_itemid(), $file->get_filepath(), $file->get_filename()), $file->get_filename()); } } // initilise options, getting files in root path $this->options = initialise_filepicker($options); // copying other options foreach ($options as $name=>$value) { if (!isset($this->options->$name)) { $this->options->$name = $value; } } } } /** * 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 { /** * @var array List of mandatory fields in user record here. (do not include TEXT columns because it would break SELECT DISTINCT in MSSQL and ORACLE) */ protected static $fields = array('id', 'picture', 'firstname', 'lastname', 'imagealt', 'email'); //TODO: add deleted /** * @var object $user A user object with at least fields all columns specified in $fields array constant 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; 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 (self::$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, useful 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 array $extrafields extra fields to be included in result (do not include TEXT columns because it would break SELECT DISTINCT in MSSQL and ORACLE) * @param string $idalias alias of id field * @param string $fieldprefix prefix to add to all columns in their aliases, does not apply to 'id' * @return string */ public static function fields($tableprefix = '', array $extrafields = NULL, $idalias = 'id', $fieldprefix = '') { if (!$tableprefix and !$extrafields and !$idalias) { return implode(',', self::$fields); } if ($tableprefix) { $tableprefix .= '.'; } $fields = array(); foreach (self::$fields as $field) { if ($field === 'id' and $idalias and $idalias !== 'id') { $fields[$field] = "$tableprefix$field AS $idalias"; } else { if ($fieldprefix and $field !== 'id') { $fields[$field] = "$tableprefix$field AS $fieldprefix$field"; } else { $fields[$field] = "$tableprefix$field"; } } } // add extra fields if not already there if ($extrafields) { foreach ($extrafields as $e) { if ($e === 'id' or isset($fields[$e])) { continue; } if ($fieldprefix) { $fields[$e] = "$tableprefix$e AS $fieldprefix$e"; } else { $fields[$e] = "$tableprefix$e"; } } } return implode(',', $fields); } /** * Extract the aliased user fields from a given record * * Given a record that was previously obtained using {@link self::fields()} with aliases, * this method extracts user related unaliased fields. * * @param stdClass $record containing user picture fields * @param array $extrafields extra fields included in the $record * @param string $idalias alias of the id field * @param string $fieldprefix prefix added to all columns in their aliases, does not apply to 'id' * @return stdClass object with unaliased user fields */ public static function unalias(stdClass $record, array $extrafields=null, $idalias='id', $fieldprefix='') { if (empty($idalias)) { $idalias = 'id'; } $return = new stdClass(); foreach (self::$fields as $field) { if ($field === 'id') { if (property_exists($record, $idalias)) { $return->id = $record->{$idalias}; } } else { if (property_exists($record, $fieldprefix.$field)) { $return->{$field} = $record->{$fieldprefix.$field}; } } } // add extra fields if not already there if ($extrafields) { foreach ($extrafields as $e) { if ($e === 'id' or property_exists($return, $e)) { continue; } $return->{$e} = $record->{$fieldprefix.$e}; } } return $return; } /** * Works out the URL for the users picture. * * This method is recommended as it avoids costly redirects of user pictures * if requests are made for non-existent files etc. * * @param renderer_base $renderer * @return moodle_url */ public function get_url(moodle_page $page, renderer_base $renderer = null) { global $CFG; if (is_null($renderer)) { $renderer = $page->get_renderer('core'); } if ((!empty($CFG->forcelogin) and !isloggedin()) || (!empty($CFG->forceloginforprofileimage) && (!isloggedin() || isguestuser()))) { // protect images if login required and not logged in; // also if login is required for profile images and is not logged in or guest // do not use require_login() because it is expensive and not suitable here anyway return $renderer->pix_url('u/f1'); } // Sort out the filename and size. Size is only required for the gravatar // implementation presently. if (empty($this->size)) { $filename = 'f2'; $size = 35; } else if ($this->size === true or $this->size == 1) { $filename = 'f1'; $size = 100; } else if ($this->size >= 50) { $filename = 'f1'; $size = (int)$this->size; } else { $filename = 'f2'; $size = (int)$this->size; } // First we need to determine whether the user has uploaded a profile // picture of not. if (!empty($this->user->deleted) or !$context = context_user::instance($this->user->id, IGNORE_MISSING)) { $hasuploadedfile = false; } else { $fs = get_file_storage(); $hasuploadedfile = ($fs->file_exists($context->id, 'user', 'icon', 0, '/', $filename.'/.png') || $fs->file_exists($context->id, 'user', 'icon', 0, '/', $filename.'/.jpg')); } $imageurl = $renderer->pix_url('u/'.$filename); if ($hasuploadedfile && $this->user->picture == 1) { $path = '/'; if (clean_param($page->theme->name, PARAM_THEME) == $page->theme->name) { // We append the theme name to the file path if we have it so that // in the circumstance that the profile picture is not available // when the user actually requests it they still get the profile // picture for the correct theme. $path .= $page->theme->name.'/'; } // Set the image URL to the URL for the uploaded file. $imageurl = moodle_url::make_pluginfile_url($context->id, 'user', 'icon', NULL, $path, $filename); } else if (!empty($CFG->enablegravatar)) { // Normalise the size variable to acceptable bounds if ($size < 1 || $size > 512) { $size = 35; } // Hash the users email address $md5 = md5(strtolower(trim($this->user->email))); // Build a gravatar URL with what we know. // Find the best default image URL we can (MDL-35669) if (empty($CFG->gravatardefaulturl)) { $absoluteimagepath = $page->theme->resolve_image_location('u/'.$filename, 'core'); if (strpos($absoluteimagepath, $CFG->dirroot) === 0) { $gravatardefault = $CFG->wwwroot . substr($absoluteimagepath, strlen($CFG->dirroot)); } else { $gravatardefault = $CFG->wwwroot . '/pix/u/' . $filename . '.png'; } } else { $gravatardefault = $CFG->gravatardefaulturl; } // If the currently requested page is https then we'll return an // https gravatar page. if (strpos($CFG->httpswwwroot, 'https:') === 0) { $gravatardefault = str_replace($CFG->wwwroot, $CFG->httpswwwroot, $gravatardefault); // Replace by secure url. $imageurl = new moodle_url("https://secure.gravatar.com/avatar/{$md5}", array('s' => $size, 'd' => $gravatardefault)); } else { $imageurl = new moodle_url("http://www.gravatar.com/avatar/{$md5}", array('s' => $size, 'd' => $gravatardefault)); } } // Return the URL that has been generated. return $imageurl; } } /** * 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 old_help_icon implements renderable { /** * @var string $helpidentifier lang pack identifier */ public $helpidentifier; /** * @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 $helpidentifier The keyword that defines a help page * @param string $title A descriptive text for accessibility only * @param string $component * @param bool $linktext add extra text to icon * @return void */ public function __construct($helpidentifier, $title, $component = 'moodle') { if (empty($title)) { throw new coding_exception('A help_icon object requires a $text parameter'); } if (empty($helpidentifier)) { throw new coding_exception('A help_icon object requires a $helpidentifier parameter'); } $this->helpidentifier = $helpidentifier; $this->title = $title; $this->component = $component; } } /** * Data structure representing a help icon. * * @copyright 2010 Petr Skoda (info@skodak.org) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 */ class help_icon implements renderable { /** * @var string $identifier lang pack identifier (without the "_help" suffix), * both get_string($identifier, $component) and get_string($identifier.'_help', $component) * must exist. */ public $identifier; /** * @var string $component Component name, the same as in get_string() */ public $component; /** * @var string $linktext Extra descriptive text next to the icon */ public $linktext = null; /** * Constructor * @param string $identifier string for help page title, * string with _help suffix is used for the actual help text. * string with _link suffix is used to create a link to further info (if it exists) * @param string $component */ public function __construct($identifier, $component) { $this->identifier = $identifier; $this->component = $component; } /** * Verifies that both help strings exists, shows debug warnings if not */ public function diag_strings() { $sm = get_string_manager(); if (!$sm->string_exists($this->identifier, $this->component)) { debugging("Help title string does not exist: [$this->identifier, $this->component]"); } if (!$sm->string_exists($this->identifier.'_help', $this->component)) { debugging("Help contents string does not exist: [{$this->identifier}_help, $this->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 $alt The alt text to use for the icon * @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']; } else if (empty($this->attributes['title'])) { // Remove the title attribute if empty, we probably want to use the parent node's title // and some browsers might overwrite it with an empty title. unset($this->attributes['title']); } } } /** * Data structure representing an emoticon image * * @since Moodle 2.0 */ class pix_emoticon extends pix_icon implements renderable { /** * Constructor * @param string $pix short icon name * @param string $alt alternative text * @param string $component emoticon image provider * @param array $attributes explicit HTML attributes */ public function __construct($pix, $alt, $component = 'moodle', array $attributes = array()) { if (empty($attributes['class'])) { $attributes['class'] = 'emoticon'; } parent::__construct($pix, $alt, $component, $attributes); } } /** * 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 confirm_action($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 = ''; /** * @var array Button label's attributes */ var $labelattributes = array(); /** * 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; } /** * Adds help icon. * @param string $page The keyword that defines a help page * @param string $title A descriptive text for accessibility only * @param string $component * @param bool $linktext add extra text to icon * @return void */ public function set_old_help_icon($helppage, $title, $component = 'moodle') { $this->helpicon = new old_help_icon($helppage, $title, $component); } /** * Adds help icon. * @param string $identifier The keyword that defines a help page * @param string $component * @param bool $linktext add extra text to icon * @return void */ public function set_help_icon($identifier, $component = 'moodle') { $this->helpicon = new help_icon($identifier, $component); } /** * Sets select's label * @param string $label * @param array $attributes (optional) * @return void */ public function set_label($label, $attributes = array()) { $this->label = $label; $this->labelattributes = $attributes; } } /** * 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 = ''; /** * @var array Button label's attributes */ var $labelattributes = array(); /** * 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; /** * @var string If set, makes button visible with given name for button */ var $showbutton = null; /** * Constructor * @param array $urls list of options * @param string $selected selected element * @param array $nothing * @param string $formid * @param string $showbutton Set to text of button if it should be visible * or null if it should be hidden (hidden version always has text 'go') */ public function __construct(array $urls, $selected='', $nothing=array(''=>'choosedots'), $formid=null, $showbutton=null) { $this->urls = $urls; $this->selected = $selected; $this->nothing = $nothing; $this->formid = $formid; $this->showbutton = $showbutton; } /** * Adds help icon. * @param string $page The keyword that defines a help page * @param string $title A descriptive text for accessibility only * @param string $component * @param bool $linktext add extra text to icon * @return void */ public function set_old_help_icon($helppage, $title, $component = 'moodle') { $this->helpicon = new old_help_icon($helppage, $title, $component); } /** * Adds help icon. * @param string $identifier The keyword that defines a help page * @param string $component * @param bool $linktext add extra text to icon * @return void */ public function set_help_icon($identifier, $component = 'moodle') { $this->helpicon = new help_icon($identifier, $component); } /** * Sets select's label * @param string $label * @param array $attributes (optional) * @return void */ public function set_label($label, $attributes = array()) { $this->label = $label; $this->labelattributes = $attributes; } } /** * 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 tag, but only if the contents are not empty * @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 nonempty_tag($tagname, $contents, array $attributes = null) { if ($contents === '' || is_null($contents)) { return ''; } return self::tag($tagname, $contents, $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') { static $counter = 0; static $uniq; if (!isset($uniq)) { $uniq = uniqid(); } $counter++; return $base.$uniq.$counter; } /** * 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 ? 'checked' : 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 array 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; if (!empty($attributes['disabled'])) { $attributes['disabled'] = 'disabled'; } else { unset($attributes['disabled']); } $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'] = (int)$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