MDL-20204 converting yes_no selects
[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 yes/no form field
694      * @param string $name name of select element
695      * @param bool $selected
696      * @param array $attributes - html select element attributes
697      * @return string HRML fragment
698      */
699     public function select_yes_no($name, $selected=true, array $attributes = null) {
700         $options = array('1'=>get_string('yes'), '0'=>get_string('no'));
701         return self::select($options, $name, $selected, null, $attributes);
702     }
704     /**
705      * Generates a simple select form field
706      * @param array $options associative array value=>label ex.:
707      *                array(1=>'One, 2=>Two)
708      *              it is also possible to specify optgroup as complex label array ex.:
709      *                array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
710      *                array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
711      * @param string $name name of select element
712      * @param string|array $selected value or arary of values depending on multiple attribute
713      * @param array|bool $nothing, add nothing selected option, or false of not added
714      * @param array $attributes - html select element attributes
715      * @return string HTML fragment
716      */
717     public static function select(array $options, $name, $selected = '', $nothing = array(''=>'choosedots'), array $attributes = null) {
718         $attributes = (array)$attributes;
719         if (is_array($nothing)) {
720             foreach ($nothing as $k=>$v) {
721                 if ($v === 'choose' or $v === 'choosedots') {
722                     $nothing[$k] = get_string('choosedots');
723                 }
724             }
725             $options = $nothing + $options; // keep keys, do not override
727         } else if (is_string($nothing) and $nothing !== '') {
728             // BC
729             $options = array(''=>$nothing) + $options;
730         }
732         // we may accept more values if multiple attribute specified
733         $selected = (array)$selected;
734         foreach ($selected as $k=>$v) {
735             $selected[$k] = (string)$v;
736         }
738         if (!isset($attributes['id'])) {
739             $id = 'menu'.$name;
740             // name may contaion [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading
741             $id = str_replace('[', '', $id);
742             $id = str_replace(']', '', $id);
743             $attributes['id'] = $id;
744         }
746         if (!isset($attributes['class'])) {
747             $class = 'menu'.$name;
748             // name may contaion [], which would make an invalid class. e.g. numeric question type editing form, assignment quickgrading
749             $class = str_replace('[', '', $class);
750             $class = str_replace(']', '', $class);
751             $attributes['class'] = $class;
752         }
753         $attributes['class'] = 'select ' . $attributes['class']; /// Add 'select' selector always
755         $attributes['name'] = $name;
757         $output = '';
758         foreach ($options as $value=>$label) {
759             if (is_array($label)) {
760                 // ignore key, it just has to be unique
761                 $output .= self::select_optgroup(key($label), current($label), $selected);
762             } else {
763                 $output .= self::select_option($label, $value, $selected);
764             }
765         }
766         return self::tag('select', $attributes, $output);
767     }
769     private static function select_option($label, $value, array $selected) {
770         $attributes = array();
771         $value = (string)$value;
772         if (in_array($value, $selected, true)) {
773             $attributes['selected'] = 'selected';
774         }
775         $attributes['value'] = $value;
776         return self::tag('option', $attributes, $label);
777     }
779     private static function select_optgroup($groupname, $options, array $selected) {
780         if (empty($options)) {
781             return '';
782         }
783         $attributes = array('label'=>$groupname);
784         $output = '';
785         foreach ($options as $value=>$label) {
786             $output .= self::select_option($label, $value, $selected);
787         }
788         return self::tag('optgroup', $attributes, $output);
789     }
791     /**
792      * Returns hidden input fields created from url parameters.
793      * @param moodle_url $url
794      * @param array $exclude list of excluded parameters
795      * @return string HTML fragment
796      */
797     public static function input_hidden_params(moodle_url $url, array $exclude = null) {
798         $exclude = (array)$exclude;
799         $params = $url->params();
800         foreach ($exclude as $key) {
801             unset($params[$key]);
802         }
804         $output = '';
805         foreach ($params as $key => $value) {
806             $attributes = array('type'=>'hidden', 'name'=>$key, 'value'=>$value);
807             $output .= self::empty_tag('input', $attributes)."\n";
808         }
809         return $output;
810     }
812     /**
813      * Generate a script tag containing the the specified code.
814      *
815      * @param string $js the JavaScript code
816          * @param moodle_url|string optional url of the external script, $code ignored if specified
817      * @return string HTML, the code wrapped in <script> tags.
818      */
819     public static function script($jscode, $url=null) {
820         if ($jscode) {
821             $attributes = array('type'=>'text/javascript');
822             return self::tag('script', $attributes, "\n//<![CDATA[\n$jscode\n//]]>\n") . "\n";
824         } else if ($url) {
825             $attributes = array('type'=>'text/javascript', 'src'=>$url);
826             return self::tag('script', $attributes, '') . "\n";
828         } else {
829             return '';
830         }
831     }
834 // ==== JS writer and helper classes, will be probably moved elsewhere ======
836 /**
837  * Simple javascript output class
838  * @copyright 2010 Petr Skoda
839  */
840 class js_writer {
841     /**
842      * Returns javascript code calling the function
843      * @param string $function function name, can be complex lin Y.Event.purgeElement
844      * @param array $arguments parameters
845      * @param int $delay execution delay in seconds
846      * @return string JS code fragment
847      */
848     public function function_call($function, array $arguments = null, $delay=0) {
849         if ($arguments) {
850             $arguments = array_map('json_encode', $arguments);
851             $arguments = implode(', ', $arguments);
852         } else {
853             $arguments = '';
854         }
855         $js = "$function($arguments);";
857         if ($delay) {
858             $delay = $delay * 1000; // in miliseconds
859             $js = "setTimeout(function() { $js }, $delay);";
860         }
861         return $js . "\n";
862     }
864     /**
865      * Special function which adds Y as first argument of fucntion call.
866      * @param string $function
867      * @param array $extraarguments
868      * @return string
869      */
870     public function function_call_with_Y($function, array $extraarguments = null) {
871         if ($extraarguments) {
872             $extraarguments = array_map('json_encode', $extraarguments);
873             $arguments = 'Y, ' . implode(', ', $extraarguments);
874         } else {
875             $arguments = 'Y';
876         }
877         return "$function($arguments);\n";
878     }
880     /**
881      * Returns JavaScript code to initialise a new object
882      * @param string|null $var If it is null then no var is assigned the new object
883      * @param string $class
884      * @param array $arguments
885      * @param array $requirements
886      * @param int $delay
887      * @return string
888      */
889     public function object_init($var, $class, array $arguments = null, array $requirements = null, $delay=0) {
890         if (is_array($arguments)) {
891             $arguments = array_map('json_encode', $arguments);
892             $arguments = implode(', ', $arguments);
893         }
895         if ($var === null) {
896             $js = "new $class(Y, $arguments);";
897         } else if (strpos($var, '.')!==false) {
898             $js = "$var = new $class(Y, $arguments);";
899         } else {
900             $js = "var $var = new $class(Y, $arguments);";
901         }
903         if ($delay) {
904             $delay = $delay * 1000; // in miliseconds
905             $js = "setTimeout(function() { $js }, $delay);";
906         }
908         if (count($requirements) > 0) {
909             $requirements = implode("', '", $requirements);
910             $js = "Y.use('$requirements', function(Y){ $js });";
911         }
912         return $js."\n";
913     }
915     /**
916      * Returns code setting value to variable
917      * @param string $name
918      * @param mixed $value json serialised value
919      * @param bool $usevar add var definition, ignored for nested properties
920      * @return string JS code fragment
921      */
922     public function set_variable($name, $value, $usevar=true) {
923         $output = '';
925         if ($usevar) {
926             if (strpos($name, '.')) {
927                 $output .= '';
928             } else {
929                 $output .= 'var ';
930             }
931         }
933         $output .= "$name = ".json_encode($value).";";
935         return $output;
936     }
938     /**
939      * Writes event handler attaching code
940      * @param mixed $selector standard YUI selector for elemnts, may be array or string, element id is in the form "#idvalue"
941      * @param string $event A valid DOM event (click, mousedown, change etc.)
942      * @param string $function The name of the function to call
943      * @param array  $arguments An optional array of argument parameters to pass to the function
944      * @return string JS code fragment
945      */
946     public function event_handler($selector, $event, $function, array $arguments = null) {
947         $selector = json_encode($selector);
948         $output = "Y.on('$event', $function, $selector, null";
949         if (!empty($arguments)) {
950             $output .= ', ' . json_encode($arguments);
951         }
952         return $output . ");\n";
953     }
957 // ===============================================================================================
958 // TODO: Following components will be refactored soon
960 /**
961  * Base class for classes representing HTML elements, like html_select.
962  *
963  * Handles the id and class attributes.
964  *
965  * @copyright 2009 Tim Hunt
966  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
967  * @since     Moodle 2.0
968  */
969 class html_component {
970     /**
971      * @var string value to use for the id attribute of this HTML tag.
972      */
973     public $id = null;
974     /**
975      * @var string $alt value to use for the alt attribute of this HTML tag.
976      */
977     public $alt = null;
978     /**
979      * @var string $style value to use for the style attribute of this HTML tag.
980      */
981     public $style = null;
982     /**
983      * @var array class names to add to this HTML element.
984      */
985     public $classes = array();
986     /**
987      * @var string $title The title attributes applicable to any XHTML element
988      */
989     public $title = null;
990     /**
991      * An optional array of component_action objects handling the action part of this component.
992      * @var array $actions
993      */
994     protected $actions = array();
996     /**
997      * Compoment constructor.
998      * @param array $options image attributes such as title, id, alt, style, class
999      */
1000     public function __construct(array $options = null) {
1001         // not implemented in this class because we want to set only public properties of this component
1002         renderer_base::apply_component_options($this, $options);
1003     }
1005     /**
1006      * Ensure some class names are an array.
1007      * @param mixed $classes either an array of class names or a space-separated
1008      *      string containing class names.
1009      * @return array the class names as an array.
1010      */
1011     public static function clean_classes($classes) {
1012         if (empty($classes)) {
1013             return array();
1014         } else if (is_array($classes)) {
1015             return $classes;
1016         } else {
1017             return explode(' ', trim($classes));
1018         }
1019     }
1021     /**
1022      * Set the class name array.
1023      * @param mixed $classes either an array of class names or a space-separated
1024      *      string containing class names.
1025      * @return void
1026      */
1027     public function set_classes($classes) {
1028         $this->classes = self::clean_classes($classes);
1029     }
1031     /**
1032      * Add a class name to the class names array.
1033      * @param string $class the new class name to add.
1034      * @return void
1035      */
1036     public function add_class($class) {
1037         $this->classes[] = $class;
1038     }
1040     /**
1041      * Add a whole lot of class names to the class names array.
1042      * @param mixed $classes either an array of class names or a space-separated
1043      *      string containing class names.
1044      * @return void
1045      */
1046     public function add_classes($classes) {
1047         $this->classes = array_merge($this->classes, self::clean_classes($classes));
1048     }
1050     /**
1051      * Get the class names as a string.
1052      * @return string the class names as a space-separated string. Ready to be put in the class="" attribute.
1053      */
1054     public function get_classes_string() {
1055         return implode(' ', $this->classes);
1056     }
1058     /**
1059      * Perform any cleanup or final processing that should be done before an
1060      * instance of this class is output. This method is supposed to be called
1061      * only from renderers.
1062      *
1063      * @param renderer_base $output output renderer
1064      * @param moodle_page $page
1065      * @param string $target rendering target
1066      * @return void
1067      */
1068     public function prepare(renderer_base $output, moodle_page $page, $target) {
1069         $this->classes = array_unique(self::clean_classes($this->classes));
1070     }
1072     /**
1073      * This checks developer do not try to assign a property directly
1074      * if we have a setter for it. Otherwise, the property is set as expected.
1075      * @param string $name The name of the variable to set
1076      * @param mixed $value The value to assign to the variable
1077      * @return void
1078      */
1079     public function __set($name, $value) {
1080         if ($name == 'class') {
1081             debugging('this way of setting css class has been deprecated. use set_classes() method instead.');
1082             $this->set_classes($value);
1083         } else {
1084             $this->{$name} = $value;
1085         }
1086     }
1088     /**
1089      * Adds a JS action to this component.
1090      * Note: the JS function you write must have only two arguments: (string)event and (object|array)args
1091      * If you want to add an instantiated component_action (or one of its subclasses), give the object as the only parameter
1092      *
1093      * @param mixed  $event a DOM event (click, mouseover etc.) or a component_action object
1094      * @param string $jsfunction The name of the JS function to call. required if argument 1 is a string (event)
1095      * @param array  $jsfunctionargs An optional array of JS arguments to pass to the function
1096      */
1097     public function add_action($event, $jsfunction=null, $jsfunctionargs=array()) {
1098         if (empty($this->id)) {
1099             $this->generate_id();
1100         }
1102         if ($event instanceof component_action) {
1103             $this->actions[] = $event;
1104         } else {
1105             if (empty($jsfunction)) {
1106                 throw new coding_exception('html_component::add_action requires a JS function argument if the first argument is a string event');
1107             }
1108             $this->actions[] = new component_action($event, $jsfunction, $jsfunctionargs);
1109         }
1110     }
1112     /**
1113      * Internal method for generating a unique ID for the purpose of event handlers.
1114      */
1115     protected function generate_id() {
1116         $this->id = uniqid(get_class($this));
1117     }
1119     /**
1120      * Returns the array of component_actions.
1121      * @return array Component actions
1122      */
1123     public function get_actions() {
1124         return $this->actions;
1125     }
1127     /**
1128      * Shortcut for adding a JS confirm dialog when the component is clicked.
1129      * The message must be a yes/no question.
1130      * @param string $message The yes/no confirmation question. If "Yes" is clicked, the original action will occur.
1131      * @param string $callback The name of a JS function whose scope will be set to the simpleDialog object and have this
1132      *    function's arguments set as this.args.
1133      * @return void
1134      */
1135     public function add_confirm_action($message, $callback=null) {
1136         $this->add_action(new component_action('click', 'M.util.show_confirm_dialog', array('message' => $message, 'callback' => $callback)));
1137     }
1139     /**
1140      * Returns true if this component has an action of the requested type (component_action by default).
1141      * @param string $class The class of the action we are looking for
1142      * @return boolean True if action is found
1143      */
1144     public function has_action($class='component_action') {
1145         foreach ($this->actions as $action) {
1146             if (get_class($action) == $class) {
1147                 return true;
1148             }
1149         }
1150         return false;
1151     }
1155 class labelled_html_component extends html_component {
1156     /**
1157      * @var mixed $label The label for that component. String or html_label object
1158      */
1159     public $label;
1161     /**
1162      * Compoment constructor.
1163      * @param array $options image attributes such as title, id, alt, style, class
1164      */
1165     public function __construct(array $options = null) {
1166         parent::__construct($options);
1167     }
1169     /**
1170      * Adds a descriptive label to the component.
1171      *
1172      * This can be used in two ways:
1173      *
1174      * <pre>
1175      * $component->set_label($elementlabel, $elementid);
1176      * // OR
1177      * $label = new html_label();
1178      * $label->for = $elementid;
1179      * $label->text = $elementlabel;
1180      * $component->set_label($label);
1181      * </pre>
1182      *
1183      * Use the second form when you need to add additional HTML attributes
1184      * to the label and/or JS actions.
1185      *
1186      * @param mixed $text Either the text of the label or a html_label object
1187      * @param text  $for The value of the "for" attribute (the associated element's id)
1188      * @return void
1189      */
1190     public function set_label($text, $for=null) {
1191         if ($text instanceof html_label) {
1192             $this->label = $text;
1193         } else if (!empty($text)) {
1194             $this->label = new html_label();
1195             $this->label->for = $for;
1196             if (empty($for)) {
1197                 if (empty($this->id)) {
1198                     $this->generate_id();
1199                 }
1200                 $this->label->for = $this->id;
1201             }
1202             $this->label->text = $text;
1203         }
1204     }
1207 /// Components representing HTML elements
1209 /**
1210  * This class represents a label element
1211  *
1212  * @copyright 2009 Nicolas Connault
1213  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1214  * @since     Moodle 2.0
1215  */
1216 class html_label extends html_component {
1217     /**
1218      * @var string $text The text to display in the label
1219      */
1220     public $text;
1221     /**
1222      * @var string $for The name of the form field this label is associated with
1223      */
1224     public $for;
1226     /**
1227      * @see html_component::prepare()
1228      * @return void
1229      */
1230     public function prepare(renderer_base $output, moodle_page $page, $target) {
1231         if (empty($this->text)) {
1232             throw new coding_exception('html_label must have a $text value.');
1233         }
1234         parent::prepare($output, $page, $target);
1235     }
1239 /**
1240  * This class hold all the information required to describe a <select> menu that
1241  * will be printed by {@link core_renderer::select()}. (Or by an overridden
1242  * version of that method in a subclass.)
1243  *
1244  * This component can also hold enough metadata to be used as a popup form. It just
1245  * needs a bit more setting up than for a simple menu. See the shortcut methods for
1246  * developer-friendly usage.
1247  *
1248  * All the fields that are not set by the constructor have sensible defaults, so
1249  * you only need to set the properties where you want non-default behaviour.
1250  *
1251  * @copyright 2009 Tim Hunt
1252  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1253  * @since     Moodle 2.0
1254  */
1255 class html_select extends labelled_html_component {
1256     /**
1257      * The html_select object parses an array of options into component objects
1258      * @see nested attribute
1259      * @var mixed $options the choices to show in the menu. An array $value => $display, of html_select_option or of html_select_optgroup objects.
1260      */
1261     public $options;
1262     /**
1263      * @var string $name the name of this form control. That is, the name of the GET/POST
1264      * variable that will be set if this select is submitted as part of a form.
1265      */
1266     public $name;
1267     /**
1268      * @var string $selectedvalue the option to select initially. Should match one
1269      * of the $options array keys. Default none.
1270      */
1271     public $selectedvalue;
1272     /**
1273      * Defaults to get_string('choosedots').
1274      * Set this to '' if you do not want a 'nothing is selected' option.
1275      * This is ignored if the rendertype is 'radio' or 'checkbox'
1276      * @var string The label for the 'nothing is selected' option.
1277      */
1278     public $nothinglabel = null;
1279     /**
1280      * @var string The value returned by the 'nothing is selected' option. Defaults to 0.
1281      */
1282     public $nothingvalue = 0;
1283     /**
1284      * @var boolean set this to true if you want the control to appear disabled.
1285      */
1286     public $disabled = false;
1287     /**
1288      * @var integer if non-zero, sets the tabindex attribute on the <select> element. Default 0.
1289      */
1290     public $tabindex = 0;
1291     /**
1292      * @var mixed Defaults to false, which means display the select as a dropdown menu.
1293      * If true, display this select as a list box whose size is chosen automatically.
1294      * If an integer, display as list box of that size.
1295      */
1296     public $listbox = false;
1297     /**
1298      * @var integer if you are using $listbox === true to get an automatically
1299      * sized list box, the size of the list box will be the number of options,
1300      * or this number, whichever is smaller.
1301      */
1302     public $maxautosize = 10;
1303     /**
1304      * @var boolean if true, allow multiple selection. Only used if $listbox is true, or if
1305      *      the select is to be output as checkboxes.
1306      */
1307     public $multiple = false;
1308     /**
1309      * Another way to use nested menu is to prefix optgroup labels with -- and end the optgroup with --
1310      * Leave this setting to false if you are using the latter method.
1311      * @var boolean $nested if true, uses $options' keys as option headings (optgroup)
1312      */
1313     public $nested = false;
1314     /**
1315      * @var html_form $form An optional html_form component
1316      */
1317     public $form;
1318     /**
1319      * @var help_icon $array help icon params
1320      */
1321     public $helpicon;
1322     /**
1323      * @var boolean $rendertype How the select element should be rendered: menu or radio (checkbox is just radio + multiple)
1324      */
1325     public $rendertype = 'menu';
1327     /**
1328      * @see html_component::prepare()
1329      * @return void
1330      */
1331     public function prepare(renderer_base $output, moodle_page $page, $target) {
1332         global $CFG;
1334         // name may contain [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading
1335         if (empty($this->id)) {
1336             $this->id = 'menu' . str_replace(array('[', ']'), '', $this->name);
1337         }
1339         if (empty($this->classes)) {
1340             $this->set_classes(array('menu' . str_replace(array('[', ']'), '', $this->name)));
1341         }
1343         if (is_null($this->nothinglabel)) {
1344             $this->nothinglabel = get_string('choosedots');
1345         }
1347         if (!empty($this->label) && !($this->label instanceof html_label)) {
1348             $label = new html_label();
1349             $label->text = $this->label;
1350             $label->for = $this->name;
1351             $this->label = $label;
1352         }
1354         $this->add_class('select');
1356         $this->initialise_options();
1357         parent::prepare($output, $page, $target);
1358     }
1360     /**
1361      * This is a shortcut for making a simple select menu. It lets you specify
1362      * the options, name and selected option in one line of code.
1363      * @param array $options used to initialise {@link $options}.
1364      * @param string $name used to initialise {@link $name}.
1365      * @param string $selected  used to initialise {@link $selected}.
1366      * @param string $nothinglabel The label for the 'nothing is selected' option. Defaults to "Choose..."
1367      * @return html_select A html_select object with the three common fields initialised.
1368      */
1369     public static function make($options, $name, $selected = '', $nothinglabel='choosedots') {
1370         $menu = new html_select();
1371         $menu->options = $options;
1372         $menu->name = $name;
1373         $menu->selectedvalue = $selected;
1374         return $menu;
1375     }
1377     /**
1378      * This is a shortcut for making an hour selector menu.
1379      * @param string $type The type of selector (years, months, days, hours, minutes)
1380      * @param string $name fieldname
1381      * @param int $currenttime A default timestamp in GMT
1382      * @param int $step minute spacing
1383      * @return html_select A menu initialised with hour options.
1384      */
1385     public static function make_time_selector($type, $name, $currenttime=0, $step=5) {
1387         if (!$currenttime) {
1388             $currenttime = time();
1389         }
1390         $currentdate = usergetdate($currenttime);
1391         $userdatetype = $type;
1393         switch ($type) {
1394             case 'years':
1395                 for ($i=1970; $i<=2020; $i++) {
1396                     $timeunits[$i] = $i;
1397                 }
1398                 $userdatetype = 'year';
1399                 break;
1400             case 'months':
1401                 for ($i=1; $i<=12; $i++) {
1402                     $timeunits[$i] = userdate(gmmktime(12,0,0,$i,15,2000), "%B");
1403                 }
1404                 $userdatetype = 'month';
1405                 $currentdate['month'] = $currentdate['mon'];
1406                 break;
1407             case 'days':
1408                 for ($i=1; $i<=31; $i++) {
1409                     $timeunits[$i] = $i;
1410                 }
1411                 $userdatetype = 'mday';
1412                 break;
1413             case 'hours':
1414                 for ($i=0; $i<=23; $i++) {
1415                     $timeunits[$i] = sprintf("%02d",$i);
1416                 }
1417                 break;
1418             case 'minutes':
1419                 if ($step != 1) {
1420                     $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
1421                 }
1423                 for ($i=0; $i<=59; $i+=$step) {
1424                     $timeunits[$i] = sprintf("%02d",$i);
1425                 }
1426                 break;
1427             default:
1428                 throw new coding_exception("Time type $type is not supported by html_select::make_time_selector().");
1429         }
1431         $timerselector = self::make($timeunits, $name, $currentdate[$userdatetype]);
1432         $timerselector->label = new html_label();
1434         $timerselector->label->text = get_string(substr($type, 0, -1), 'form');
1435         $timerselector->label->for = "menu$timerselector->name";
1436         $timerselector->label->add_class('accesshide');
1437         $timerselector->nothinglabel = '';
1439         return $timerselector;
1440     }
1442     /**
1443      * Given an associative array of type => fieldname and an optional timestamp,
1444      * returns an array of html_select components representing date/time selectors.
1445      * @param array $selectors Arrays of type => fieldname. Selectors will be returned in the order of the types given
1446      * @param int $currenttime A UNIX timestamp
1447      * @param int $step minute spacing
1448      * @return array Instantiated date/time selectors
1449      */
1450     public static function make_time_selectors($selectors, $currenttime=0, $step=5) {
1451         $selects = array();
1452         foreach ($selectors as $type => $name) {
1453             $selects[] = html_select::make_time_selector($type, $name, $currenttime, $step);
1454         }
1455         return $selects;
1456     }
1458     /**
1459      * Adds a help icon next to the select menu.
1460      *
1461      * <pre>
1462      * $select->set_help_icon($page, $text, $component);
1463      * </pre>
1464      *
1465      * @param string $helppage Either the keyword that defines a help page or a help_icon object
1466      * @param text  $text The text of the help icon
1467      * @param component $component
1468      * @param boolean $linktext Whether or not to show text next to the icon
1469      * @return void
1470      */
1471     public function set_help_icon($helppage='', $text='', $component='moodle') {
1472         if ($helppage) {
1473             $this->helpicon = array('helppage'=>$helppage, 'text'=>$text, 'component'=>$component);
1474         } else {
1475             $this->helpicon = null;
1476         }
1477     }
1479     /**
1480      * Parses the $options array and instantiates html_select_option objects in
1481      * the place of the original value => label pairs. This is useful for when you
1482      * need to setup extra html attributes and actions on individual options before
1483      * the component is sent to the renderer
1484      * @return void;
1485      */
1486     public function initialise_options() {
1487         // If options are already instantiated objects, stop here
1488         $firstoption = reset($this->options);
1489         if ($firstoption instanceof html_select_option || $firstoption instanceof html_select_optgroup) {
1490             return;
1491         }
1493         if ($this->rendertype == 'radio' && $this->multiple) {
1494             $this->rendertype = 'checkbox';
1495         }
1497         // If nested is on, or if radio/checkbox rendertype is set, remove the default Choose option
1498         if ($this->nested || $this->rendertype == 'radio' || $this->rendertype == 'checkbox') {
1499             $this->nothinglabel = '';
1500         }
1502         $options = $this->options;
1504         $this->options = array();
1506         if ($this->nested && $this->rendertype != 'menu') {
1507             throw new coding_exception('html_select cannot render nested options as radio buttons or checkboxes.');
1508         } else if ($this->nested) {
1509             foreach ($options as $section => $values) {
1510                 $optgroup = new html_select_optgroup();
1511                 $optgroup->text = $section;
1513                 foreach ($values as $value => $display) {
1514                     $option = new html_select_option();
1515                     $option->value = s($value);
1516                     $option->text = $display;
1517                     if ($display === '') {
1518                         $option->text = $value;
1519                     }
1521                     if ((string) $value == (string) $this->selectedvalue ||
1522                             (is_array($this->selectedvalue) && in_array($value, $this->selectedvalue))) {
1523                         $option->selected = 'selected';
1524                     }
1526                     $optgroup->options[] = $option;
1527                 }
1529                 $this->options[] = $optgroup;
1530             }
1531         } else {
1532             $inoptgroup = false;
1533             $optgroup = false;
1535             foreach ($options as $value => $display) {
1536                 if ($display == '--') { /// we are ending previous optgroup
1537                     // $this->options[] = $optgroup;
1538                     $inoptgroup = false;
1539                     continue;
1540                 } else if (substr($display,0,2) == '--') { /// we are starting a new optgroup
1541                     if (!empty($optgroup->options)) {
1542                         $this->options[] = $optgroup;
1543                     }
1545                     $optgroup = new html_select_optgroup();
1546                     $optgroup->text = substr($display,2); // stripping the --
1548                     $inoptgroup = true; /// everything following will be in an optgroup
1549                     continue;
1551                 } else {
1552                     // Add $nothing option if there are not optgroups
1553                     if ($this->nothinglabel && empty($this->options[0]) && !$inoptgroup) {
1554                         $nothingoption = new html_select_option();
1555                         $nothingoption->value = 0;
1556                         if (!empty($this->nothingvalue)) {
1557                             $nothingoption->value = $this->nothingvalue;
1558                         }
1559                         $nothingoption->text = $this->nothinglabel;
1560                         $this->options = array($nothingoption) + $this->options;
1561                     }
1563                     $option = new html_select_option();
1564                     $option->text = $display;
1566                     if ($display === '') {
1567                         $option->text = $value;
1568                     }
1570                     if ((string) $value == (string) $this->selectedvalue ||
1571                             (is_array($this->selectedvalue) && in_array($value, $this->selectedvalue))) {
1572                         $option->selected = 'selected';
1573                     }
1575                     $option->value = s($value);
1577                     if ($inoptgroup) {
1578                         $optgroup->options[] = $option;
1579                     } else {
1580                         $this->options[] = $option;
1581                     }
1582                 }
1583             }
1585             if ($optgroup) {
1586                 $this->options[] = $optgroup;
1587             }
1588         }
1589     }
1593 /**
1594  * This class represents a select option element
1595  *
1596  * @copyright 2009 Nicolas Connault
1597  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1598  * @since     Moodle 2.0
1599  */
1600 class html_select_option extends labelled_html_component {
1601     /**
1602      * @var string $value The value of this option (will be sent with form)
1603      */
1604     public $value;
1605     /**
1606      * @var string $text The display value of the option
1607      */
1608     public $text;
1609     /**
1610      * @var boolean $selected Whether or not this option is selected
1611      */
1612     public $selected = false;
1613     /**
1614      * @var boolean $disabled Whether or not this option is disabled
1615      */
1616     public $disabled = false;
1618     public function __construct() {
1619         $this->label = new html_label();
1620     }
1622     /**
1623      * @see html_component::prepare()
1624      * @return void
1625      */
1626     public function prepare(renderer_base $output, moodle_page $page, $target) {
1627         if (empty($this->text) && (string)$this->text!=='0') {
1628             throw new coding_exception('html_select_option requires a $text value.');
1629         }
1631         if (empty($this->label->text)) {
1632             $this->set_label($this->text);
1633         } else if (!($this->label instanceof html_label)) {
1634             $this->set_label($this->label);
1635         }
1636         if (empty($this->id)) {
1637             $this->generate_id();
1638         }
1640         parent::prepare($output, $page, $target);
1641     }
1645 /**
1646  * This class represents a select optgroup element
1647  *
1648  * @copyright 2009 Nicolas Connault
1649  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1650  * @since     Moodle 2.0
1651  */
1652 class html_select_optgroup extends html_component {
1653     /**
1654      * @var string $text The display value of the optgroup
1655      */
1656     public $text;
1657     /**
1658      * @var array $options An array of html_select_option objects
1659      */
1660     public $options = array();
1662     public function prepare(renderer_base $output, moodle_page $page, $target) {
1663         if (empty($this->text)) {
1664             throw new coding_exception('html_select_optgroup requires a $text value.');
1665         }
1666         if (empty($this->options)) {
1667             throw new coding_exception('html_select_optgroup requires at least one html_select_option object');
1668         }
1669         parent::prepare($output, $page, $target);
1670     }
1674 /**
1675  * Holds all the information required to render a <table> by
1676  * {@see core_renderer::table()} or by an overridden version of that
1677  * method in a subclass.
1678  *
1679  * Example of usage:
1680  * $t = new html_table();
1681  * ... // set various properties of the object $t as described below
1682  * echo $OUTPUT->table($t);
1683  *
1684  * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
1685  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1686  * @since     Moodle 2.0
1687  */
1688 class html_table extends labelled_html_component {
1689     /**
1690      * For more control over the rendering of the headers, an array of html_table_cell objects
1691      * can be passed instead of an array of strings.
1692      * @var array of headings. The n-th array item is used as a heading of the n-th column.
1693      *
1694      * Example of usage:
1695      * $t->head = array('Student', 'Grade');
1696      */
1697     public $head;
1698     /**
1699      * @var array can be used to make a heading span multiple columns
1700      *
1701      * Example of usage:
1702      * $t->headspan = array(2,1);
1703      *
1704      * In this example, {@see html_table:$data} is supposed to have three columns. For the first two columns,
1705      * the same heading is used. Therefore, {@see html_table::$head} should consist of two items.
1706      */
1707     public $headspan;
1708     /**
1709      * @var array of column alignments. The value is used as CSS 'text-align' property. Therefore, possible
1710      * values are 'left', 'right', 'center' and 'justify'. Specify 'right' or 'left' from the perspective
1711      * of a left-to-right (LTR) language. For RTL, the values are flipped automatically.
1712      *
1713      * Examples of usage:
1714      * $t->align = array(null, 'right');
1715      * or
1716      * $t->align[1] = 'right';
1717      *
1718      */
1719     public $align;
1720     /**
1721      * @var array of column sizes. The value is used as CSS 'size' property.
1722      *
1723      * Examples of usage:
1724      * $t->size = array('50%', '50%');
1725      * or
1726      * $t->size[1] = '120px';
1727      */
1728     public $size;
1729     /**
1730      * @var array of wrapping information. The only possible value is 'nowrap' that sets the
1731      * CSS property 'white-space' to the value 'nowrap' in the given column.
1732      *
1733      * Example of usage:
1734      * $t->wrap = array(null, 'nowrap');
1735      */
1736     public $wrap;
1737     /**
1738      * @var array of arrays or html_table_row objects containing the data. Alternatively, if you have
1739      * $head specified, the string 'hr' (for horizontal ruler) can be used
1740      * instead of an array of cells data resulting in a divider rendered.
1741      *
1742      * Example of usage with array of arrays:
1743      * $row1 = array('Harry Potter', '76 %');
1744      * $row2 = array('Hermione Granger', '100 %');
1745      * $t->data = array($row1, $row2);
1746      *
1747      * Example with array of html_table_row objects: (used for more fine-grained control)
1748      * $cell1 = new html_table_cell();
1749      * $cell1->text = 'Harry Potter';
1750      * $cell1->colspan = 2;
1751      * $row1 = new html_table_row();
1752      * $row1->cells[] = $cell1;
1753      * $cell2 = new html_table_cell();
1754      * $cell2->text = 'Hermione Granger';
1755      * $cell3 = new html_table_cell();
1756      * $cell3->text = '100 %';
1757      * $row2 = new html_table_row();
1758      * $row2->cells = array($cell2, $cell3);
1759      * $t->data = array($row1, $row2);
1760      */
1761     public $data;
1762     /**
1763      * @var string width of the table, percentage of the page preferred. Defaults to 80% of the page width.
1764      * @deprecated since Moodle 2.0. Styling should be in the CSS.
1765      */
1766     public $width = null;
1767     /**
1768      * @var string alignment the whole table. Can be 'right', 'left' or 'center' (default).
1769      * @deprecated since Moodle 2.0. Styling should be in the CSS.
1770      */
1771     public $tablealign = null;
1772     /**
1773      * @var int padding on each cell, in pixels
1774      * @deprecated since Moodle 2.0. Styling should be in the CSS.
1775      */
1776     public $cellpadding = null;
1777     /**
1778      * @var int spacing between cells, in pixels
1779      * @deprecated since Moodle 2.0. Styling should be in the CSS.
1780      */
1781     public $cellspacing = null;
1782     /**
1783      * @var array classes to add to particular rows, space-separated string.
1784      * Classes 'r0' or 'r1' are added automatically for every odd or even row,
1785      * respectively. Class 'lastrow' is added automatically for the last row
1786      * in the table.
1787      *
1788      * Example of usage:
1789      * $t->rowclasses[9] = 'tenth'
1790      */
1791     public $rowclasses;
1792     /**
1793      * @var array classes to add to every cell in a particular column,
1794      * space-separated string. Class 'cell' is added automatically by the renderer.
1795      * Classes 'c0' or 'c1' are added automatically for every odd or even column,
1796      * respectively. Class 'lastcol' is added automatically for all last cells
1797      * in a row.
1798      *
1799      * Example of usage:
1800      * $t->colclasses = array(null, 'grade');
1801      */
1802     public $colclasses;
1803     /**
1804      * @var string description of the contents for screen readers.
1805      */
1806     public $summary;
1807     /**
1808      * @var bool true causes the contents of the heading cells to be rotated 90 degrees.
1809      */
1810     public $rotateheaders = false;
1811     /**
1812      * @var array $headclasses Array of CSS classes to apply to the table's thead.
1813      */
1814     public $headclasses = array();
1815     /**
1816      * @var array $bodyclasses Array of CSS classes to apply to the table's tbody.
1817      */
1818     public $bodyclasses = array();
1819     /**
1820      * @var array $footclasses Array of CSS classes to apply to the table's tfoot.
1821      */
1822     public $footclasses = array();
1825     /**
1826      * @see html_component::prepare()
1827      * @return void
1828      */
1829     public function prepare(renderer_base $output, moodle_page $page, $target) {
1830         if (!empty($this->align)) {
1831             foreach ($this->align as $key => $aa) {
1832                 if ($aa) {
1833                     $this->align[$key] = 'text-align:'. fix_align_rtl($aa) .';';  // Fix for RTL languages
1834                 } else {
1835                     $this->align[$key] = null;
1836                 }
1837             }
1838         }
1839         if (!empty($this->size)) {
1840             foreach ($this->size as $key => $ss) {
1841                 if ($ss) {
1842                     $this->size[$key] = 'width:'. $ss .';';
1843                 } else {
1844                     $this->size[$key] = null;
1845                 }
1846             }
1847         }
1848         if (!empty($this->wrap)) {
1849             foreach ($this->wrap as $key => $ww) {
1850                 if ($ww) {
1851                     $this->wrap[$key] = 'white-space:nowrap;';
1852                 } else {
1853                     $this->wrap[$key] = '';
1854                 }
1855             }
1856         }
1857         if (!empty($this->head)) {
1858             foreach ($this->head as $key => $val) {
1859                 if (!isset($this->align[$key])) {
1860                     $this->align[$key] = null;
1861                 }
1862                 if (!isset($this->size[$key])) {
1863                     $this->size[$key] = null;
1864                 }
1865                 if (!isset($this->wrap[$key])) {
1866                     $this->wrap[$key] = null;
1867                 }
1869             }
1870         }
1871         if (empty($this->classes)) { // must be done before align
1872             $this->set_classes(array('generaltable'));
1873         }
1874         if (!empty($this->tablealign)) {
1875             $this->add_class('boxalign' . $this->tablealign);
1876         }
1877         if (!empty($this->rotateheaders)) {
1878             $this->add_class('rotateheaders');
1879         } else {
1880             $this->rotateheaders = false; // Makes life easier later.
1881         }
1882         parent::prepare($output, $page, $target);
1883     }
1884     /**
1885      * @param string $name The name of the variable to set
1886      * @param mixed $value The value to assign to the variable
1887      * @return void
1888      */
1889     public function __set($name, $value) {
1890         if ($name == 'rowclass') {
1891             debugging('rowclass[] has been deprecated for html_table ' .
1892                       'and should be replaced with rowclasses[]. please fix the code.');
1893             $this->rowclasses = $value;
1894         } else {
1895             parent::__set($name, $value);
1896         }
1897     }
1901 /**
1902  * Component representing a table row.
1903  *
1904  * @copyright 2009 Nicolas Connault
1905  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1906  * @since     Moodle 2.0
1907  */
1908 class html_table_row extends html_component {
1909     /**
1910      * @var array $cells Array of html_table_cell objects
1911      */
1912     public $cells = array();
1914     /**
1915      * @see lib/html_component#prepare()
1916      * @return void
1917      */
1918     public function prepare(renderer_base $output, moodle_page $page, $target) {
1919         parent::prepare($output, $page, $target);
1920     }
1922     /**
1923      * Shortcut method for creating a row with an array of cells. Converts cells to html_table_cell objects.
1924      * @param array $cells
1925      * @return html_table_row
1926      */
1927     public static function make($cells=array()) {
1928         $row = new html_table_row();
1929         foreach ($cells as $celltext) {
1930             if (!($celltext instanceof html_table_cell)) {
1931                 $cell = new html_table_cell();
1932                 $cell->text = $celltext;
1933                 $row->cells[] = $cell;
1934             } else {
1935                 $row->cells[] = $celltext;
1936             }
1937         }
1938         return $row;
1939     }
1943 /**
1944  * Component representing a table cell.
1945  *
1946  * @copyright 2009 Nicolas Connault
1947  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1948  * @since     Moodle 2.0
1949  */
1950 class html_table_cell extends html_component {
1951     /**
1952      * @var string $text The contents of the cell
1953      */
1954     public $text;
1955     /**
1956      * @var string $abbr Abbreviated version of the contents of the cell
1957      */
1958     public $abbr = null;
1959     /**
1960      * @var int $colspan Number of columns this cell should span
1961      */
1962     public $colspan = null;
1963     /**
1964      * @var int $rowspan Number of rows this cell should span
1965      */
1966     public $rowspan = null;
1967     /**
1968      * @var string $scope Defines a way to associate header cells and data cells in a table
1969      */
1970     public $scope = null;
1971     /**
1972      * @var boolean $header Whether or not this cell is a header cell
1973      */
1974     public $header = null;
1976     /**
1977      * @see lib/html_component#prepare()
1978      * @return void
1979      */
1980     public function prepare(renderer_base $output, moodle_page $page, $target) {
1981         if ($this->header && empty($this->scope)) {
1982             $this->scope = 'col';
1983         }
1984         parent::prepare($output, $page, $target);
1985     }
1989 /**
1990  * Component representing a XHTML link.
1991  *
1992  * @copyright 2009 Nicolas Connault
1993  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1994  * @since     Moodle 2.0
1995  */
1996 class html_link extends html_component {
1997     /**
1998      * URL can be simple text or a moodle_url object
1999      * @var mixed $url
2000      */
2001     public $url;
2003     /**
2004      * @var string $text The HTML text that will appear between the link tags
2005      */
2006     public $text = null;
2008     /**
2009      * @var boolean $disabled Whether or not this link is disabled (will be rendered as plain text)
2010      */
2011     public $disabled = false;
2013     /**
2014      * @var boolean $disableifcurrent Whether or not this link should be disabled if it the same as the current page
2015      */
2016     public $disableifcurrent = false;
2018     /**
2019      * New link constructor.
2020      *
2021      * @param moodle_url|string $url url of the image
2022      * @param array $options link attributes such as title, id, disabled, disableifcurrent, etc.
2023      */
2024     public function __construct($url = null, $text = null, array $options = null) {
2025         parent::__construct($options);
2027         if (is_null($url)) {
2028             // to be filled later
2030         } else if ($url instanceof moodle_url) {
2031             $this->url = clone($url);
2033         } else if (is_string($url)) {
2034             $this->url = new moodle_url($url);
2036         } else {
2037             throw new coding_style_exception('Image can be constructed only from moodle_url or string url.');
2038         }
2040         $this->text = $text;
2041     }
2043     /**
2044      * @see lib/html_component#prepare() Disables the link if it links to the current page.
2045      * @return void
2046      */
2047     public function prepare(renderer_base $output, moodle_page $page, $target) {
2048         // We can't accept an empty text value
2049         if ($this->text === '' or is_null($this->text)) { // 0 is valid value, do not use empty()
2050             throw new coding_exception('A html_link must have a descriptive text value!');
2051         }
2053         if (!($this->url instanceof moodle_url)) {
2054             $this->url = new moodle_url($this->url);
2055         }
2057         if ($this->disableifcurrent and $this->url->compare($page->url, URL_MATCH_PARAMS)) {
2058             $this->disabled = true;
2059         }
2061         parent::prepare($output, $page, $target);
2062     }
2064     /**
2065      * Shortcut for creating a link component.
2066      * @param mixed  $url String or moodle_url
2067      * @param string $text The text of the link
2068      * @return html_link The link component
2069      */
2070     public static function make($url, $text) {
2071         return new html_link($url, $text);
2072     }
2076 /**
2077  * Component representing a XHTML button (input of type 'button').
2078  * The renderer will either output it as a button with an onclick event,
2079  * or as a form with hidden inputs.
2080  *
2081  * @copyright 2009 Nicolas Connault
2082  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2083  * @since     Moodle 2.0
2084  */
2085 class html_button extends labelled_html_component {
2086     /**
2087      * @var string $text
2088      */
2089     public $text;
2091     /**
2092      * @var boolean $disabled Whether or not this button is disabled
2093      */
2094     public $disabled = false;
2096     /**
2097      * @see lib/html_component#prepare()
2098      * @return void
2099      */
2100     public function prepare(renderer_base $output, moodle_page $page, $target) {
2101         $this->add_class('singlebutton');
2103         if (empty($this->text)) {
2104             $this->text = get_string('submit');
2105         }
2107         if ($this->disabled) {
2108             $this->disabled = 'disabled';
2109         }
2111         parent::prepare($output, $page, $target);
2112     }
2115 /**
2116  * Component representing an image.
2117  *
2118  * @copyright 2009 Nicolas Connault
2119  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2120  * @since     Moodle 2.0
2121  */
2122 class html_image extends labelled_html_component {
2123     /**
2124      * @var string $src The path to the image being used
2125      */
2126     public $src;
2127     /**
2128      * @var int $width of image
2129      */
2130     public $width;
2131     /**
2132      * @var int $height of image
2133      */
2134     public $height;
2136     /**
2137      * New image constructor.
2138      *
2139      * @param moodle_url|string $url url of the image
2140      * @param array $options image attributes such as title, id, alt, widht, height
2141      */
2142     public function __construct($src = null, array $options = null) {
2143         parent::__construct($options);
2145         if (is_null($src)) {
2146             // to be filled later
2148         } else if ($src instanceof moodle_url) {
2149             $this->src = clone($src);
2151         } else if (is_string($src)) {
2152             $this->src = new moodle_url($src);
2154         } else {
2155             throw new coding_style_exception('Image can be constructed only from moodle_url or string url.');
2156         }
2157     }
2159     /**
2160      * @see lib/html_component#prepare()
2161      * @return void
2162      */
2163     public function prepare(renderer_base $output, moodle_page $page, $target) {
2164         if (empty($this->src)) {
2165             throw new coding_exception('html_image requires a $src value (moodle_url).');
2166         }
2168         // no general class here, use custom class instead or img element directly in css selectors
2169         parent::prepare($output, $page, $target);
2171         if ($this->alt === null) {
2172             // needs to be set for accessibility reasons
2173             $this->alt = '';
2174         }
2175     }
2179 /**
2180  * Component representing a textarea.
2181  *
2182  * @copyright 2009 Nicolas Connault
2183  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2184  * @since     Moodle 2.0
2185  */
2186 class html_textarea extends html_component {
2187     /**
2188      * @param string $name Name to use for the textarea element.
2189      */
2190     public $name;
2191     /**
2192      * @param string $value Initial content to display in the textarea.
2193      */
2194     public $value;
2195     /**
2196      * @param int $rows Number of rows to display  (minimum of 10 when $height is non-null)
2197      */
2198     public $rows;
2199     /**
2200      * @param int $cols Number of columns to display (minimum of 65 when $width is non-null)
2201      */
2202     public $cols;
2203     /**
2204      * @param bool $usehtmleditor Enables the use of the htmleditor for this field.
2205      */
2206     public $usehtmleditor;
2208     /**
2209      * @see lib/html_component#prepare()
2210      * @return void
2211      */
2212     public function prepare(renderer_base $output, moodle_page $page, $target) {
2213         $this->add_class('form-textarea');
2215         if (empty($this->id)) {
2216             $this->id = "edit-$this->name";
2217         }
2219         if ($this->usehtmleditor) {
2220             editors_head_setup();
2221             $editor = get_preferred_texteditor(FORMAT_HTML);
2222             $editor->use_editor($this->id, array('legacy'=>true));
2223             $this->value = htmlspecialchars($value);
2224         }
2226         parent::prepare($output, $page, $target);
2227     }
2231 /**
2232  * Component representing a simple form wrapper. Its purpose is mainly to enclose
2233  * a submit input with the appropriate action and hidden inputs.
2234  *
2235  * @copyright 2009 Nicolas Connault
2236  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2237  * @since     Moodle 2.0
2238  */
2239 class html_form extends html_component {
2240     /**
2241      * @var string $method post or get
2242      */
2243     public $method = 'post';
2244     /**
2245      * If a string is given, it will be converted to a moodle_url during prepare()
2246      * @var mixed $url A moodle_url including params or a string
2247      */
2248     public $url;
2249     /**
2250      * @var boolean $showbutton If true, the submit button will always be shown even if JavaScript is available
2251      */
2252     public $showbutton = false;
2253     /**
2254      * @var string $targetwindow The name of the target page to open the linked page in.
2255      */
2256     public $targetwindow = 'self';
2257     /**
2258      * @var html_button $button A submit button
2259      */
2260     public $button;
2261     /**
2262      * @var boolean $jssubmitaction If true, the submit button will be hidden when JS is enabled
2263      */
2264     public $jssubmitaction = false;
2265     /**
2266      * Constructor: sets up the other components in case they are needed
2267      * @return void
2268      */
2269     public function __construct(array $options = null) {
2270         parent::__construct($options);
2271         $this->button = new html_button();
2272         $this->button->text = get_string('go');
2273     }
2275     /**
2276      * @see lib/html_component#prepare()
2277      * @return void
2278      */
2279     public function prepare(renderer_base $output, moodle_page $page, $target) {
2281         if (empty($this->url)) {
2282             throw new coding_exception('A html_form must have a $url value (string or moodle_url).');
2283         }
2285         if (is_string($this->url)) {
2286             $this->url = new moodle_url($this->url);
2287         }
2289         if ($this->method == 'post') {
2290             // automatic CSRF protection
2291             $this->url->param('sesskey', sesskey());
2292         }
2294         parent::prepare($output, $page, $target);
2295     }
2299 /**
2300  * Component representing a list.
2301  *
2302  * The advantage of using this object instead of a flat array is that you can load it
2303  * with metadata (CSS classes, event handlers etc.) which can be used by the renderers.
2304  *
2305  * @copyright 2009 Nicolas Connault
2306  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2307  * @since     Moodle 2.0
2308  */
2309 class html_list extends html_component {
2311     /**
2312      * @var array $items An array of html_list_item or html_list objects
2313      */
2314     public $items = array();
2316     /**
2317      * @var string $type The type of list (ordered|unordered), definition type not yet supported
2318      */
2319     public $type = 'unordered';
2321     /**
2322      * @var string $text An optional descriptive text for the list. Will be output as a list item before opening the new list
2323      */
2324     public $text = false;
2326     /**
2327      * @see lib/html_component#prepare()
2328      * @return void
2329      */
2330     public function prepare(renderer_base $output, moodle_page $page, $target) {
2331         parent::prepare($output, $page, $target);
2332     }
2334     /**
2335      * This function takes a nested array of data and maps it into this list's $items array
2336      * as proper html_list_item and html_list objects, with appropriate metadata.
2337      *
2338      * @param array $tree A nested array (array keys are ignored);
2339      * @param int $row Used in identifying the iteration level and in ul classes
2340      * @return void
2341      */
2342     public function load_data($tree, $level=0) {
2344         $this->add_class("list-$level");
2346         $i = 1;
2347         foreach ($tree as $key => $element) {
2348             if (is_array($element)) {
2349                 $newhtmllist = new html_list();
2350                 $newhtmllist->type = $this->type;
2351                 $newhtmllist->load_data($element, $level + 1);
2352                 $newhtmllist->text = $key;
2353                 $this->items[] = $newhtmllist;
2354             } else {
2355                 $listitem = new html_list_item();
2356                 $listitem->value = $element;
2357                 $listitem->add_class("list-item-$level-$i");
2358                 $this->items[] = $listitem;
2359             }
2360             $i++;
2361         }
2362     }
2364     /**
2365      * Adds a html_list_item or html_list to this list.
2366      * If the param is a string, a html_list_item will be added.
2367      * @param mixed $item String, html_list or html_list_item object
2368      * @return void
2369      */
2370     public function add_item($item) {
2371         if ($item instanceof html_list_item || $item instanceof html_list) {
2372             $this->items[] = $item;
2373         } else {
2374             $listitem = new html_list_item();
2375             $listitem->value = $item;
2376             $this->items[] = $item;
2377         }
2378     }
2382 /**
2383  * Component representing a list item.
2384  *
2385  * @copyright 2009 Nicolas Connault
2386  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2387  * @since     Moodle 2.0
2388  */
2389 class html_list_item extends html_component {
2390     /**
2391      * @var string $value The value of the list item
2392      */
2393     public $value;
2395     /**
2396      * @see lib/html_component#prepare()
2397      * @return void
2398      */
2399     public function prepare(renderer_base $output, moodle_page $page, $target) {
2400         parent::prepare($output, $page, $target);
2401     }
2405 /**
2406  * Component representing a span element. It has no special attributes, so
2407  * it is very low-level and can be used for styling and JS actions.
2408  *
2409  * @copyright 2009 Nicolas Connault
2410  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2411  * @since     Moodle 2.0
2412  */
2413 class html_span extends html_component {
2414     /**
2415      * @var string $text The contents of the span
2416      */
2417     public $contents;
2418     /**
2419      * @see lib/html_component#prepare()
2420      * @return void
2421      */
2422     public function prepare(renderer_base $output, moodle_page $page, $target) {
2423         parent::prepare($output, $page, $target);
2424     }
2427 /// Complex components aggregating simpler components
2430 /**
2431  * Component representing a paging bar.
2432  *
2433  * @copyright 2009 Nicolas Connault
2434  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2435  * @since     Moodle 2.0
2436  */
2437 class moodle_paging_bar extends html_component {
2438     /**
2439      * @var int $maxdisplay The maximum number of pagelinks to display
2440      */
2441     public $maxdisplay = 18;
2442     /**
2443      * @var int $totalcount post or get
2444      */
2445     public $totalcount;
2446     /**
2447      * @var int $page The page you are currently viewing
2448      */
2449     public $page = 0;
2450     /**
2451      * @var int $perpage The number of entries that should be shown per page
2452      */
2453     public $perpage;
2454     /**
2455      * @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.
2456      *      If this is a moodle_url object then the pagevar param will be replaced by the page no, for each page.
2457      */
2458     public $baseurl;
2459     /**
2460      * @var string $pagevar This is the variable name that you use for the page number in your code (ie. 'tablepage', 'blogpage', etc)
2461      */
2462     public $pagevar = 'page';
2463     /**
2464      * @var html_link $previouslink A HTML link representing the "previous" page
2465      */
2466     public $previouslink = null;
2467     /**
2468      * @var html_link $nextlink A HTML link representing the "next" page
2469      */
2470     public $nextlink = null;
2471     /**
2472      * @var html_link $firstlink A HTML link representing the first page
2473      */
2474     public $firstlink = null;
2475     /**
2476      * @var html_link $lastlink A HTML link representing the last page
2477      */
2478     public $lastlink = null;
2479     /**
2480      * @var array $pagelinks An array of html_links. One of them is just a string: the current page
2481      */
2482     public $pagelinks = array();
2484     /**
2485      * @see lib/html_component#prepare()
2486      * @return void
2487      */
2488     public function prepare(renderer_base $output, moodle_page $page, $target) {
2489         if (!isset($this->totalcount) || is_null($this->totalcount)) {
2490             throw new coding_exception('moodle_paging_bar requires a totalcount value.');
2491         }
2492         if (!isset($this->page) || is_null($this->page)) {
2493             throw new coding_exception('moodle_paging_bar requires a page value.');
2494         }
2495         if (empty($this->perpage)) {
2496             throw new coding_exception('moodle_paging_bar requires a perpage value.');
2497         }
2498         if (empty($this->baseurl)) {
2499             throw new coding_exception('moodle_paging_bar requires a baseurl value.');
2500         }
2501         if (!($this->baseurl instanceof moodle_url)) {
2502             $this->baseurl = new moodle_url($this->baseurl);
2503         }
2505         if ($this->totalcount > $this->perpage) {
2506             $pagenum = $this->page - 1;
2508             if ($this->page > 0) {
2509                 $this->previouslink = new html_link();
2510                 $this->previouslink->add_class('previous');
2511                 $this->previouslink->url = clone($this->baseurl);
2512                 $this->previouslink->url->param($this->pagevar, $pagenum);
2513                 $this->previouslink->text = get_string('previous');
2514             }
2516             if ($this->perpage > 0) {
2517                 $lastpage = ceil($this->totalcount / $this->perpage);
2518             } else {
2519                 $lastpage = 1;
2520             }
2522             if ($this->page > 15) {
2523                 $startpage = $this->page - 10;
2525                 $this->firstlink = new html_link();
2526                 $this->firstlink->url = clone($this->baseurl);
2527                 $this->firstlink->url->param($this->pagevar, 0);
2528                 $this->firstlink->text = 1;
2529                 $this->firstlink->add_class('first');
2530             } else {
2531                 $startpage = 0;
2532             }
2534             $currpage = $startpage;
2535             $displaycount = $displaypage = 0;
2537             while ($displaycount < $this->maxdisplay and $currpage < $lastpage) {
2538                 $displaypage = $currpage + 1;
2540                 if ($this->page == $currpage) {
2541                     $this->pagelinks[] = $displaypage;
2542                 } else {
2543                     $pagelink = new html_link();
2544                     $pagelink->url = clone($this->baseurl);
2545                     $pagelink->url->param($this->pagevar, $currpage);
2546                     $pagelink->text = $displaypage;
2547                     $this->pagelinks[] = $pagelink;
2548                 }
2550                 $displaycount++;
2551                 $currpage++;
2552             }
2554             if ($currpage < $lastpage) {
2555                 $lastpageactual = $lastpage - 1;
2556                 $this->lastlink = new html_link();
2557                 $this->lastlink->url = clone($this->baseurl);
2558                 $this->lastlink->url->param($this->pagevar, $lastpageactual);
2559                 $this->lastlink->text = $lastpage;
2560                 $this->lastlink->add_class('last');
2561             }
2563             $pagenum = $this->page + 1;
2565             if ($pagenum != $displaypage) {
2566                 $this->nextlink = new html_link();
2567                 $this->nextlink->url = clone($this->baseurl);
2568                 $this->nextlink->url->param($this->pagevar, $pagenum);
2569                 $this->nextlink->text = get_string('next');
2570                 $this->nextlink->add_class('next');
2571             }
2572         }
2573     }
2575     /**
2576      * Shortcut for initialising a moodle_paging_bar with only the required params.
2577      *
2578      * @param int $totalcount Thetotal number of entries available to be paged through
2579      * @param int $page The page you are currently viewing
2580      * @param int $perpage The number of entries that should be shown per page
2581      * @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.
2582      *                          If this is a moodle_url object then the pagevar param will be replaced by the page no, for each page.
2583      */
2584     public static function make($totalcount, $page, $perpage, $baseurl) {
2585         $pagingbar = new moodle_paging_bar();
2586         $pagingbar->totalcount = $totalcount;
2587         $pagingbar->page = $page;
2588         $pagingbar->perpage = $perpage;
2589         $pagingbar->baseurl = $baseurl;
2590         return $pagingbar;
2591     }
2595 /**
2596  * This class represents how a block appears on a page.
2597  *
2598  * During output, each block instance is asked to return a block_contents object,
2599  * those are then passed to the $OUTPUT->block function for display.
2600  *
2601  * {@link $contents} should probably be generated using a moodle_block_..._renderer.
2602  *
2603  * Other block-like things that need to appear on the page, for example the
2604  * add new block UI, are also represented as block_contents objects.
2605  *
2606  * @copyright 2009 Tim Hunt
2607  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2608  * @since     Moodle 2.0
2609  */
2610 class block_contents extends html_component {
2611     /** @var int used to set $skipid. */
2612     protected static $idcounter = 1;
2614     const NOT_HIDEABLE = 0;
2615     const VISIBLE = 1;
2616     const HIDDEN = 2;
2618     /**
2619      * @param integer $skipid All the blocks (or things that look like blocks)
2620      * printed on a page are given a unique number that can be used to construct
2621      * id="" attributes. This is set automatically be the {@link prepare()} method.
2622      * Do not try to set it manually.
2623      */
2624     public $skipid;
2626     /**
2627      * @var integer If this is the contents of a real block, this should be set to
2628      * the block_instance.id. Otherwise this should be set to 0.
2629      */
2630     public $blockinstanceid = 0;
2632     /**
2633      * @var integer if this is a real block instance, and there is a corresponding
2634      * block_position.id for the block on this page, this should be set to that id.
2635      * Otherwise it should be 0.
2636      */
2637     public $blockpositionid = 0;
2639     /**
2640      * @param array $attributes an array of attribute => value pairs that are put on the
2641      * outer div of this block. {@link $id} and {@link $classes} attributes should be set separately.
2642      */
2643     public $attributes = array();
2645     /**
2646      * @param string $title The title of this block. If this came from user input,
2647      * it should already have had format_string() processing done on it. This will
2648      * be output inside <h2> tags. Please do not cause invalid XHTML.
2649      */
2650     public $title = '';
2652     /**
2653      * @param string $content HTML for the content
2654      */
2655     public $content = '';
2657     /**
2658      * @param array $list an alternative to $content, it you want a list of things with optional icons.
2659      */
2660     public $footer = '';
2662     /**
2663      * Any small print that should appear under the block to explain to the
2664      * teacher about the block, for example 'This is a sticky block that was
2665      * added in the system context.'
2666      * @var string
2667      */
2668     public $annotation = '';
2670     /**
2671      * @var integer one of the constants NOT_HIDEABLE, VISIBLE, HIDDEN. Whether
2672      * the user can toggle whether this block is visible.
2673      */
2674     public $collapsible = self::NOT_HIDEABLE;
2676     /**
2677      * A (possibly empty) array of editing controls. Each element of this array
2678      * should be an array('url' => $url, 'icon' => $icon, 'caption' => $caption).
2679      * $icon is the icon name. Fed to $OUTPUT->pix_url.
2680      * @var array
2681      */
2682     public $controls = array();
2684     /**
2685      * @see html_component::prepare()
2686      * @return void
2687      */
2688     public function prepare(renderer_base $output, moodle_page $page, $target) {
2689         $this->skipid = self::$idcounter;
2690         self::$idcounter += 1;
2691         $this->add_class('sideblock');
2692         if (empty($this->blockinstanceid) || !strip_tags($this->title)) {
2693             $this->collapsible = self::NOT_HIDEABLE;
2694         }
2695         if ($this->collapsible == self::HIDDEN) {
2696             $this->add_class('hidden');
2697         }
2698         if (!empty($this->controls)) {
2699             $this->add_class('block_with_controls');
2700         }
2701         parent::prepare($output, $page, $target);
2702     }
2706 /**
2707  * This class represents a target for where a block can go when it is being moved.
2708  *
2709  * This needs to be rendered as a form with the given hidden from fields, and
2710  * clicking anywhere in the form should submit it. The form action should be
2711  * $PAGE->url.
2712  *
2713  * @copyright 2009 Tim Hunt
2714  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2715  * @since     Moodle 2.0
2716  */
2717 class block_move_target extends html_component {
2718     /**
2719      * List of hidden form fields.
2720      * @var array
2721      */
2722     public $url = array();
2723     /**
2724      * List of hidden form fields.
2725      * @var array
2726      */
2727     public $text = '';