MDL-20204 using url_select
[moodle.git] / lib / outputcomponents.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * Classes representing HTML elements, used by $OUTPUT methods
20  *
21  * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML
22  * for an overview.
23  *
24  * @package   moodlecore
25  * @copyright 2009 Tim Hunt
26  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27  */
30 /**
31  * Interface marking other classes as suitable for renderer_base::render()
32  * @author 2010 Petr Skoda (skodak) info@skodak.org
33  */
34 interface renderable {
35     // intentionally empty
36 }
39 /**
40  * Data structure representing a user picture.
41  *
42  * @copyright 2009 Nicolas Connault, 2010 Petr Skoda
43  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44  * @since     Moodle 2.0
45  */
46 class user_picture implements renderable {
47     /**
48      * List of mandatory fields in user record here.
49      * @var string
50      */
51     const FIELDS = 'id,picture,firstname,lastname,imagealt';
53     /**
54      * @var object $user A user object with at least fields id, picture, imagealt, firstname and lastname set.
55      */
56     public $user;
57     /**
58      * @var int $courseid The course id. Used when constructing the link to the user's profile,
59      * page course id used if not specified.
60      */
61     public $courseid;
62     /**
63      * @var bool $link add course profile link to image
64      */
65     public $link = true;
66     /**
67      * @var int $size Size in pixels. Special values are (true/1 = 100px) and (false/0 = 35px) for backward compatibility
68      */
69     public $size = 35;
70     /**
71      * @var boolean $alttext add non-blank alt-text to the image.
72      * Default true, set to false when image alt just duplicates text in screenreaders.
73      */
74     public $alttext = true;
75     /**
76      * @var boolean $popup Whether or not to open the link in a popup window.
77      */
78     public $popup = false;
79     /**
80      * @var string Image class attribute
81      */
82     public $class = 'userpicture';
84     /**
85      * User picture constructor.
86      *
87      * @param object $user user record with at least id, picture, imagealt, firstname and lastname set.
88      * @param array $options such as link, size, link, ...
89      */
90     public function __construct(stdClass $user) {
91         global $DB;
93         static $fields = null;
94         if (is_null($fields)) {
95             $fields = explode(',', self::FIELDS);
96         }
98         if (empty($user->id)) {
99             throw new coding_exception('User id is required when printing user avatar image.');
100         }
102         // only touch the DB if we are missing data and complain loudly...
103         $needrec = false;
104         foreach ($fields as $field) {
105             if (!array_key_exists($field, $user)) {
106                 $needrec = true;
107                 debugging('Missing '.$field.' property in $user object, this is a performance problem that needs to be fixed by a developer. '
108                           .'Please use user_picture::fields() to get the full list of required fields.', DEBUG_DEVELOPER);
109                 break;
110             }
111         }
113         if ($needrec) {
114             $this->user = $DB->get_record('user', array('id'=>$user->id), self::FIELDS, MUST_EXIST);
115         } else {
116             $this->user = clone($user);
117         }
118     }
120     /**
121      * Returns a list of required user fields, usefull when fetching required user info from db.
122      * @param string $tableprefix name of database table prefix in query
123      * @return string
124      */
125     public static function fields($tableprefix = '') {
126         if ($tableprefix === '') {
127             return self::FIELDS;
128         } else {
129             return "$tableprefix." . str_replace(',', ",$tableprefix.", self::FIELDS);
130         }
131     }
135 /**
136  * Data structure representing a help icon.
137  *
138  * @copyright 2009 Nicolas Connault, 2010 Petr Skoda
139  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
140  * @since     Moodle 2.0
141  */
142 class help_icon implements renderable {
143     /**
144      * @var string $page name of help page
145      */
146     public $helppage;
147     /**
148      * @var string $title A descriptive text for title tooltip
149      */
150     public $title = null;
151     /**
152      * @var string $component Component name, the same as in get_string()
153      */
154     public $component = 'moodle';
155     /**
156      * @var string $linktext Extra descriptive text next to the icon
157      */
158     public $linktext = null;
160     /**
161      * Constructor: sets up the other components in case they are needed
162      * @param string $page  The keyword that defines a help page
163      * @param string $title A descriptive text for accesibility only
164      * @param string $component
165      * @param bool $linktext add extra text to icon
166      * @return void
167      */
168     public function __construct($helppage, $title, $component = 'moodle') {
169         if (empty($title)) {
170             throw new coding_exception('A help_icon object requires a $text parameter');
171         }
172         if (empty($helppage)) {
173             throw new coding_exception('A help_icon object requires a $helppage parameter');
174         }
176         $this->helppage  = $helppage;
177         $this->title     = $title;
178         $this->component = $component;
179     }
183 /**
184  * Data structure representing a simple form with only one button.
185  *
186  * @copyright 2009 Petr Skoda
187  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
188  * @since     Moodle 2.0
189  */
190 class single_button implements renderable {
191     /**
192      * Target url
193      * @var moodle_url
194      */
195     var $url;
196     /**
197      * Button label
198      * @var string
199      */
200     var $label;
201     /**
202      * Form submit method
203      * @var string post or get
204      */
205     var $method = 'post';
206     /**
207      * Wrapping div class
208      * @var string
209      * */
210     var $class = 'singlebutton';
211     /**
212      * True if button disabled, false if normal
213      * @var boolean
214      */
215     var $disabled = false;
216     /**
217      * Button tooltip
218      * @var string
219      */
220     var $tooltip = null;
221     /**
222      * Form id
223      * @var string
224      */
225     var $formid;
226     /**
227      * List of attached actions
228      * @var array of component_action
229      */
230     var $actions = array();
232     /**
233      * Constructor
234      * @param string|moodle_url $url
235      * @param string $label button text
236      * @param string $method get or post submit method
237      */
238     public function __construct(moodle_url $url, $label, $method='post') {
239         $this->url    = clone($url);
240         $this->label  = $label;
241         $this->method = $method;
242     }
244     /**
245      * Shortcut for adding a JS confirm dialog when the button is clicked.
246      * The message must be a yes/no question.
247      * @param string $message The yes/no confirmation question. If "Yes" is clicked, the original action will occur.
248      * @return void
249      */
250     public function add_confirm_action($confirmmessage) {
251         $this->add_action(new component_action('click', 'M.util.show_confirm_dialog', array('message' => $confirmmessage)));
252     }
254     /**
255      * Add action to the button.
256      * @param component_action $action
257      * @return void
258      */
259     public function add_action(component_action $action) {
260         $this->actions[] = $action;
261     }
265 /**
266  * Simple form with just one select field that gets submitted automatically.
267  * If JS not enabled small go button is printed too.
268  *
269  * @copyright 2009 Petr Skoda
270  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
271  * @since     Moodle 2.0
272  */
273 class single_select implements renderable {
274     /**
275      * Target url - includes hidden fields
276      * @var moodle_url
277      */
278     var $url;
279     /**
280      * Name of the select element.
281      * @var string
282      */
283     var $name;
284     /**
285      * @var array $options associative array value=>label ex.:
286      *              array(1=>'One, 2=>Two)
287      *              it is also possible to specify optgroup as complex label array ex.:
288      *                array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
289      *                array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
290      */
291     var $options;
292     /**
293      * Selected option
294      * @var string
295      */
296     var $selected;
297     /**
298      * Nothing selected
299      * @var array
300      */
301     var $nothing;
302     /**
303      * Extra select field attributes
304      * @var array
305      */
306     var $attributes = array();
307     /**
308      * Button label
309      * @var string
310      */
311     var $label = '';
312     /**
313      * Form submit method
314      * @var string post or get
315      */
316     var $method = 'get';
317     /**
318      * Wrapping div class
319      * @var string
320      * */
321     var $class = 'singleselect';
322     /**
323      * True if button disabled, false if normal
324      * @var boolean
325      */
326     var $disabled = false;
327     /**
328      * Button tooltip
329      * @var string
330      */
331     var $tooltip = null;
332     /**
333      * Form id
334      * @var string
335      */
336     var $formid = null;
337     /**
338      * List of attached actions
339      * @var array of component_action
340      */
341     var $helpicon = null;
342     /**
343      * Constructor
344      * @param moodle_url $url form action target, includes hidden fields
345      * @param string $name name of selection field - the changing parameter in url
346      * @param array $options list of options
347      * @param string $selected selected element
348      * @param array $nothing
349      * @param string $formid
350      */
351     public function __construct(moodle_url $url, $name, array $options, $selected='', $nothing=array(''=>'choosedots'), $formid=null) {
352         $this->url      = $url;
353         $this->name     = $name;
354         $this->options  = $options;
355         $this->selected = $selected;
356         $this->nothing  = $nothing;
357         $this->formid   = $formid;
358     }
360     /**
361      * Shortcut for adding a JS confirm dialog when the button is clicked.
362      * The message must be a yes/no question.
363      * @param string $message The yes/no confirmation question. If "Yes" is clicked, the original action will occur.
364      * @return void
365      */
366     public function add_confirm_action($confirmmessage) {
367         $this->add_action(new component_action('submit', 'M.util.show_confirm_dialog', array('message' => $confirmmessage)));
368     }
370     /**
371      * Add action to the button.
372      * @param component_action $action
373      * @return void
374      */
375     public function add_action(component_action $action) {
376         $this->actions[] = $action;
377     }
379     /**
380      * Constructor: sets up the other components in case they are needed
381      * @param string $page  The keyword that defines a help page
382      * @param string $title A descriptive text for accesibility only
383      * @param string $component
384      * @param bool $linktext add extra text to icon
385      * @return void
386      */
387     public function set_help_icon($helppage, $title, $component = 'moodle') {
388         $this->helpicon = new help_icon($helppage, $title, $component);
389     }
391     /**
392      * Set's select lable
393      * @param string $label
394      * @return void
395      */
396     public function set_label($label) {
397         $this->label = $label;
398     }
402 /**
403  * Simple URL selection widget description.
404  * @copyright 2009 Petr Skoda
405  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
406  * @since     Moodle 2.0
407  */
408 class url_select implements renderable {
409     /**
410      * @var array $urls associative array value=>label ex.:
411      *              array(1=>'One, 2=>Two)
412      *              it is also possible to specify optgroup as complex label array ex.:
413      *                array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
414      *                array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
415      */
416     var $urls;
417     /**
418      * Selected option
419      * @var string
420      */
421     var $selected;
422     /**
423      * Nothing selected
424      * @var array
425      */
426     var $nothing;
427     /**
428      * Extra select field attributes
429      * @var array
430      */
431     var $attributes = array();
432     /**
433      * Button label
434      * @var string
435      */
436     var $label = '';
437     /**
438      * Wrapping div class
439      * @var string
440      * */
441     var $class = 'urlselect';
442     /**
443      * True if button disabled, false if normal
444      * @var boolean
445      */
446     var $disabled = false;
447     /**
448      * Button tooltip
449      * @var string
450      */
451     var $tooltip = null;
452     /**
453      * Form id
454      * @var string
455      */
456     var $formid = null;
457     /**
458      * List of attached actions
459      * @var array of component_action
460      */
461     var $helpicon = null;
462     /**
463      * Constructor
464      * @param array $urls list of options
465      * @param string $selected selected element
466      * @param array $nothing
467      * @param string $formid
468      */
469     public function __construct(array $urls, $selected='', $nothing=array(''=>'choosedots'), $formid=null) {
470         $this->urls     = $urls;
471         $this->selected = $selected;
472         $this->nothing  = $nothing;
473         $this->formid   = $formid;
474     }
476     /**
477      * Constructor: sets up the other components in case they are needed
478      * @param string $page  The keyword that defines a help page
479      * @param string $title A descriptive text for accesibility only
480      * @param string $component
481      * @param bool $linktext add extra text to icon
482      * @return void
483      */
484     public function set_help_icon($helppage, $title, $component = 'moodle') {
485         $this->helpicon = new help_icon($helppage, $title, $component);
486     }
488     /**
489      * Set's select lable
490      * @param string $label
491      * @return void
492      */
493     public function set_label($label) {
494         $this->label = $label;
495     }
499 /**
500  * Data structure describing html link with special action attached.
501  * @copyright 2010 Petr Skoda
502  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
503  * @since     Moodle 2.0
504  */
505 class action_link implements renderable {
506     /**
507      * Href url
508      * @var moodle_url
509      */
510     var $url;
511     /**
512      * Link text
513      * @var string HTML fragment
514      */
515     var $text;
516     /**
517      * HTML attributes
518      * @var array
519      */
520     var $attributes;
521     /**
522      * List of actions attached to link
523      * @var array of component_action
524      */
525     var $actions;
527     /**
528      * Constructor
529      * @param string|moodle_url $url
530      * @param string $text HTML fragment
531      * @param component_action $action
532      * @param array $attributes associative array of html link attributes + disabled
533      */
534     public function __construct(moodle_url $url, $text, component_action $action=null, array $attributes=null) {
535         $this->url       = clone($url);
536         $this->text      = $text;
537         if ($action) {
538             $this->add_action($action);
539         }
540     }
542     /**
543      * Add action to the link.
544      * @param component_action $action
545      * @return void
546      */
547     public function add_action(component_action $action) {
548         $this->actions[] = $action;
549     }
553 // ==== HTML writer and helper classes, will be probably moved elsewhere ======
555 /**
556  * Simple html output class
557  * @copyright 2009 Tim Hunt, 2010 Petr Skoda
558  */
559 class html_writer {
560     /**
561      * Outputs a tag with attributes and contents
562      * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
563      * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
564      * @param string $contents What goes between the opening and closing tags
565      * @return string HTML fragment
566      */
567     public static function tag($tagname, array $attributes = null, $contents) {
568         return self::start_tag($tagname, $attributes) . $contents . self::end_tag($tagname);
569     }
571     /**
572      * Outputs an opening tag with attributes
573      * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
574      * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
575      * @return string HTML fragment
576      */
577     public static function start_tag($tagname, array $attributes = null) {
578         return '<' . $tagname . self::attributes($attributes) . '>';
579     }
581     /**
582      * Outputs a closing tag
583      * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
584      * @return string HTML fragment
585      */
586     public static function end_tag($tagname) {
587         return '</' . $tagname . '>';
588     }
590     /**
591      * Outputs an empty tag with attributes
592      * @param string $tagname The name of tag ('input', 'img', 'br' etc.)
593      * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
594      * @return string HTML fragment
595      */
596     public static function empty_tag($tagname, array $attributes = null) {
597         return '<' . $tagname . self::attributes($attributes) . ' />';
598     }
600     /**
601      * Outputs a HTML attribute and value
602      * @param string $name The name of the attribute ('src', 'href', 'class' etc.)
603      * @param string $value The value of the attribute. The value will be escaped with {@link s()}
604      * @return string HTML fragment
605      */
606     public static function attribute($name, $value) {
607         if (is_array($value)) {
608             debugging("Passed an array for the HTML attribute $name", DEBUG_DEVELOPER);
609         }
610         if ($value instanceof moodle_url) {
611             return ' ' . $name . '="' . $value->out() . '"';
612         }
614         // special case, we do not want these in output
615         if ($value === null) {
616             return '';
617         }
619         // no sloppy trimming here!
620         return ' ' . $name . '="' . s($value) . '"';
621     }
623     /**
624      * Outputs a list of HTML attributes and values
625      * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
626      *       The values will be escaped with {@link s()}
627      * @return string HTML fragment
628      */
629     public static function attributes(array $attributes = null) {
630         $attributes = (array)$attributes;
631         $output = '';
632         foreach ($attributes as $name => $value) {
633             $output .= self::attribute($name, $value);
634         }
635         return $output;
636     }
638     /**
639      * Generates random html element id.
640      * @param string $base
641      * @return string
642      */
643     public static function random_id($base='random') {
644         return uniqid($base);
645     }
647     /**
648      * Generates a simple html link
649      * @param string|moodle_url $url
650      * @param string $text link txt
651      * @param array $attributes extra html attributes
652      * @return string HTML fragment
653      */
654     public static function link($url, $text, array $attributes = null) {
655         $attributes = (array)$attributes;
656         $attributes['href']  = $url;
657         return self::tag('a', $attributes, $text);
658     }
660     /**
661      * generates a simple checkbox with optional label
662      * @param string $name
663      * @param string $value
664      * @param bool $checked
665      * @param string $label
666      * @param array $attributes
667      * @return string html fragment
668      */
669     public static function checkbox($name, $value, $checked = true, $label = '', array $attributes = null) {
670         $attributes = (array)$attributes;
671         $output = '';
673         if ($label !== '' and !is_null($label)) {
674             if (empty($attributes['id'])) {
675                 $attributes['id'] = self::random_id('checkbox_');
676             }
677         }
678         $attributes['type']    = 'checkbox';
679         $attributes['value']   = $value;
680         $attributes['name']    = $name;
681         $attributes['checked'] = $checked ? 'selected' : null;
683         $output .= self::empty_tag('input', $attributes);
685         if ($label !== '' and !is_null($label)) {
686             $output .= self::tag('label', array('for'=>$attributes['id']), $label);
687         }
689         return $output;
690     }
692     /**
693      * Generates a simple select form field
694      * @param array $options associative array value=>label ex.:
695      *                array(1=>'One, 2=>Two)
696      *              it is also possible to specify optgroup as complex label array ex.:
697      *                array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
698      *                array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
699      * @param string $name name of select element
700      * @param string|array $selected value or arary of values depending on multiple attribute
701      * @param array|bool $nothing, add nothing selected option, or false of not added
702      * @param array $attributes - html select element attributes
703      * @return string HRML fragment
704      */
705     public static function select(array $options, $name, $selected = '', $nothing = array(''=>'choosedots'), array $attributes = null) {
706         $attributes = (array)$attributes;
707         if (is_array($nothing)) {
708             foreach ($nothing as $k=>$v) {
709                 if ($v === 'choose' or $v === 'choosedots') {
710                     $nothing[$k] = get_string('choosedots');
711                 }
712             }
713             $options = $nothing + $options; // keep keys, do not override
715         } else if (is_string($nothing) and $nothing !== '') {
716             // BC
717             $options = array(''=>$nothing) + $options;
718         }
720         // we may accept more values if multiple attribute specified
721         $selected = (array)$selected;
722         foreach ($selected as $k=>$v) {
723             $selected[$k] = (string)$v;
724         }
726         if (!isset($attributes['id'])) {
727             $id = 'menu'.$name;
728             // name may contaion [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading
729             $id = str_replace('[', '', $id);
730             $id = str_replace(']', '', $id);
731             $attributes['id'] = $id;
732         }
734         if (!isset($attributes['class'])) {
735             $class = 'menu'.$name;
736             // name may contaion [], which would make an invalid class. e.g. numeric question type editing form, assignment quickgrading
737             $class = str_replace('[', '', $class);
738             $class = str_replace(']', '', $class);
739             $attributes['class'] = $class;
740         }
741         $attributes['class'] = 'select ' . $attributes['class']; /// Add 'select' selector always
743         $attributes['name'] = $name;
745         $output = '';
746         foreach ($options as $value=>$label) {
747             if (is_array($label)) {
748                 // ignore key, it just has to be unique
749                 $output .= self::select_optgroup(key($label), current($label), $selected);
750             } else {
751                 $output .= self::select_option($label, $value, $selected);
752             }
753         }
754         return self::tag('select', $attributes, $output);
755     }
757     private static function select_option($label, $value, array $selected) {
758         $attributes = array();
759         $value = (string)$value;
760         if (in_array($value, $selected, true)) {
761             $attributes['selected'] = 'selected';
762         }
763         $attributes['value'] = $value;
764         return self::tag('option', $attributes, $label);
765     }
767     private static function select_optgroup($groupname, $options, array $selected) {
768         if (empty($options)) {
769             return '';
770         }
771         $attributes = array('label'=>$groupname);
772         $output = '';
773         foreach ($options as $value=>$label) {
774             $output .= self::select_option($label, $value, $selected);
775         }
776         return self::tag('optgroup', $attributes, $output);
777     }
779     /**
780      * Returns hidden input fields created from url parameters.
781      * @param moodle_url $url
782      * @param array $exclude list of excluded parameters
783      * @return string HTML fragment
784      */
785     public static function input_hidden_params(moodle_url $url, array $exclude = null) {
786         $exclude = (array)$exclude;
787         $params = $url->params();
788         foreach ($exclude as $key) {
789             unset($params[$key]);
790         }
792         $output = '';
793         foreach ($params as $key => $value) {
794             $attributes = array('type'=>'hidden', 'name'=>$key, 'value'=>$value);
795             $output .= self::empty_tag('input', $attributes)."\n";
796         }
797         return $output;
798     }
800     /**
801      * Generate a script tag containing the the specified code.
802      *
803      * @param string $js the JavaScript code
804          * @param moodle_url|string optional url of the external script, $code ignored if specified
805      * @return string HTML, the code wrapped in <script> tags.
806      */
807     public static function script($jscode, $url=null) {
808         if ($jscode) {
809             $attributes = array('type'=>'text/javascript');
810             return self::tag('script', $attributes, "\n//<![CDATA[\n$jscode\n//]]>\n") . "\n";
812         } else if ($url) {
813             $attributes = array('type'=>'text/javascript', 'src'=>$url);
814             return self::tag('script', $attributes, '') . "\n";
816         } else {
817             return '';
818         }
819     }
822 // ==== JS writer and helper classes, will be probably moved elsewhere ======
824 /**
825  * Simple javascript output class
826  * @copyright 2010 Petr Skoda
827  */
828 class js_writer {
829     /**
830      * Returns javascript code calling the function
831      * @param string $function function name, can be complex lin Y.Event.purgeElement
832      * @param array $arguments parameters
833      * @param int $delay execution delay in seconds
834      * @return string JS code fragment
835      */
836     public function function_call($function, array $arguments = null, $delay=0) {
837         if ($arguments) {
838             $arguments = array_map('json_encode', $arguments);
839             $arguments = implode(', ', $arguments);
840         } else {
841             $arguments = '';
842         }
843         $js = "$function($arguments);";
845         if ($delay) {
846             $delay = $delay * 1000; // in miliseconds
847             $js = "setTimeout(function() { $js }, $delay);";
848         }
849         return $js . "\n";
850     }
852     /**
853      * Special function which adds Y as first argument of fucntion call.
854      * @param string $function
855      * @param array $extraarguments
856      * @return string
857      */
858     public function function_call_with_Y($function, array $extraarguments = null) {
859         if ($extraarguments) {
860             $extraarguments = array_map('json_encode', $extraarguments);
861             $arguments = 'Y, ' . implode(', ', $extraarguments);
862         } else {
863             $arguments = 'Y';
864         }
865         return "$function($arguments);\n";
866     }
868     /**
869      * Returns JavaScript code to initialise a new object
870      * @param string|null $var If it is null then no var is assigned the new object
871      * @param string $class
872      * @param array $arguments
873      * @param array $requirements
874      * @param int $delay
875      * @return string
876      */
877     public function object_init($var, $class, array $arguments = null, array $requirements = null, $delay=0) {
878         if (is_array($arguments)) {
879             $arguments = array_map('json_encode', $arguments);
880             $arguments = implode(', ', $arguments);
881         }
883         if ($var === null) {
884             $js = "new $class(Y, $arguments);";
885         } else if (strpos($var, '.')!==false) {
886             $js = "$var = new $class(Y, $arguments);";
887         } else {
888             $js = "var $var = new $class(Y, $arguments);";
889         }
891         if ($delay) {
892             $delay = $delay * 1000; // in miliseconds
893             $js = "setTimeout(function() { $js }, $delay);";
894         }
896         if (count($requirements) > 0) {
897             $requirements = implode("', '", $requirements);
898             $js = "Y.use('$requirements', function(Y){ $js });";
899         }
900         return $js."\n";
901     }
903     /**
904      * Returns code setting value to variable
905      * @param string $name
906      * @param mixed $value json serialised value
907      * @param bool $usevar add var definition, ignored for nested properties
908      * @return string JS code fragment
909      */
910     public function set_variable($name, $value, $usevar=true) {
911         $output = '';
913         if ($usevar) {
914             if (strpos($name, '.')) {
915                 $output .= '';
916             } else {
917                 $output .= 'var ';
918             }
919         }
921         $output .= "$name = ".json_encode($value).";";
923         return $output;
924     }
926     /**
927      * Writes event handler attaching code
928      * @param mixed $selector standard YUI selector for elemnts, may be array or string, element id is in the form "#idvalue"
929      * @param string $event A valid DOM event (click, mousedown, change etc.)
930      * @param string $function The name of the function to call
931      * @param array  $arguments An optional array of argument parameters to pass to the function
932      * @return string JS code fragment
933      */
934     public function event_handler($selector, $event, $function, array $arguments = null) {
935         $selector = json_encode($selector);
936         $output = "Y.on('$event', $function, $selector, null";
937         if (!empty($arguments)) {
938             $output .= ', ' . json_encode($arguments);
939         }
940         return $output . ");\n";
941     }
945 // ===============================================================================================
946 // TODO: Following components will be refactored soon
948 /**
949  * Base class for classes representing HTML elements, like html_select.
950  *
951  * Handles the id and class attributes.
952  *
953  * @copyright 2009 Tim Hunt
954  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
955  * @since     Moodle 2.0
956  */
957 class html_component {
958     /**
959      * @var string value to use for the id attribute of this HTML tag.
960      */
961     public $id = null;
962     /**
963      * @var string $alt value to use for the alt attribute of this HTML tag.
964      */
965     public $alt = null;
966     /**
967      * @var string $style value to use for the style attribute of this HTML tag.
968      */
969     public $style = null;
970     /**
971      * @var array class names to add to this HTML element.
972      */
973     public $classes = array();
974     /**
975      * @var string $title The title attributes applicable to any XHTML element
976      */
977     public $title = null;
978     /**
979      * An optional array of component_action objects handling the action part of this component.
980      * @var array $actions
981      */
982     protected $actions = array();
984     /**
985      * Compoment constructor.
986      * @param array $options image attributes such as title, id, alt, style, class
987      */
988     public function __construct(array $options = null) {
989         // not implemented in this class because we want to set only public properties of this component
990         renderer_base::apply_component_options($this, $options);
991     }
993     /**
994      * Ensure some class names are an array.
995      * @param mixed $classes either an array of class names or a space-separated
996      *      string containing class names.
997      * @return array the class names as an array.
998      */
999     public static function clean_classes($classes) {
1000         if (empty($classes)) {
1001             return array();
1002         } else if (is_array($classes)) {
1003             return $classes;
1004         } else {
1005             return explode(' ', trim($classes));
1006         }
1007     }
1009     /**
1010      * Set the class name array.
1011      * @param mixed $classes either an array of class names or a space-separated
1012      *      string containing class names.
1013      * @return void
1014      */
1015     public function set_classes($classes) {
1016         $this->classes = self::clean_classes($classes);
1017     }
1019     /**
1020      * Add a class name to the class names array.
1021      * @param string $class the new class name to add.
1022      * @return void
1023      */
1024     public function add_class($class) {
1025         $this->classes[] = $class;
1026     }
1028     /**
1029      * Add a whole lot of class names to the class names array.
1030      * @param mixed $classes either an array of class names or a space-separated
1031      *      string containing class names.
1032      * @return void
1033      */
1034     public function add_classes($classes) {
1035         $this->classes = array_merge($this->classes, self::clean_classes($classes));
1036     }
1038     /**
1039      * Get the class names as a string.
1040      * @return string the class names as a space-separated string. Ready to be put in the class="" attribute.
1041      */
1042     public function get_classes_string() {
1043         return implode(' ', $this->classes);
1044     }
1046     /**
1047      * Perform any cleanup or final processing that should be done before an
1048      * instance of this class is output. This method is supposed to be called
1049      * only from renderers.
1050      *
1051      * @param renderer_base $output output renderer
1052      * @param moodle_page $page
1053      * @param string $target rendering target
1054      * @return void
1055      */
1056     public function prepare(renderer_base $output, moodle_page $page, $target) {
1057         $this->classes = array_unique(self::clean_classes($this->classes));
1058     }
1060     /**
1061      * This checks developer do not try to assign a property directly
1062      * if we have a setter for it. Otherwise, the property is set as expected.
1063      * @param string $name The name of the variable to set
1064      * @param mixed $value The value to assign to the variable
1065      * @return void
1066      */
1067     public function __set($name, $value) {
1068         if ($name == 'class') {
1069             debugging('this way of setting css class has been deprecated. use set_classes() method instead.');
1070             $this->set_classes($value);
1071         } else {
1072             $this->{$name} = $value;
1073         }
1074     }
1076     /**
1077      * Adds a JS action to this component.
1078      * Note: the JS function you write must have only two arguments: (string)event and (object|array)args
1079      * If you want to add an instantiated component_action (or one of its subclasses), give the object as the only parameter
1080      *
1081      * @param mixed  $event a DOM event (click, mouseover etc.) or a component_action object
1082      * @param string $jsfunction The name of the JS function to call. required if argument 1 is a string (event)
1083      * @param array  $jsfunctionargs An optional array of JS arguments to pass to the function
1084      */
1085     public function add_action($event, $jsfunction=null, $jsfunctionargs=array()) {
1086         if (empty($this->id)) {
1087             $this->generate_id();
1088         }
1090         if ($event instanceof component_action) {
1091             $this->actions[] = $event;
1092         } else {
1093             if (empty($jsfunction)) {
1094                 throw new coding_exception('html_component::add_action requires a JS function argument if the first argument is a string event');
1095             }
1096             $this->actions[] = new component_action($event, $jsfunction, $jsfunctionargs);
1097         }
1098     }
1100     /**
1101      * Internal method for generating a unique ID for the purpose of event handlers.
1102      */
1103     protected function generate_id() {
1104         $this->id = uniqid(get_class($this));
1105     }
1107     /**
1108      * Returns the array of component_actions.
1109      * @return array Component actions
1110      */
1111     public function get_actions() {
1112         return $this->actions;
1113     }
1115     /**
1116      * Shortcut for adding a JS confirm dialog when the component is clicked.
1117      * The message must be a yes/no question.
1118      * @param string $message The yes/no confirmation question. If "Yes" is clicked, the original action will occur.
1119      * @param string $callback The name of a JS function whose scope will be set to the simpleDialog object and have this
1120      *    function's arguments set as this.args.
1121      * @return void
1122      */
1123     public function add_confirm_action($message, $callback=null) {
1124         $this->add_action(new component_action('click', 'M.util.show_confirm_dialog', array('message' => $message, 'callback' => $callback)));
1125     }
1127     /**
1128      * Returns true if this component has an action of the requested type (component_action by default).
1129      * @param string $class The class of the action we are looking for
1130      * @return boolean True if action is found
1131      */
1132     public function has_action($class='component_action') {
1133         foreach ($this->actions as $action) {
1134             if (get_class($action) == $class) {
1135                 return true;
1136             }
1137         }
1138         return false;
1139     }
1143 class labelled_html_component extends html_component {
1144     /**
1145      * @var mixed $label The label for that component. String or html_label object
1146      */
1147     public $label;
1149     /**
1150      * Compoment constructor.
1151      * @param array $options image attributes such as title, id, alt, style, class
1152      */
1153     public function __construct(array $options = null) {
1154         parent::__construct($options);
1155     }
1157     /**
1158      * Adds a descriptive label to the component.
1159      *
1160      * This can be used in two ways:
1161      *
1162      * <pre>
1163      * $component->set_label($elementlabel, $elementid);
1164      * // OR
1165      * $label = new html_label();
1166      * $label->for = $elementid;
1167      * $label->text = $elementlabel;
1168      * $component->set_label($label);
1169      * </pre>
1170      *
1171      * Use the second form when you need to add additional HTML attributes
1172      * to the label and/or JS actions.
1173      *
1174      * @param mixed $text Either the text of the label or a html_label object
1175      * @param text  $for The value of the "for" attribute (the associated element's id)
1176      * @return void
1177      */
1178     public function set_label($text, $for=null) {
1179         if ($text instanceof html_label) {
1180             $this->label = $text;
1181         } else if (!empty($text)) {
1182             $this->label = new html_label();
1183             $this->label->for = $for;
1184             if (empty($for)) {
1185                 if (empty($this->id)) {
1186                     $this->generate_id();
1187                 }
1188                 $this->label->for = $this->id;
1189             }
1190             $this->label->text = $text;
1191         }
1192     }
1195 /// Components representing HTML elements
1197 /**
1198  * This class represents a label element
1199  *
1200  * @copyright 2009 Nicolas Connault
1201  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1202  * @since     Moodle 2.0
1203  */
1204 class html_label extends html_component {
1205     /**
1206      * @var string $text The text to display in the label
1207      */
1208     public $text;
1209     /**
1210      * @var string $for The name of the form field this label is associated with
1211      */
1212     public $for;
1214     /**
1215      * @see html_component::prepare()
1216      * @return void
1217      */
1218     public function prepare(renderer_base $output, moodle_page $page, $target) {
1219         if (empty($this->text)) {
1220             throw new coding_exception('html_label must have a $text value.');
1221         }
1222         parent::prepare($output, $page, $target);
1223     }
1227 /**
1228  * This class hold all the information required to describe a <select> menu that
1229  * will be printed by {@link core_renderer::select()}. (Or by an overridden
1230  * version of that method in a subclass.)
1231  *
1232  * This component can also hold enough metadata to be used as a popup form. It just
1233  * needs a bit more setting up than for a simple menu. See the shortcut methods for
1234  * developer-friendly usage.
1235  *
1236  * All the fields that are not set by the constructor have sensible defaults, so
1237  * you only need to set the properties where you want non-default behaviour.
1238  *
1239  * @copyright 2009 Tim Hunt
1240  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1241  * @since     Moodle 2.0
1242  */
1243 class html_select extends labelled_html_component {
1244     /**
1245      * The html_select object parses an array of options into component objects
1246      * @see nested attribute
1247      * @var mixed $options the choices to show in the menu. An array $value => $display, of html_select_option or of html_select_optgroup objects.
1248      */
1249     public $options;
1250     /**
1251      * @var string $name the name of this form control. That is, the name of the GET/POST
1252      * variable that will be set if this select is submitted as part of a form.
1253      */
1254     public $name;
1255     /**
1256      * @var string $selectedvalue the option to select initially. Should match one
1257      * of the $options array keys. Default none.
1258      */
1259     public $selectedvalue;
1260     /**
1261      * Defaults to get_string('choosedots').
1262      * Set this to '' if you do not want a 'nothing is selected' option.
1263      * This is ignored if the rendertype is 'radio' or 'checkbox'
1264      * @var string The label for the 'nothing is selected' option.
1265      */
1266     public $nothinglabel = null;
1267     /**
1268      * @var string The value returned by the 'nothing is selected' option. Defaults to 0.
1269      */
1270     public $nothingvalue = 0;
1271     /**
1272      * @var boolean set this to true if you want the control to appear disabled.
1273      */
1274     public $disabled = false;
1275     /**
1276      * @var integer if non-zero, sets the tabindex attribute on the <select> element. Default 0.
1277      */
1278     public $tabindex = 0;
1279     /**
1280      * @var mixed Defaults to false, which means display the select as a dropdown menu.
1281      * If true, display this select as a list box whose size is chosen automatically.
1282      * If an integer, display as list box of that size.
1283      */
1284     public $listbox = false;
1285     /**
1286      * @var integer if you are using $listbox === true to get an automatically
1287      * sized list box, the size of the list box will be the number of options,
1288      * or this number, whichever is smaller.
1289      */
1290     public $maxautosize = 10;
1291     /**
1292      * @var boolean if true, allow multiple selection. Only used if $listbox is true, or if
1293      *      the select is to be output as checkboxes.
1294      */
1295     public $multiple = false;
1296     /**
1297      * Another way to use nested menu is to prefix optgroup labels with -- and end the optgroup with --
1298      * Leave this setting to false if you are using the latter method.
1299      * @var boolean $nested if true, uses $options' keys as option headings (optgroup)
1300      */
1301     public $nested = false;
1302     /**
1303      * @var html_form $form An optional html_form component
1304      */
1305     public $form;
1306     /**
1307      * @var help_icon $array help icon params
1308      */
1309     public $helpicon;
1310     /**
1311      * @var boolean $rendertype How the select element should be rendered: menu or radio (checkbox is just radio + multiple)
1312      */
1313     public $rendertype = 'menu';
1315     /**
1316      * @see html_component::prepare()
1317      * @return void
1318      */
1319     public function prepare(renderer_base $output, moodle_page $page, $target) {
1320         global $CFG;
1322         // name may contain [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading
1323         if (empty($this->id)) {
1324             $this->id = 'menu' . str_replace(array('[', ']'), '', $this->name);
1325         }
1327         if (empty($this->classes)) {
1328             $this->set_classes(array('menu' . str_replace(array('[', ']'), '', $this->name)));
1329         }
1331         if (is_null($this->nothinglabel)) {
1332             $this->nothinglabel = get_string('choosedots');
1333         }
1335         if (!empty($this->label) && !($this->label instanceof html_label)) {
1336             $label = new html_label();
1337             $label->text = $this->label;
1338             $label->for = $this->name;
1339             $this->label = $label;
1340         }
1342         $this->add_class('select');
1344         $this->initialise_options();
1345         parent::prepare($output, $page, $target);
1346     }
1348     /**
1349      * This is a shortcut for making a simple select menu. It lets you specify
1350      * the options, name and selected option in one line of code.
1351      * @param array $options used to initialise {@link $options}.
1352      * @param string $name used to initialise {@link $name}.
1353      * @param string $selected  used to initialise {@link $selected}.
1354      * @param string $nothinglabel The label for the 'nothing is selected' option. Defaults to "Choose..."
1355      * @return html_select A html_select object with the three common fields initialised.
1356      */
1357     public static function make($options, $name, $selected = '', $nothinglabel='choosedots') {
1358         $menu = new html_select();
1359         $menu->options = $options;
1360         $menu->name = $name;
1361         $menu->selectedvalue = $selected;
1362         return $menu;
1363     }
1365     /**
1366      * This is a shortcut for making a yes/no select menu.
1367      * @param string $name used to initialise {@link $name}.
1368      * @param string $selected  used to initialise {@link $selected}.
1369      * @return html_select A menu initialised with yes/no options.
1370      */
1371     public static function make_yes_no($name, $selected) {
1372         return self::make(array(0 => get_string('no'), 1 => get_string('yes')), $name, $selected);
1373     }
1375     /**
1376      * This is a shortcut for making an hour selector menu.
1377      * @param string $type The type of selector (years, months, days, hours, minutes)
1378      * @param string $name fieldname
1379      * @param int $currenttime A default timestamp in GMT
1380      * @param int $step minute spacing
1381      * @return html_select A menu initialised with hour options.
1382      */
1383     public static function make_time_selector($type, $name, $currenttime=0, $step=5) {
1385         if (!$currenttime) {
1386             $currenttime = time();
1387         }
1388         $currentdate = usergetdate($currenttime);
1389         $userdatetype = $type;
1391         switch ($type) {
1392             case 'years':
1393                 for ($i=1970; $i<=2020; $i++) {
1394                     $timeunits[$i] = $i;
1395                 }
1396                 $userdatetype = 'year';
1397                 break;
1398             case 'months':
1399                 for ($i=1; $i<=12; $i++) {
1400                     $timeunits[$i] = userdate(gmmktime(12,0,0,$i,15,2000), "%B");
1401                 }
1402                 $userdatetype = 'month';
1403                 $currentdate['month'] = $currentdate['mon'];
1404                 break;
1405             case 'days':
1406                 for ($i=1; $i<=31; $i++) {
1407                     $timeunits[$i] = $i;
1408                 }
1409                 $userdatetype = 'mday';
1410                 break;
1411             case 'hours':
1412                 for ($i=0; $i<=23; $i++) {
1413                     $timeunits[$i] = sprintf("%02d",$i);
1414                 }
1415                 break;
1416             case 'minutes':
1417                 if ($step != 1) {
1418                     $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
1419                 }
1421                 for ($i=0; $i<=59; $i+=$step) {
1422                     $timeunits[$i] = sprintf("%02d",$i);
1423                 }
1424                 break;
1425             default:
1426                 throw new coding_exception("Time type $type is not supported by html_select::make_time_selector().");
1427         }
1429         $timerselector = self::make($timeunits, $name, $currentdate[$userdatetype]);
1430         $timerselector->label = new html_label();
1432         $timerselector->label->text = get_string(substr($type, 0, -1), 'form');
1433         $timerselector->label->for = "menu$timerselector->name";
1434         $timerselector->label->add_class('accesshide');
1435         $timerselector->nothinglabel = '';
1437         return $timerselector;
1438     }
1440     /**
1441      * Given an associative array of type => fieldname and an optional timestamp,
1442      * returns an array of html_select components representing date/time selectors.
1443      * @param array $selectors Arrays of type => fieldname. Selectors will be returned in the order of the types given
1444      * @param int $currenttime A UNIX timestamp
1445      * @param int $step minute spacing
1446      * @return array Instantiated date/time selectors
1447      */
1448     public static function make_time_selectors($selectors, $currenttime=0, $step=5) {
1449         $selects = array();
1450         foreach ($selectors as $type => $name) {
1451             $selects[] = html_select::make_time_selector($type, $name, $currenttime, $step);
1452         }
1453         return $selects;
1454     }
1456     /**
1457      * This is a shortcut for making a select popup form.
1458      * @param mixed $baseurl The target URL, string or moodle_url
1459      * @param string $name The variable which this select's options are changing in the URL
1460      * @param array $options A list of value-label pairs for the popup list
1461      * @param string $formid id for the control. Must be unique on the page. Used in the HTML.
1462      * @param string $selected The option that is initially selected
1463      * @return html_select A menu initialised as a popup form.
1464      */
1465     public static function make_popup_form($baseurl, $name, $options, $formid, $selected=null) {
1466         global $CFG;
1468         $selectedurl = null;
1470         if (!($baseurl instanceof moodle_url)) {
1471             $baseurl = new moodle_url($baseurl);
1472         }
1474         if (!empty($selected)) {
1475             $selectedurl = $baseurl->out(false, array($name => $selected));
1476         }
1478         // Replace real value by formatted URLs
1479         foreach ($options as $value => $label) {
1480             $options[$baseurl->out(false, array($name => $value))] = $label;
1481             unset($options[$value]);
1482         }
1484         $select = self::make($options, 'jump', $selectedurl);
1486         $select->form = new html_form();
1487         $select->form->id = $formid;
1488         $select->form->method = 'get';
1489         $select->form->jssubmitaction = true;
1490         $select->form->add_class('popupform');
1491         $select->form->url = new moodle_url('/course/jumpto.php', array('sesskey' => sesskey()));
1492         $select->form->button->text = get_string('go');
1494         $select->id = $formid . '_jump';
1496         $select->add_action('change', 'submit_form_by_id', array('id' => $formid, 'selectid' => $select->id));
1498         return $select;
1499     }
1501     /**
1502      * Adds a help icon next to the select menu.
1503      *
1504      * <pre>
1505      * $select->set_help_icon($page, $text, $component);
1506      * </pre>
1507      *
1508      * @param string $helppage Either the keyword that defines a help page or a help_icon object
1509      * @param text  $text The text of the help icon
1510      * @param component $component
1511      * @param boolean $linktext Whether or not to show text next to the icon
1512      * @return void
1513      */
1514     public function set_help_icon($helppage='', $text='', $component='moodle') {
1515         if ($helppage) {
1516             $this->helpicon = array('helppage'=>$helppage, 'text'=>$text, 'component'=>$component);
1517         } else {
1518             $this->helpicon = null;
1519         }
1520     }
1522     /**
1523      * Parses the $options array and instantiates html_select_option objects in
1524      * the place of the original value => label pairs. This is useful for when you
1525      * need to setup extra html attributes and actions on individual options before
1526      * the component is sent to the renderer
1527      * @return void;
1528      */
1529     public function initialise_options() {
1530         // If options are already instantiated objects, stop here
1531         $firstoption = reset($this->options);
1532         if ($firstoption instanceof html_select_option || $firstoption instanceof html_select_optgroup) {
1533             return;
1534         }
1536         if ($this->rendertype == 'radio' && $this->multiple) {
1537             $this->rendertype = 'checkbox';
1538         }
1540         // If nested is on, or if radio/checkbox rendertype is set, remove the default Choose option
1541         if ($this->nested || $this->rendertype == 'radio' || $this->rendertype == 'checkbox') {
1542             $this->nothinglabel = '';
1543         }
1545         $options = $this->options;
1547         $this->options = array();
1549         if ($this->nested && $this->rendertype != 'menu') {
1550             throw new coding_exception('html_select cannot render nested options as radio buttons or checkboxes.');
1551         } else if ($this->nested) {
1552             foreach ($options as $section => $values) {
1553                 $optgroup = new html_select_optgroup();
1554                 $optgroup->text = $section;
1556                 foreach ($values as $value => $display) {
1557                     $option = new html_select_option();
1558                     $option->value = s($value);
1559                     $option->text = $display;
1560                     if ($display === '') {
1561                         $option->text = $value;
1562                     }
1564                     if ((string) $value == (string) $this->selectedvalue ||
1565                             (is_array($this->selectedvalue) && in_array($value, $this->selectedvalue))) {
1566                         $option->selected = 'selected';
1567                     }
1569                     $optgroup->options[] = $option;
1570                 }
1572                 $this->options[] = $optgroup;
1573             }
1574         } else {
1575             $inoptgroup = false;
1576             $optgroup = false;
1578             foreach ($options as $value => $display) {
1579                 if ($display == '--') { /// we are ending previous optgroup
1580                     // $this->options[] = $optgroup;
1581                     $inoptgroup = false;
1582                     continue;
1583                 } else if (substr($display,0,2) == '--') { /// we are starting a new optgroup
1584                     if (!empty($optgroup->options)) {
1585                         $this->options[] = $optgroup;
1586                     }
1588                     $optgroup = new html_select_optgroup();
1589                     $optgroup->text = substr($display,2); // stripping the --
1591                     $inoptgroup = true; /// everything following will be in an optgroup
1592                     continue;
1594                 } else {
1595                     // Add $nothing option if there are not optgroups
1596                     if ($this->nothinglabel && empty($this->options[0]) && !$inoptgroup) {
1597                         $nothingoption = new html_select_option();
1598                         $nothingoption->value = 0;
1599                         if (!empty($this->nothingvalue)) {
1600                             $nothingoption->value = $this->nothingvalue;
1601                         }
1602                         $nothingoption->text = $this->nothinglabel;
1603                         $this->options = array($nothingoption) + $this->options;
1604                     }
1606                     $option = new html_select_option();
1607                     $option->text = $display;
1609                     if ($display === '') {
1610                         $option->text = $value;
1611                     }
1613                     if ((string) $value == (string) $this->selectedvalue ||
1614                             (is_array($this->selectedvalue) && in_array($value, $this->selectedvalue))) {
1615                         $option->selected = 'selected';
1616                     }
1618                     $option->value = s($value);
1620                     if ($inoptgroup) {
1621                         $optgroup->options[] = $option;
1622                     } else {
1623                         $this->options[] = $option;
1624                     }
1625                 }
1626             }
1628             if ($optgroup) {
1629                 $this->options[] = $optgroup;
1630             }
1631         }
1632     }
1636 /**
1637  * This class represents a select option element
1638  *
1639  * @copyright 2009 Nicolas Connault
1640  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1641  * @since     Moodle 2.0
1642  */
1643 class html_select_option extends labelled_html_component {
1644     /**
1645      * @var string $value The value of this option (will be sent with form)
1646      */
1647     public $value;
1648     /**
1649      * @var string $text The display value of the option
1650      */
1651     public $text;
1652     /**
1653      * @var boolean $selected Whether or not this option is selected
1654      */
1655     public $selected = false;
1656     /**
1657      * @var boolean $disabled Whether or not this option is disabled
1658      */
1659     public $disabled = false;
1661     public function __construct() {
1662         $this->label = new html_label();
1663     }
1665     /**
1666      * @see html_component::prepare()
1667      * @return void
1668      */
1669     public function prepare(renderer_base $output, moodle_page $page, $target) {
1670         if (empty($this->text) && (string)$this->text!=='0') {
1671             throw new coding_exception('html_select_option requires a $text value.');
1672         }
1674         if (empty($this->label->text)) {
1675             $this->set_label($this->text);
1676         } else if (!($this->label instanceof html_label)) {
1677             $this->set_label($this->label);
1678         }
1679         if (empty($this->id)) {
1680             $this->generate_id();
1681         }
1683         parent::prepare($output, $page, $target);
1684     }
1688 /**
1689  * This class represents a select optgroup element
1690  *
1691  * @copyright 2009 Nicolas Connault
1692  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1693  * @since     Moodle 2.0
1694  */
1695 class html_select_optgroup extends html_component {
1696     /**
1697      * @var string $text The display value of the optgroup
1698      */
1699     public $text;
1700     /**
1701      * @var array $options An array of html_select_option objects
1702      */
1703     public $options = array();
1705     public function prepare(renderer_base $output, moodle_page $page, $target) {
1706         if (empty($this->text)) {
1707             throw new coding_exception('html_select_optgroup requires a $text value.');
1708         }
1709         if (empty($this->options)) {
1710             throw new coding_exception('html_select_optgroup requires at least one html_select_option object');
1711         }
1712         parent::prepare($output, $page, $target);
1713     }
1717 /**
1718  * Holds all the information required to render a <table> by
1719  * {@see core_renderer::table()} or by an overridden version of that
1720  * method in a subclass.
1721  *
1722  * Example of usage:
1723  * $t = new html_table();
1724  * ... // set various properties of the object $t as described below
1725  * echo $OUTPUT->table($t);
1726  *
1727  * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
1728  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1729  * @since     Moodle 2.0
1730  */
1731 class html_table extends labelled_html_component {
1732     /**
1733      * For more control over the rendering of the headers, an array of html_table_cell objects
1734      * can be passed instead of an array of strings.
1735      * @var array of headings. The n-th array item is used as a heading of the n-th column.
1736      *
1737      * Example of usage:
1738      * $t->head = array('Student', 'Grade');
1739      */
1740     public $head;
1741     /**
1742      * @var array can be used to make a heading span multiple columns
1743      *
1744      * Example of usage:
1745      * $t->headspan = array(2,1);
1746      *
1747      * In this example, {@see html_table:$data} is supposed to have three columns. For the first two columns,
1748      * the same heading is used. Therefore, {@see html_table::$head} should consist of two items.
1749      */
1750     public $headspan;
1751     /**
1752      * @var array of column alignments. The value is used as CSS 'text-align' property. Therefore, possible
1753      * values are 'left', 'right', 'center' and 'justify'. Specify 'right' or 'left' from the perspective
1754      * of a left-to-right (LTR) language. For RTL, the values are flipped automatically.
1755      *
1756      * Examples of usage:
1757      * $t->align = array(null, 'right');
1758      * or
1759      * $t->align[1] = 'right';
1760      *
1761      */
1762     public $align;
1763     /**
1764      * @var array of column sizes. The value is used as CSS 'size' property.
1765      *
1766      * Examples of usage:
1767      * $t->size = array('50%', '50%');
1768      * or
1769      * $t->size[1] = '120px';
1770      */
1771     public $size;
1772     /**
1773      * @var array of wrapping information. The only possible value is 'nowrap' that sets the
1774      * CSS property 'white-space' to the value 'nowrap' in the given column.
1775      *
1776      * Example of usage:
1777      * $t->wrap = array(null, 'nowrap');
1778      */
1779     public $wrap;
1780     /**
1781      * @var array of arrays or html_table_row objects containing the data. Alternatively, if you have
1782      * $head specified, the string 'hr' (for horizontal ruler) can be used
1783      * instead of an array of cells data resulting in a divider rendered.
1784      *
1785      * Example of usage with array of arrays:
1786      * $row1 = array('Harry Potter', '76 %');
1787      * $row2 = array('Hermione Granger', '100 %');
1788      * $t->data = array($row1, $row2);
1789      *
1790      * Example with array of html_table_row objects: (used for more fine-grained control)
1791      * $cell1 = new html_table_cell();
1792      * $cell1->text = 'Harry Potter';
1793      * $cell1->colspan = 2;
1794      * $row1 = new html_table_row();
1795      * $row1->cells[] = $cell1;
1796      * $cell2 = new html_table_cell();
1797      * $cell2->text = 'Hermione Granger';
1798      * $cell3 = new html_table_cell();
1799      * $cell3->text = '100 %';
1800      * $row2 = new html_table_row();
1801      * $row2->cells = array($cell2, $cell3);
1802      * $t->data = array($row1, $row2);
1803      */
1804     public $data;
1805     /**
1806      * @var string width of the table, percentage of the page preferred. Defaults to 80% of the page width.
1807      * @deprecated since Moodle 2.0. Styling should be in the CSS.
1808      */
1809     public $width = null;
1810     /**
1811      * @var string alignment the whole table. Can be 'right', 'left' or 'center' (default).
1812      * @deprecated since Moodle 2.0. Styling should be in the CSS.
1813      */
1814     public $tablealign = null;
1815     /**
1816      * @var int padding on each cell, in pixels
1817      * @deprecated since Moodle 2.0. Styling should be in the CSS.
1818      */
1819     public $cellpadding = null;
1820     /**
1821      * @var int spacing between cells, in pixels
1822      * @deprecated since Moodle 2.0. Styling should be in the CSS.
1823      */
1824     public $cellspacing = null;
1825     /**
1826      * @var array classes to add to particular rows, space-separated string.
1827      * Classes 'r0' or 'r1' are added automatically for every odd or even row,
1828      * respectively. Class 'lastrow' is added automatically for the last row
1829      * in the table.
1830      *
1831      * Example of usage:
1832      * $t->rowclasses[9] = 'tenth'
1833      */
1834     public $rowclasses;
1835     /**
1836      * @var array classes to add to every cell in a particular column,
1837      * space-separated string. Class 'cell' is added automatically by the renderer.
1838      * Classes 'c0' or 'c1' are added automatically for every odd or even column,
1839      * respectively. Class 'lastcol' is added automatically for all last cells
1840      * in a row.
1841      *
1842      * Example of usage:
1843      * $t->colclasses = array(null, 'grade');
1844      */
1845     public $colclasses;
1846     /**
1847      * @var string description of the contents for screen readers.
1848      */
1849     public $summary;
1850     /**
1851      * @var bool true causes the contents of the heading cells to be rotated 90 degrees.
1852      */
1853     public $rotateheaders = false;
1854     /**
1855      * @var array $headclasses Array of CSS classes to apply to the table's thead.
1856      */
1857     public $headclasses = array();
1858     /**
1859      * @var array $bodyclasses Array of CSS classes to apply to the table's tbody.
1860      */
1861     public $bodyclasses = array();
1862     /**
1863      * @var array $footclasses Array of CSS classes to apply to the table's tfoot.
1864      */
1865     public $footclasses = array();
1868     /**
1869      * @see html_component::prepare()
1870      * @return void
1871      */
1872     public function prepare(renderer_base $output, moodle_page $page, $target) {
1873         if (!empty($this->align)) {
1874             foreach ($this->align as $key => $aa) {
1875                 if ($aa) {
1876                     $this->align[$key] = 'text-align:'. fix_align_rtl($aa) .';';  // Fix for RTL languages
1877                 } else {
1878                     $this->align[$key] = null;
1879                 }
1880             }
1881         }
1882         if (!empty($this->size)) {
1883             foreach ($this->size as $key => $ss) {
1884                 if ($ss) {
1885                     $this->size[$key] = 'width:'. $ss .';';
1886                 } else {
1887                     $this->size[$key] = null;
1888                 }
1889             }
1890         }
1891         if (!empty($this->wrap)) {
1892             foreach ($this->wrap as $key => $ww) {
1893                 if ($ww) {
1894                     $this->wrap[$key] = 'white-space:nowrap;';
1895                 } else {
1896                     $this->wrap[$key] = '';
1897                 }
1898             }
1899         }
1900         if (!empty($this->head)) {
1901             foreach ($this->head as $key => $val) {
1902                 if (!isset($this->align[$key])) {
1903                     $this->align[$key] = null;
1904                 }
1905                 if (!isset($this->size[$key])) {
1906                     $this->size[$key] = null;
1907                 }
1908                 if (!isset($this->wrap[$key])) {
1909                     $this->wrap[$key] = null;
1910                 }
1912             }
1913         }
1914         if (empty($this->classes)) { // must be done before align
1915             $this->set_classes(array('generaltable'));
1916         }
1917         if (!empty($this->tablealign)) {
1918             $this->add_class('boxalign' . $this->tablealign);
1919         }
1920         if (!empty($this->rotateheaders)) {
1921             $this->add_class('rotateheaders');
1922         } else {
1923             $this->rotateheaders = false; // Makes life easier later.
1924         }
1925         parent::prepare($output, $page, $target);
1926     }
1927     /**
1928      * @param string $name The name of the variable to set
1929      * @param mixed $value The value to assign to the variable
1930      * @return void
1931      */
1932     public function __set($name, $value) {
1933         if ($name == 'rowclass') {
1934             debugging('rowclass[] has been deprecated for html_table ' .
1935                       'and should be replaced with rowclasses[]. please fix the code.');
1936             $this->rowclasses = $value;
1937         } else {
1938             parent::__set($name, $value);
1939         }
1940     }
1944 /**
1945  * Component representing a table row.
1946  *
1947  * @copyright 2009 Nicolas Connault
1948  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1949  * @since     Moodle 2.0
1950  */
1951 class html_table_row extends html_component {
1952     /**
1953      * @var array $cells Array of html_table_cell objects
1954      */
1955     public $cells = array();
1957     /**
1958      * @see lib/html_component#prepare()
1959      * @return void
1960      */
1961     public function prepare(renderer_base $output, moodle_page $page, $target) {
1962         parent::prepare($output, $page, $target);
1963     }
1965     /**
1966      * Shortcut method for creating a row with an array of cells. Converts cells to html_table_cell objects.
1967      * @param array $cells
1968      * @return html_table_row
1969      */
1970     public static function make($cells=array()) {
1971         $row = new html_table_row();
1972         foreach ($cells as $celltext) {
1973             if (!($celltext instanceof html_table_cell)) {
1974                 $cell = new html_table_cell();
1975                 $cell->text = $celltext;
1976                 $row->cells[] = $cell;
1977             } else {
1978                 $row->cells[] = $celltext;
1979             }
1980         }
1981         return $row;
1982     }
1986 /**
1987  * Component representing a table cell.
1988  *
1989  * @copyright 2009 Nicolas Connault
1990  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1991  * @since     Moodle 2.0
1992  */
1993 class html_table_cell extends html_component {
1994     /**
1995      * @var string $text The contents of the cell
1996      */
1997     public $text;
1998     /**
1999      * @var string $abbr Abbreviated version of the contents of the cell
2000      */
2001     public $abbr = null;
2002     /**
2003      * @var int $colspan Number of columns this cell should span
2004      */
2005     public $colspan = null;
2006     /**
2007      * @var int $rowspan Number of rows this cell should span
2008      */
2009     public $rowspan = null;
2010     /**
2011      * @var string $scope Defines a way to associate header cells and data cells in a table
2012      */
2013     public $scope = null;
2014     /**
2015      * @var boolean $header Whether or not this cell is a header cell
2016      */
2017     public $header = null;
2019     /**
2020      * @see lib/html_component#prepare()
2021      * @return void
2022      */
2023     public function prepare(renderer_base $output, moodle_page $page, $target) {
2024         if ($this->header && empty($this->scope)) {
2025             $this->scope = 'col';
2026         }
2027         parent::prepare($output, $page, $target);
2028     }
2032 /**
2033  * Component representing a XHTML link.
2034  *
2035  * @copyright 2009 Nicolas Connault
2036  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2037  * @since     Moodle 2.0
2038  */
2039 class html_link extends html_component {
2040     /**
2041      * URL can be simple text or a moodle_url object
2042      * @var mixed $url
2043      */
2044     public $url;
2046     /**
2047      * @var string $text The HTML text that will appear between the link tags
2048      */
2049     public $text = null;
2051     /**
2052      * @var boolean $disabled Whether or not this link is disabled (will be rendered as plain text)
2053      */
2054     public $disabled = false;
2056     /**
2057      * @var boolean $disableifcurrent Whether or not this link should be disabled if it the same as the current page
2058      */
2059     public $disableifcurrent = false;
2061     /**
2062      * New link constructor.
2063      *
2064      * @param moodle_url|string $url url of the image
2065      * @param array $options link attributes such as title, id, disabled, disableifcurrent, etc.
2066      */
2067     public function __construct($url = null, $text = null, array $options = null) {
2068         parent::__construct($options);
2070         if (is_null($url)) {
2071             // to be filled later
2073         } else if ($url instanceof moodle_url) {
2074             $this->url = clone($url);
2076         } else if (is_string($url)) {
2077             $this->url = new moodle_url($url);
2079         } else {
2080             throw new coding_style_exception('Image can be constructed only from moodle_url or string url.');
2081         }
2083         $this->text = $text;
2084     }
2086     /**
2087      * @see lib/html_component#prepare() Disables the link if it links to the current page.
2088      * @return void
2089      */
2090     public function prepare(renderer_base $output, moodle_page $page, $target) {
2091         // We can't accept an empty text value
2092         if ($this->text === '' or is_null($this->text)) { // 0 is valid value, do not use empty()
2093             throw new coding_exception('A html_link must have a descriptive text value!');
2094         }
2096         if (!($this->url instanceof moodle_url)) {
2097             $this->url = new moodle_url($this->url);
2098         }
2100         if ($this->disableifcurrent and $this->url->compare($page->url, URL_MATCH_PARAMS)) {
2101             $this->disabled = true;
2102         }
2104         parent::prepare($output, $page, $target);
2105     }
2107     /**
2108      * Shortcut for creating a link component.
2109      * @param mixed  $url String or moodle_url
2110      * @param string $text The text of the link
2111      * @return html_link The link component
2112      */
2113     public static function make($url, $text) {
2114         return new html_link($url, $text);
2115     }
2119 /**
2120  * Component representing a XHTML button (input of type 'button').
2121  * The renderer will either output it as a button with an onclick event,
2122  * or as a form with hidden inputs.
2123  *
2124  * @copyright 2009 Nicolas Connault
2125  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2126  * @since     Moodle 2.0
2127  */
2128 class html_button extends labelled_html_component {
2129     /**
2130      * @var string $text
2131      */
2132     public $text;
2134     /**
2135      * @var boolean $disabled Whether or not this button is disabled
2136      */
2137     public $disabled = false;
2139     /**
2140      * @see lib/html_component#prepare()
2141      * @return void
2142      */
2143     public function prepare(renderer_base $output, moodle_page $page, $target) {
2144         $this->add_class('singlebutton');
2146         if (empty($this->text)) {
2147             $this->text = get_string('submit');
2148         }
2150         if ($this->disabled) {
2151             $this->disabled = 'disabled';
2152         }
2154         parent::prepare($output, $page, $target);
2155     }
2158 /**
2159  * Component representing an image.
2160  *
2161  * @copyright 2009 Nicolas Connault
2162  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2163  * @since     Moodle 2.0
2164  */
2165 class html_image extends labelled_html_component {
2166     /**
2167      * @var string $src The path to the image being used
2168      */
2169     public $src;
2170     /**
2171      * @var int $width of image
2172      */
2173     public $width;
2174     /**
2175      * @var int $height of image
2176      */
2177     public $height;
2179     /**
2180      * New image constructor.
2181      *
2182      * @param moodle_url|string $url url of the image
2183      * @param array $options image attributes such as title, id, alt, widht, height
2184      */
2185     public function __construct($src = null, array $options = null) {
2186         parent::__construct($options);
2188         if (is_null($src)) {
2189             // to be filled later
2191         } else if ($src instanceof moodle_url) {
2192             $this->src = clone($src);
2194         } else if (is_string($src)) {
2195             $this->src = new moodle_url($src);
2197         } else {
2198             throw new coding_style_exception('Image can be constructed only from moodle_url or string url.');
2199         }
2200     }
2202     /**
2203      * @see lib/html_component#prepare()
2204      * @return void
2205      */
2206     public function prepare(renderer_base $output, moodle_page $page, $target) {
2207         if (empty($this->src)) {
2208             throw new coding_exception('html_image requires a $src value (moodle_url).');
2209         }
2211         // no general class here, use custom class instead or img element directly in css selectors
2212         parent::prepare($output, $page, $target);
2214         if ($this->alt === null) {
2215             // needs to be set for accessibility reasons
2216             $this->alt = '';
2217         }
2218     }
2222 /**
2223  * Component representing a textarea.
2224  *
2225  * @copyright 2009 Nicolas Connault
2226  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2227  * @since     Moodle 2.0
2228  */
2229 class html_textarea extends html_component {
2230     /**
2231      * @param string $name Name to use for the textarea element.
2232      */
2233     public $name;
2234     /**
2235      * @param string $value Initial content to display in the textarea.
2236      */
2237     public $value;
2238     /**
2239      * @param int $rows Number of rows to display  (minimum of 10 when $height is non-null)
2240      */
2241     public $rows;
2242     /**
2243      * @param int $cols Number of columns to display (minimum of 65 when $width is non-null)
2244      */
2245     public $cols;
2246     /**
2247      * @param bool $usehtmleditor Enables the use of the htmleditor for this field.
2248      */
2249     public $usehtmleditor;
2251     /**
2252      * @see lib/html_component#prepare()
2253      * @return void
2254      */
2255     public function prepare(renderer_base $output, moodle_page $page, $target) {
2256         $this->add_class('form-textarea');
2258         if (empty($this->id)) {
2259             $this->id = "edit-$this->name";
2260         }
2262         if ($this->usehtmleditor) {
2263             editors_head_setup();
2264             $editor = get_preferred_texteditor(FORMAT_HTML);
2265             $editor->use_editor($this->id, array('legacy'=>true));
2266             $this->value = htmlspecialchars($value);
2267         }
2269         parent::prepare($output, $page, $target);
2270     }
2274 /**
2275  * Component representing a simple form wrapper. Its purpose is mainly to enclose
2276  * a submit input with the appropriate action and hidden inputs.
2277  *
2278  * @copyright 2009 Nicolas Connault
2279  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2280  * @since     Moodle 2.0
2281  */
2282 class html_form extends html_component {
2283     /**
2284      * @var string $method post or get
2285      */
2286     public $method = 'post';
2287     /**
2288      * If a string is given, it will be converted to a moodle_url during prepare()
2289      * @var mixed $url A moodle_url including params or a string
2290      */
2291     public $url;
2292     /**
2293      * @var boolean $showbutton If true, the submit button will always be shown even if JavaScript is available
2294      */
2295     public $showbutton = false;
2296     /**
2297      * @var string $targetwindow The name of the target page to open the linked page in.
2298      */
2299     public $targetwindow = 'self';
2300     /**
2301      * @var html_button $button A submit button
2302      */
2303     public $button;
2304     /**
2305      * @var boolean $jssubmitaction If true, the submit button will be hidden when JS is enabled
2306      */
2307     public $jssubmitaction = false;
2308     /**
2309      * Constructor: sets up the other components in case they are needed
2310      * @return void
2311      */
2312     public function __construct(array $options = null) {
2313         parent::__construct($options);
2314         $this->button = new html_button();
2315         $this->button->text = get_string('go');
2316     }
2318     /**
2319      * @see lib/html_component#prepare()
2320      * @return void
2321      */
2322     public function prepare(renderer_base $output, moodle_page $page, $target) {
2324         if (empty($this->url)) {
2325             throw new coding_exception('A html_form must have a $url value (string or moodle_url).');
2326         }
2328         if (is_string($this->url)) {
2329             $this->url = new moodle_url($this->url);
2330         }
2332         if ($this->method == 'post') {
2333             // automatic CSRF protection
2334             $this->url->param('sesskey', sesskey());
2335         }
2337         parent::prepare($output, $page, $target);
2338     }
2342 /**
2343  * Component representing a list.
2344  *
2345  * The advantage of using this object instead of a flat array is that you can load it
2346  * with metadata (CSS classes, event handlers etc.) which can be used by the renderers.
2347  *
2348  * @copyright 2009 Nicolas Connault
2349  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2350  * @since     Moodle 2.0
2351  */
2352 class html_list extends html_component {
2354     /**
2355      * @var array $items An array of html_list_item or html_list objects
2356      */
2357     public $items = array();
2359     /**
2360      * @var string $type The type of list (ordered|unordered), definition type not yet supported
2361      */
2362     public $type = 'unordered';
2364     /**
2365      * @var string $text An optional descriptive text for the list. Will be output as a list item before opening the new list
2366      */
2367     public $text = false;
2369     /**
2370      * @see lib/html_component#prepare()
2371      * @return void
2372      */
2373     public function prepare(renderer_base $output, moodle_page $page, $target) {
2374         parent::prepare($output, $page, $target);
2375     }
2377     /**
2378      * This function takes a nested array of data and maps it into this list's $items array
2379      * as proper html_list_item and html_list objects, with appropriate metadata.
2380      *
2381      * @param array $tree A nested array (array keys are ignored);
2382      * @param int $row Used in identifying the iteration level and in ul classes
2383      * @return void
2384      */
2385     public function load_data($tree, $level=0) {
2387         $this->add_class("list-$level");
2389         $i = 1;
2390         foreach ($tree as $key => $element) {
2391             if (is_array($element)) {
2392                 $newhtmllist = new html_list();
2393                 $newhtmllist->type = $this->type;
2394                 $newhtmllist->load_data($element, $level + 1);
2395                 $newhtmllist->text = $key;
2396                 $this->items[] = $newhtmllist;
2397             } else {
2398                 $listitem = new html_list_item();
2399                 $listitem->value = $element;
2400                 $listitem->add_class("list-item-$level-$i");
2401                 $this->items[] = $listitem;
2402             }
2403             $i++;
2404         }
2405     }
2407     /**
2408      * Adds a html_list_item or html_list to this list.
2409      * If the param is a string, a html_list_item will be added.
2410      * @param mixed $item String, html_list or html_list_item object
2411      * @return void
2412      */
2413     public function add_item($item) {
2414         if ($item instanceof html_list_item || $item instanceof html_list) {
2415             $this->items[] = $item;
2416         } else {
2417             $listitem = new html_list_item();
2418             $listitem->value = $item;
2419             $this->items[] = $item;
2420         }
2421     }
2425 /**
2426  * Component representing a list item.
2427  *
2428  * @copyright 2009 Nicolas Connault
2429  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2430  * @since     Moodle 2.0
2431  */
2432 class html_list_item extends html_component {
2433     /**
2434      * @var string $value The value of the list item
2435      */
2436     public $value;
2438     /**
2439      * @see lib/html_component#prepare()
2440      * @return void
2441      */
2442     public function prepare(renderer_base $output, moodle_page $page, $target) {
2443         parent::prepare($output, $page, $target);
2444     }
2448 /**
2449  * Component representing a span element. It has no special attributes, so
2450  * it is very low-level and can be used for styling and JS actions.
2451  *
2452  * @copyright 2009 Nicolas Connault
2453  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2454  * @since     Moodle 2.0
2455  */
2456 class html_span extends html_component {
2457     /**
2458      * @var string $text The contents of the span
2459      */
2460     public $contents;
2461     /**
2462      * @see lib/html_component#prepare()
2463      * @return void
2464      */
2465     public function prepare(renderer_base $output, moodle_page $page, $target) {
2466         parent::prepare($output, $page, $target);
2467     }
2470 /// Complex components aggregating simpler components
2473 /**
2474  * Component representing a paging bar.
2475  *
2476  * @copyright 2009 Nicolas Connault
2477  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2478  * @since     Moodle 2.0
2479  */
2480 class moodle_paging_bar extends html_component {
2481     /**
2482      * @var int $maxdisplay The maximum number of pagelinks to display
2483      */
2484     public $maxdisplay = 18;
2485     /**
2486      * @var int $totalcount post or get
2487      */
2488     public $totalcount;
2489     /**
2490      * @var int $page The page you are currently viewing
2491      */
2492     public $page = 0;
2493     /**
2494      * @var int $perpage The number of entries that should be shown per page
2495      */
2496     public $perpage;
2497     /**
2498      * @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.
2499      *      If this is a moodle_url object then the pagevar param will be replaced by the page no, for each page.
2500      */
2501     public $baseurl;
2502     /**
2503      * @var string $pagevar This is the variable name that you use for the page number in your code (ie. 'tablepage', 'blogpage', etc)
2504      */
2505     public $pagevar = 'page';
2506     /**
2507      * @var html_link $previouslink A HTML link representing the "previous" page
2508      */
2509     public $previouslink = null;
2510     /**
2511      * @var html_link $nextlink A HTML link representing the "next" page
2512      */
2513     public $nextlink = null;
2514     /**
2515      * @var html_link $firstlink A HTML link representing the first page
2516      */
2517     public $firstlink = null;
2518     /**
2519      * @var html_link $lastlink A HTML link representing the last page
2520      */
2521     public $lastlink = null;
2522     /**
2523      * @var array $pagelinks An array of html_links. One of them is just a string: the current page
2524      */
2525     public $pagelinks = array();
2527     /**
2528      * @see lib/html_component#prepare()
2529      * @return void
2530      */
2531     public function prepare(renderer_base $output, moodle_page $page, $target) {
2532         if (!isset($this->totalcount) || is_null($this->totalcount)) {
2533             throw new coding_exception('moodle_paging_bar requires a totalcount value.');
2534         }
2535         if (!isset($this->page) || is_null($this->page)) {
2536             throw new coding_exception('moodle_paging_bar requires a page value.');
2537         }
2538         if (empty($this->perpage)) {
2539             throw new coding_exception('moodle_paging_bar requires a perpage value.');
2540         }
2541         if (empty($this->baseurl)) {
2542             throw new coding_exception('moodle_paging_bar requires a baseurl value.');
2543         }
2544         if (!($this->baseurl instanceof moodle_url)) {
2545             $this->baseurl = new moodle_url($this->baseurl);
2546         }
2548         if ($this->totalcount > $this->perpage) {
2549             $pagenum = $this->page - 1;
2551             if ($this->page > 0) {
2552                 $this->previouslink = new html_link();
2553                 $this->previouslink->add_class('previous');
2554                 $this->previouslink->url = clone($this->baseurl);
2555                 $this->previouslink->url->param($this->pagevar, $pagenum);
2556                 $this->previouslink->text = get_string('previous');
2557             }
2559             if ($this->perpage > 0) {
2560                 $lastpage = ceil($this->totalcount / $this->perpage);
2561             } else {
2562                 $lastpage = 1;
2563             }
2565             if ($this->page > 15) {
2566                 $startpage = $this->page - 10;
2568                 $this->firstlink = new html_link();
2569                 $this->firstlink->url = clone($this->baseurl);
2570                 $this->firstlink->url->param($this->pagevar, 0);
2571                 $this->firstlink->text = 1;
2572                 $this->firstlink->add_class('first');
2573             } else {
2574                 $startpage = 0;
2575             }
2577             $currpage = $startpage;
2578             $displaycount = $displaypage = 0;
2580             while ($displaycount < $this->maxdisplay and $currpage < $lastpage) {
2581                 $displaypage = $currpage + 1;
2583                 if ($this->page == $currpage) {
2584                     $this->pagelinks[] = $displaypage;
2585                 } else {
2586                     $pagelink = new html_link();
2587                     $pagelink->url = clone($this->baseurl);
2588                     $pagelink->url->param($this->pagevar, $currpage);
2589                     $pagelink->text = $displaypage;
2590                     $this->pagelinks[] = $pagelink;
2591                 }
2593                 $displaycount++;
2594                 $currpage++;
2595             }
2597             if ($currpage < $lastpage) {
2598                 $lastpageactual = $lastpage - 1;
2599                 $this->lastlink = new html_link();
2600                 $this->lastlink->url = clone($this->baseurl);
2601                 $this->lastlink->url->param($this->pagevar, $lastpageactual);
2602                 $this->lastlink->text = $lastpage;
2603                 $this->lastlink->add_class('last');
2604             }
2606             $pagenum = $this->page + 1;
2608             if ($pagenum != $displaypage) {
2609                 $this->nextlink = new html_link();
2610                 $this->nextlink->url = clone($this->baseurl);
2611                 $this->nextlink->url->param($this->pagevar, $pagenum);
2612                 $this->nextlink->text = get_string('next');
2613                 $this->nextlink->add_class('next');
2614             }
2615         }
2616     }
2618     /**
2619      * Shortcut for initialising a moodle_paging_bar with only the required params.
2620      *
2621      * @param int $totalcount Thetotal number of entries available to be paged through
2622      * @param int $page The page you are currently viewing
2623      * @param int $perpage The number of entries that should be shown per page
2624      * @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.
2625      *                          If this is a moodle_url object then the pagevar param will be replaced by the page no, for each page.
2626      */
2627     public static function make($totalcount, $page, $perpage, $baseurl) {
2628         $pagingbar = new moodle_paging_bar();
2629         $pagingbar->totalcount = $totalcount;
2630         $pagingbar->page = $page;
2631         $pagingbar->perpage = $perpage;
2632         $pagingbar->baseurl = $baseurl;
2633         return $pagingbar;
2634     }
2638 /**
2639  * This class represents how a block appears on a page.
2640  *
2641  * During output, each block instance is asked to return a block_contents object,
2642  * those are then passed to the $OUTPUT->block function for display.
2643  *
2644  * {@link $contents} should probably be generated using a moodle_block_..._renderer.
2645  *
2646  * Other block-like things that need to appear on the page, for example the
2647  * add new block UI, are also represented as block_contents objects.
2648  *
2649  * @copyright 2009 Tim Hunt
2650  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2651  * @since     Moodle 2.0
2652  */
2653 class block_contents extends html_component {
2654     /** @var int used to set $skipid. */
2655     protected static $idcounter = 1;
2657     const NOT_HIDEABLE = 0;
2658     const VISIBLE = 1;
2659     const HIDDEN = 2;
2661     /**
2662      * @param integer $skipid All the blocks (or things that look like blocks)
2663      * printed on a page are given a unique number that can be used to construct
2664      * id="" attributes. This is set automatically be the {@link prepare()} method.
2665      * Do not try to set it manually.
2666      */
2667     public $skipid;
2669     /**
2670      * @var integer If this is the contents of a real block, this should be set to
2671      * the block_instance.id. Otherwise this should be set to 0.
2672      */
2673     public $blockinstanceid = 0;
2675     /**
2676      * @var integer if this is a real block instance, and there is a corresponding
2677      * block_position.id for the block on this page, this should be set to that id.
2678      * Otherwise it should be 0.
2679      */
2680     public $blockpositionid = 0;
2682     /**
2683      * @param array $attributes an array of attribute => value pairs that are put on the
2684      * outer div of this block. {@link $id} and {@link $classes} attributes should be set separately.
2685      */
2686     public $attributes = array();
2688     /**
2689      * @param string $title The title of this block. If this came from user input,
2690      * it should already have had format_string() processing done on it. This will
2691      * be output inside <h2> tags. Please do not cause invalid XHTML.
2692      */
2693     public $title = '';
2695     /**
2696      * @param string $content HTML for the content
2697      */
2698     public $content = '';
2700     /**
2701      * @param array $list an alternative to $content, it you want a list of things with optional icons.
2702      */
2703     public $footer = '';
2705     /**
2706      * Any small print that should appear under the block to explain to the
2707      * teacher about the block, for example 'This is a sticky block that was
2708      * added in the system context.'
2709      * @var string
2710      */
2711     public $annotation = '';
2713     /**
2714      * @var integer one of the constants NOT_HIDEABLE, VISIBLE, HIDDEN. Whether
2715      * the user can toggle whether this block is visible.
2716      */
2717     public $collapsible = self::NOT_HIDEABLE;
2719     /**
2720      * A (possibly empty) array of editing controls. Each element of this array
2721      * should be an array('url' => $url, 'icon' => $icon, 'caption' => $caption).
2722      * $icon is the icon name. Fed to $OUTPUT->pix_url.
2723      * @var array
2724      */
2725     public $controls = array();
2727     /**
2728      * @see html_component::prepare()
2729      * @return void
2730      */
2731     public function prepare(renderer_base $output, moodle_page $page, $target) {
2732         $this->skipid = self::$idcounter;
2733         self::$idcounter += 1;
2734         $this->add_class('sideblock');
2735         if (empty($this->blockinstanceid) || !strip_tags($this->title)) {
2736             $this->collapsible = self::NOT_HIDEABLE;
2737         }
2738         if ($this->collapsible == self::HIDDEN) {
2739             $this->add_class('hidden');
2740         }
2741         if (!empty($this->controls)) {
2742             $this->add_class('block_with_controls');
2743         }
2744         parent::prepare($output, $page, $target);
2745     }
2749 /**
2750  * This class represents a target for where a block can go when it is being moved.
2751  *
2752  * This needs to be rendered as a form with the given hidden from fields, and
2753  * clicking anywhere in the form should submit it. The form action should be
2754  * $PAGE->url.
2755  *
2756  * @copyright 2009 Tim Hunt
2757  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2758  * @since     Moodle 2.0
2759  */
2760 class block_move_target extends html_component {
2761     /**
2762      * List of hidden form fields.
2763      * @var array
2764      */
2765     public $url = array();
2766     /**
2767      * List of hidden form fields.
2768      * @var array
2769      */
2770     public $text = '';