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