30dfd97ee929c7254c021cd54fadc2df6295a6b2
[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      *
123      * In some cases we have to fetch the user data together with some other information,
124      * the idalias is useful there because the id would otherwise override the main
125      * id of the result record. Please note it has to be converted back to id before rendering.
126      *
127      * @param string $tableprefix name of database table prefix in query
128      * @param string $idalias alias of id field
129      * @return string
130      */
131     public static function fields($tableprefix = '', $idalias = '') {
132         if ($tableprefix === '' and $idalias === '') {
133             return self::FIELDS;
134         }
135         $fields = explode(',', self::FIELDS);
136         foreach ($fields as $key=>$field) {
137             if ($field === 'id' and $idalias !== '') {
138                 $field = "$field AS $idalias";
139             }
140             $fields[$key] = "$tableprefix.$field";
141         }
142         return implode(',', $fields);
143     }
147 /**
148  * Data structure representing a help icon.
149  *
150  * @copyright 2009 Nicolas Connault, 2010 Petr Skoda
151  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
152  * @since     Moodle 2.0
153  */
154 class old_help_icon implements renderable {
155     /**
156      * @var string $helpidentifier lang pack identifier (should inlcude the "_hlp" suffix)
157      */
158     public $helpidentifier;
159     /**
160      * @var string $title A descriptive text for title tooltip
161      */
162     public $title = null;
163     /**
164      * @var string $component Component name, the same as in get_string()
165      */
166     public $component = 'moodle';
167     /**
168      * @var string $linktext Extra descriptive text next to the icon
169      */
170     public $linktext = null;
172     /**
173      * Constructor: sets up the other components in case they are needed
174      * @param string $helpidentifier  The keyword that defines a help page
175      * @param string $title A descriptive text for accesibility only
176      * @param string $component
177      * @param bool $linktext add extra text to icon
178      * @return void
179      */
180     public function __construct($helpidentifier, $title, $component = 'moodle') {
181         if (empty($title)) {
182             throw new coding_exception('A help_icon object requires a $text parameter');
183         }
184         if (empty($helpidentifier)) {
185             throw new coding_exception('A help_icon object requires a $helpidentifier parameter');
186         }
188         $this->helpidentifier  = $helpidentifier;
189         $this->title           = $title;
190         $this->component       = $component;
191     }
195 /**
196  * Data structure representing an icon.
197  *
198  * @copyright 2010 Petr Skoda
199  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
200  * @since     Moodle 2.0
201  */
202 class pix_icon implements renderable {
203     var $pix;
204     var $component;
205     var $attributes = array();
207     /**
208      * Constructor
209      * @param string $pix short icon name
210      * @param string $component component name
211      * @param array $attributes html attributes
212      */
213     public function __construct($pix, $alt, $component='moodle', array $attributes = null) {
214         $this->pix        = $pix;
215         $this->component  = $component;
216         $this->attributes = (array)$attributes;
218         $this->attributes['alt'] = $alt;
219         if (empty($this->attributes['class'])) {
220             $this->attributes['class'] = 'smallicon';
221         }
222         if (!isset($this->attributes['title'])) {
223             $this->attributes['title'] = $this->attributes['alt'];
224         }
225     }
229 /**
230  * Data structure representing a simple form with only one button.
231  *
232  * @copyright 2009 Petr Skoda
233  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
234  * @since     Moodle 2.0
235  */
236 class single_button implements renderable {
237     /**
238      * Target url
239      * @var moodle_url
240      */
241     var $url;
242     /**
243      * Button label
244      * @var string
245      */
246     var $label;
247     /**
248      * Form submit method
249      * @var string post or get
250      */
251     var $method = 'post';
252     /**
253      * Wrapping div class
254      * @var string
255      * */
256     var $class = 'singlebutton';
257     /**
258      * True if button disabled, false if normal
259      * @var boolean
260      */
261     var $disabled = false;
262     /**
263      * Button tooltip
264      * @var string
265      */
266     var $tooltip = null;
267     /**
268      * Form id
269      * @var string
270      */
271     var $formid;
272     /**
273      * List of attached actions
274      * @var array of component_action
275      */
276     var $actions = array();
278     /**
279      * Constructor
280      * @param string|moodle_url $url
281      * @param string $label button text
282      * @param string $method get or post submit method
283      */
284     public function __construct(moodle_url $url, $label, $method='post') {
285         $this->url    = clone($url);
286         $this->label  = $label;
287         $this->method = $method;
288     }
290     /**
291      * Shortcut for adding a JS confirm dialog when the button is clicked.
292      * The message must be a yes/no question.
293      * @param string $message The yes/no confirmation question. If "Yes" is clicked, the original action will occur.
294      * @return void
295      */
296     public function add_confirm_action($confirmmessage) {
297         $this->add_action(new component_action('click', 'M.util.show_confirm_dialog', array('message' => $confirmmessage)));
298     }
300     /**
301      * Add action to the button.
302      * @param component_action $action
303      * @return void
304      */
305     public function add_action(component_action $action) {
306         $this->actions[] = $action;
307     }
311 /**
312  * Simple form with just one select field that gets submitted automatically.
313  * If JS not enabled small go button is printed too.
314  *
315  * @copyright 2009 Petr Skoda
316  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
317  * @since     Moodle 2.0
318  */
319 class single_select implements renderable {
320     /**
321      * Target url - includes hidden fields
322      * @var moodle_url
323      */
324     var $url;
325     /**
326      * Name of the select element.
327      * @var string
328      */
329     var $name;
330     /**
331      * @var array $options associative array value=>label ex.:
332      *              array(1=>'One, 2=>Two)
333      *              it is also possible to specify optgroup as complex label array ex.:
334      *                array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
335      *                array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
336      */
337     var $options;
338     /**
339      * Selected option
340      * @var string
341      */
342     var $selected;
343     /**
344      * Nothing selected
345      * @var array
346      */
347     var $nothing;
348     /**
349      * Extra select field attributes
350      * @var array
351      */
352     var $attributes = array();
353     /**
354      * Button label
355      * @var string
356      */
357     var $label = '';
358     /**
359      * Form submit method
360      * @var string post or get
361      */
362     var $method = 'get';
363     /**
364      * Wrapping div class
365      * @var string
366      * */
367     var $class = 'singleselect';
368     /**
369      * True if button disabled, false if normal
370      * @var boolean
371      */
372     var $disabled = false;
373     /**
374      * Button tooltip
375      * @var string
376      */
377     var $tooltip = null;
378     /**
379      * Form id
380      * @var string
381      */
382     var $formid = null;
383     /**
384      * List of attached actions
385      * @var array of component_action
386      */
387     var $helpicon = null;
388     /**
389      * Constructor
390      * @param moodle_url $url form action target, includes hidden fields
391      * @param string $name name of selection field - the changing parameter in url
392      * @param array $options list of options
393      * @param string $selected selected element
394      * @param array $nothing
395      * @param string $formid
396      */
397     public function __construct(moodle_url $url, $name, array $options, $selected='', $nothing=array(''=>'choosedots'), $formid=null) {
398         $this->url      = $url;
399         $this->name     = $name;
400         $this->options  = $options;
401         $this->selected = $selected;
402         $this->nothing  = $nothing;
403         $this->formid   = $formid;
404     }
406     /**
407      * Shortcut for adding a JS confirm dialog when the button is clicked.
408      * The message must be a yes/no question.
409      * @param string $message The yes/no confirmation question. If "Yes" is clicked, the original action will occur.
410      * @return void
411      */
412     public function add_confirm_action($confirmmessage) {
413         $this->add_action(new component_action('submit', 'M.util.show_confirm_dialog', array('message' => $confirmmessage)));
414     }
416     /**
417      * Add action to the button.
418      * @param component_action $action
419      * @return void
420      */
421     public function add_action(component_action $action) {
422         $this->actions[] = $action;
423     }
425     /**
426      * Constructor: sets up the other components in case they are needed
427      * @param string $page  The keyword that defines a help page
428      * @param string $title A descriptive text for accesibility only
429      * @param string $component
430      * @param bool $linktext add extra text to icon
431      * @return void
432      */
433     public function set_old_help_icon($helppage, $title, $component = 'moodle') {
434         $this->helpicon = new old_help_icon($helppage, $title, $component);
435     }
437     /**
438      * Set's select lable
439      * @param string $label
440      * @return void
441      */
442     public function set_label($label) {
443         $this->label = $label;
444     }
448 /**
449  * Simple URL selection widget description.
450  * @copyright 2009 Petr Skoda
451  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
452  * @since     Moodle 2.0
453  */
454 class url_select implements renderable {
455     /**
456      * @var array $urls associative array value=>label ex.:
457      *              array(1=>'One, 2=>Two)
458      *              it is also possible to specify optgroup as complex label array ex.:
459      *                array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
460      *                array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
461      */
462     var $urls;
463     /**
464      * Selected option
465      * @var string
466      */
467     var $selected;
468     /**
469      * Nothing selected
470      * @var array
471      */
472     var $nothing;
473     /**
474      * Extra select field attributes
475      * @var array
476      */
477     var $attributes = array();
478     /**
479      * Button label
480      * @var string
481      */
482     var $label = '';
483     /**
484      * Wrapping div class
485      * @var string
486      * */
487     var $class = 'urlselect';
488     /**
489      * True if button disabled, false if normal
490      * @var boolean
491      */
492     var $disabled = false;
493     /**
494      * Button tooltip
495      * @var string
496      */
497     var $tooltip = null;
498     /**
499      * Form id
500      * @var string
501      */
502     var $formid = null;
503     /**
504      * List of attached actions
505      * @var array of component_action
506      */
507     var $helpicon = null;
508     /**
509      * Constructor
510      * @param array $urls list of options
511      * @param string $selected selected element
512      * @param array $nothing
513      * @param string $formid
514      */
515     public function __construct(array $urls, $selected='', $nothing=array(''=>'choosedots'), $formid=null) {
516         $this->urls     = $urls;
517         $this->selected = $selected;
518         $this->nothing  = $nothing;
519         $this->formid   = $formid;
520     }
522     /**
523      * Constructor: sets up the other components in case they are needed
524      * @param string $page  The keyword that defines a help page
525      * @param string $title A descriptive text for accesibility only
526      * @param string $component
527      * @param bool $linktext add extra text to icon
528      * @return void
529      */
530     public function set_old_help_icon($helppage, $title, $component = 'moodle') {
531         $this->helpicon = new old_help_icon($helppage, $title, $component);
532     }
534     /**
535      * Set's select lable
536      * @param string $label
537      * @return void
538      */
539     public function set_label($label) {
540         $this->label = $label;
541     }
545 /**
546  * Data structure describing html link with special action attached.
547  * @copyright 2010 Petr Skoda
548  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
549  * @since     Moodle 2.0
550  */
551 class action_link implements renderable {
552     /**
553      * Href url
554      * @var moodle_url
555      */
556     var $url;
557     /**
558      * Link text
559      * @var string HTML fragment
560      */
561     var $text;
562     /**
563      * HTML attributes
564      * @var array
565      */
566     var $attributes;
567     /**
568      * List of actions attached to link
569      * @var array of component_action
570      */
571     var $actions;
573     /**
574      * Constructor
575      * @param string|moodle_url $url
576      * @param string $text HTML fragment
577      * @param component_action $action
578      * @param array $attributes associative array of html link attributes + disabled
579      */
580     public function __construct(moodle_url $url, $text, component_action $action=null, array $attributes=null) {
581         $this->url       = clone($url);
582         $this->text      = $text;
583         $this->attributes = (array)$attributes;
584         if ($action) {
585             $this->add_action($action);
586         }
587     }
589     /**
590      * Add action to the link.
591      * @param component_action $action
592      * @return void
593      */
594     public function add_action(component_action $action) {
595         $this->actions[] = $action;
596     }
598     public function add_class($class) {
599         if (empty($this->attributes['class'])) {
600             $this->attributes['class'] = $class;
601         } else {
602             $this->attributes['class'] .= ' ' . $class;
603         }
604     }
607 // ==== HTML writer and helper classes, will be probably moved elsewhere ======
609 /**
610  * Simple html output class
611  * @copyright 2009 Tim Hunt, 2010 Petr Skoda
612  */
613 class html_writer {
614     /**
615      * Outputs a tag with attributes and contents
616      * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
617      * @param string $contents What goes between the opening and closing tags
618      * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
619      * @return string HTML fragment
620      */
621     public static function tag($tagname, $contents, array $attributes = null) {
622         return self::start_tag($tagname, $attributes) . $contents . self::end_tag($tagname);
623     }
625     /**
626      * Outputs an opening tag with attributes
627      * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
628      * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
629      * @return string HTML fragment
630      */
631     public static function start_tag($tagname, array $attributes = null) {
632         return '<' . $tagname . self::attributes($attributes) . '>';
633     }
635     /**
636      * Outputs a closing tag
637      * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
638      * @return string HTML fragment
639      */
640     public static function end_tag($tagname) {
641         return '</' . $tagname . '>';
642     }
644     /**
645      * Outputs an empty tag with attributes
646      * @param string $tagname The name of tag ('input', 'img', 'br' etc.)
647      * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
648      * @return string HTML fragment
649      */
650     public static function empty_tag($tagname, array $attributes = null) {
651         return '<' . $tagname . self::attributes($attributes) . ' />';
652     }
654     /**
655      * Outputs a HTML attribute and value
656      * @param string $name The name of the attribute ('src', 'href', 'class' etc.)
657      * @param string $value The value of the attribute. The value will be escaped with {@link s()}
658      * @return string HTML fragment
659      */
660     public static function attribute($name, $value) {
661         if (is_array($value)) {
662             debugging("Passed an array for the HTML attribute $name", DEBUG_DEVELOPER);
663         }
664         if ($value instanceof moodle_url) {
665             return ' ' . $name . '="' . $value->out() . '"';
666         }
668         // special case, we do not want these in output
669         if ($value === null) {
670             return '';
671         }
673         // no sloppy trimming here!
674         return ' ' . $name . '="' . s($value) . '"';
675     }
677     /**
678      * Outputs a list of HTML attributes and values
679      * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
680      *       The values will be escaped with {@link s()}
681      * @return string HTML fragment
682      */
683     public static function attributes(array $attributes = null) {
684         $attributes = (array)$attributes;
685         $output = '';
686         foreach ($attributes as $name => $value) {
687             $output .= self::attribute($name, $value);
688         }
689         return $output;
690     }
692     /**
693      * Generates random html element id.
694      * @param string $base
695      * @return string
696      */
697     public static function random_id($base='random') {
698         return uniqid($base);
699     }
701     /**
702      * Generates a simple html link
703      * @param string|moodle_url $url
704      * @param string $text link txt
705      * @param array $attributes extra html attributes
706      * @return string HTML fragment
707      */
708     public static function link($url, $text, array $attributes = null) {
709         $attributes = (array)$attributes;
710         $attributes['href']  = $url;
711         return self::tag('a', $text, $attributes);
712     }
714     /**
715      * generates a simple checkbox with optional label
716      * @param string $name
717      * @param string $value
718      * @param bool $checked
719      * @param string $label
720      * @param array $attributes
721      * @return string html fragment
722      */
723     public static function checkbox($name, $value, $checked = true, $label = '', array $attributes = null) {
724         $attributes = (array)$attributes;
725         $output = '';
727         if ($label !== '' and !is_null($label)) {
728             if (empty($attributes['id'])) {
729                 $attributes['id'] = self::random_id('checkbox_');
730             }
731         }
732         $attributes['type']    = 'checkbox';
733         $attributes['value']   = $value;
734         $attributes['name']    = $name;
735         $attributes['checked'] = $checked ? 'selected' : null;
737         $output .= self::empty_tag('input', $attributes);
739         if ($label !== '' and !is_null($label)) {
740             $output .= self::tag('label', $label, array('for'=>$attributes['id']));
741         }
743         return $output;
744     }
746     /**
747      * Generates a simple select yes/no form field
748      * @param string $name name of select element
749      * @param bool $selected
750      * @param array $attributes - html select element attributes
751      * @return string HRML fragment
752      */
753     public static function select_yes_no($name, $selected=true, array $attributes = null) {
754         $options = array('1'=>get_string('yes'), '0'=>get_string('no'));
755         return self::select($options, $name, $selected, null, $attributes);
756     }
758     /**
759      * Generates a simple select form field
760      * @param array $options associative array value=>label ex.:
761      *                array(1=>'One, 2=>Two)
762      *              it is also possible to specify optgroup as complex label array ex.:
763      *                array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
764      *                array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
765      * @param string $name name of select element
766      * @param string|array $selected value or arary of values depending on multiple attribute
767      * @param array|bool $nothing, add nothing selected option, or false of not added
768      * @param array $attributes - html select element attributes
769      * @return string HTML fragment
770      */
771     public static function select(array $options, $name, $selected = '', $nothing = array(''=>'choosedots'), array $attributes = null) {
772         $attributes = (array)$attributes;
773         if (is_array($nothing)) {
774             foreach ($nothing as $k=>$v) {
775                 if ($v === 'choose' or $v === 'choosedots') {
776                     $nothing[$k] = get_string('choosedots');
777                 }
778             }
779             $options = $nothing + $options; // keep keys, do not override
781         } else if (is_string($nothing) and $nothing !== '') {
782             // BC
783             $options = array(''=>$nothing) + $options;
784         }
786         // we may accept more values if multiple attribute specified
787         $selected = (array)$selected;
788         foreach ($selected as $k=>$v) {
789             $selected[$k] = (string)$v;
790         }
792         if (!isset($attributes['id'])) {
793             $id = 'menu'.$name;
794             // name may contaion [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading
795             $id = str_replace('[', '', $id);
796             $id = str_replace(']', '', $id);
797             $attributes['id'] = $id;
798         }
800         if (!isset($attributes['class'])) {
801             $class = 'menu'.$name;
802             // name may contaion [], which would make an invalid class. e.g. numeric question type editing form, assignment quickgrading
803             $class = str_replace('[', '', $class);
804             $class = str_replace(']', '', $class);
805             $attributes['class'] = $class;
806         }
807         $attributes['class'] = 'select ' . $attributes['class']; /// Add 'select' selector always
809         $attributes['name'] = $name;
811         $output = '';
812         foreach ($options as $value=>$label) {
813             if (is_array($label)) {
814                 // ignore key, it just has to be unique
815                 $output .= self::select_optgroup(key($label), current($label), $selected);
816             } else {
817                 $output .= self::select_option($label, $value, $selected);
818             }
819         }
820         return self::tag('select', $output, $attributes);
821     }
823     private static function select_option($label, $value, array $selected) {
824         $attributes = array();
825         $value = (string)$value;
826         if (in_array($value, $selected, true)) {
827             $attributes['selected'] = 'selected';
828         }
829         $attributes['value'] = $value;
830         return self::tag('option', $label, $attributes);
831     }
833     private static function select_optgroup($groupname, $options, array $selected) {
834         if (empty($options)) {
835             return '';
836         }
837         $attributes = array('label'=>$groupname);
838         $output = '';
839         foreach ($options as $value=>$label) {
840             $output .= self::select_option($label, $value, $selected);
841         }
842         return self::tag('optgroup', $output, $attributes);
843     }
845     /**
846      * This is a shortcut for making an hour selector menu.
847      * @param string $type The type of selector (years, months, days, hours, minutes)
848      * @param string $name fieldname
849      * @param int $currenttime A default timestamp in GMT
850      * @param int $step minute spacing
851      * @param array $attributes - html select element attributes
852      * @return HTML fragment
853      */
854     public static function select_time($type, $name, $currenttime=0, $step=5, array $attributes=null) {
855         if (!$currenttime) {
856             $currenttime = time();
857         }
858         $currentdate = usergetdate($currenttime);
859         $userdatetype = $type;
860         $timeunits = array();
862         switch ($type) {
863             case 'years':
864                 for ($i=1970; $i<=2020; $i++) {
865                     $timeunits[$i] = $i;
866                 }
867                 $userdatetype = 'year';
868                 break;
869             case 'months':
870                 for ($i=1; $i<=12; $i++) {
871                     $timeunits[$i] = userdate(gmmktime(12,0,0,$i,15,2000), "%B");
872                 }
873                 $userdatetype = 'month';
874                 $currentdate['month'] = $currentdate['mon'];
875                 break;
876             case 'days':
877                 for ($i=1; $i<=31; $i++) {
878                     $timeunits[$i] = $i;
879                 }
880                 $userdatetype = 'mday';
881                 break;
882             case 'hours':
883                 for ($i=0; $i<=23; $i++) {
884                     $timeunits[$i] = sprintf("%02d",$i);
885                 }
886                 break;
887             case 'minutes':
888                 if ($step != 1) {
889                     $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
890                 }
892                 for ($i=0; $i<=59; $i+=$step) {
893                     $timeunits[$i] = sprintf("%02d",$i);
894                 }
895                 break;
896             default:
897                 throw new coding_exception("Time type $type is not supported by html_writer::select_time().");
898         }
900         if (empty($attributes['id'])) {
901             $attributes['id'] = self::random_id('ts_');
902         }
903         $timerselector = self::select($timeunits, $name, $currentdate[$userdatetype], null, array('id'=>$attributes['id']));
904         $label = self::tag('label', get_string(substr($type, 0, -1), 'form'), array('for'=>$attributes['id'], 'class'=>'accesshide'));
906         return $label.$timerselector;
907     }
909     /**
910      * Shortcut for quick making of lists
911      * @param array $items
912      * @param string $tag ul or ol
913      * @param array $attributes
914      * @return string
915      */
916     public static function alist(array $items, array $attributes = null, $tag = 'ul') {
917         //note: 'list' is a reserved keyword ;-)
919         $output = '';
921         foreach ($items as $item) {
922             $output .= html_writer::start_tag('li') . "\n";
923             $output .= $item . "\n";
924             $output .= html_writer::end_tag('li') . "\n";
925         }
927         return html_writer::tag($tag, $output, $attributes);
928     }
930     /**
931      * Returns hidden input fields created from url parameters.
932      * @param moodle_url $url
933      * @param array $exclude list of excluded parameters
934      * @return string HTML fragment
935      */
936     public static function input_hidden_params(moodle_url $url, array $exclude = null) {
937         $exclude = (array)$exclude;
938         $params = $url->params();
939         foreach ($exclude as $key) {
940             unset($params[$key]);
941         }
943         $output = '';
944         foreach ($params as $key => $value) {
945             $attributes = array('type'=>'hidden', 'name'=>$key, 'value'=>$value);
946             $output .= self::empty_tag('input', $attributes)."\n";
947         }
948         return $output;
949     }
951     /**
952      * Generate a script tag containing the the specified code.
953      *
954      * @param string $js the JavaScript code
955          * @param moodle_url|string optional url of the external script, $code ignored if specified
956      * @return string HTML, the code wrapped in <script> tags.
957      */
958     public static function script($jscode, $url=null) {
959         if ($jscode) {
960             $attributes = array('type'=>'text/javascript');
961             return self::tag('script', "\n//<![CDATA[\n$jscode\n//]]>\n", $attributes) . "\n";
963         } else if ($url) {
964             $attributes = array('type'=>'text/javascript', 'src'=>$url);
965             return self::tag('script', '', $attributes) . "\n";
967         } else {
968             return '';
969         }
970     }
972     /**
973      * Renders HTML table
974      *
975      * This method may modify the passed instance by adding some default properties if they are not set yet.
976      * If this is not what you want, you should make a full clone of your data before passing them to this
977      * method. In most cases this is not an issue at all so we do not clone by default for performance
978      * and memory consumption reasons.
979      *
980      * @param html_table $table data to be rendered
981      * @return string HTML code
982      */
983     public static function table(html_table $table) {
984         // prepare table data and populate missing properties with reasonable defaults
985         if (!empty($table->align)) {
986             foreach ($table->align as $key => $aa) {
987                 if ($aa) {
988                     $table->align[$key] = 'text-align:'. fix_align_rtl($aa) .';';  // Fix for RTL languages
989                 } else {
990                     $table->align[$key] = null;
991                 }
992             }
993         }
994         if (!empty($table->size)) {
995             foreach ($table->size as $key => $ss) {
996                 if ($ss) {
997                     $table->size[$key] = 'width:'. $ss .';';
998                 } else {
999                     $table->size[$key] = null;
1000                 }
1001             }
1002         }
1003         if (!empty($table->wrap)) {
1004             foreach ($table->wrap as $key => $ww) {
1005                 if ($ww) {
1006                     $table->wrap[$key] = 'white-space:nowrap;';
1007                 } else {
1008                     $table->wrap[$key] = '';
1009                 }
1010             }
1011         }
1012         if (!empty($table->head)) {
1013             foreach ($table->head as $key => $val) {
1014                 if (!isset($table->align[$key])) {
1015                     $table->align[$key] = null;
1016                 }
1017                 if (!isset($table->size[$key])) {
1018                     $table->size[$key] = null;
1019                 }
1020                 if (!isset($table->wrap[$key])) {
1021                     $table->wrap[$key] = null;
1022                 }
1024             }
1025         }
1026         if (empty($table->attributes['class'])) {
1027             $table->attributes['class'] = 'generaltable';
1028         }
1029         if (!empty($table->tablealign)) {
1030             $table->attributes['class'] .= ' boxalign' . $table->tablealign;
1031         }
1033         // explicitly assigned properties override those defined via $table->attributes
1034         $table->attributes['class'] = trim($table->attributes['class']);
1035         $attributes = array_merge($table->attributes, array(
1036                 'id'            => $table->id,
1037                 'width'         => $table->width,
1038                 'summary'       => $table->summary,
1039                 'cellpadding'   => $table->cellpadding,
1040                 'cellspacing'   => $table->cellspacing,
1041             ));
1042         $output = html_writer::start_tag('table', $attributes) . "\n";
1044         $countcols = 0;
1046         if (!empty($table->head)) {
1047             $countcols = count($table->head);
1048             $output .= html_writer::start_tag('thead', array()) . "\n";
1049             $output .= html_writer::start_tag('tr', array()) . "\n";
1050             $keys = array_keys($table->head);
1051             $lastkey = end($keys);
1053             foreach ($table->head as $key => $heading) {
1054                 // Convert plain string headings into html_table_cell objects
1055                 if (!($heading instanceof html_table_cell)) {
1056                     $headingtext = $heading;
1057                     $heading = new html_table_cell();
1058                     $heading->text = $headingtext;
1059                     $heading->header = true;
1060                 }
1062                 if ($heading->header !== false) {
1063                     $heading->header = true;
1064                 }
1066                 if ($heading->header && empty($heading->scope)) {
1067                     $heading->scope = 'col';
1068                 }
1070                 $heading->attributes['class'] .= ' header c' . $key;
1071                 if (isset($table->headspan[$key]) && $table->headspan[$key] > 1) {
1072                     $heading->colspan = $table->headspan[$key];
1073                     $countcols += $table->headspan[$key] - 1;
1074                 }
1076                 if ($key == $lastkey) {
1077                     $heading->attributes['class'] .= ' lastcol';
1078                 }
1079                 if (isset($table->colclasses[$key])) {
1080                     $heading->attributes['class'] .= ' ' . $table->colclasses[$key];
1081                 }
1082                 $heading->attributes['class'] = trim($heading->attributes['class']);
1083                 $attributes = array_merge($heading->attributes, array(
1084                         'style'     => $table->align[$key] . $table->size[$key] . $heading->style,
1085                         'scope'     => $heading->scope,
1086                         'colspan'   => $heading->colspan,
1087                     ));
1089                 $tagtype = 'td';
1090                 if ($heading->header === true) {
1091                     $tagtype = 'th';
1092                 }
1093                 $output .= html_writer::tag($tagtype, $heading->text, $attributes) . "\n";
1094             }
1095             $output .= html_writer::end_tag('tr') . "\n";
1096             $output .= html_writer::end_tag('thead') . "\n";
1098             if (empty($table->data)) {
1099                 // For valid XHTML strict every table must contain either a valid tr
1100                 // or a valid tbody... both of which must contain a valid td
1101                 $output .= html_writer::start_tag('tbody', array('class' => 'empty'));
1102                 $output .= html_writer::tag('tr', html_writer::tag('td', '', array('colspan'=>count($table->head))));
1103                 $output .= html_writer::end_tag('tbody');
1104             }
1105         }
1107         if (!empty($table->data)) {
1108             $oddeven    = 1;
1109             $keys       = array_keys($table->data);
1110             $lastrowkey = end($keys);
1111             $output .= html_writer::start_tag('tbody', array());
1113             foreach ($table->data as $key => $row) {
1114                 if (($row === 'hr') && ($countcols)) {
1115                     $output .= html_writer::tag('td', html_writer::tag('div', '', array('class' => 'tabledivider')), array('colspan' => $countcols));
1116                 } else {
1117                     // Convert array rows to html_table_rows and cell strings to html_table_cell objects
1118                     if (!($row instanceof html_table_row)) {
1119                         $newrow = new html_table_row();
1121                         foreach ($row as $item) {
1122                             $cell = new html_table_cell();
1123                             $cell->text = $item;
1124                             $newrow->cells[] = $cell;
1125                         }
1126                         $row = $newrow;
1127                     }
1129                     $oddeven = $oddeven ? 0 : 1;
1130                     if (isset($table->rowclasses[$key])) {
1131                         $row->attributes['class'] .= ' ' . $table->rowclasses[$key];
1132                     }
1134                     $row->attributes['class'] .= ' r' . $oddeven;
1135                     if ($key == $lastrowkey) {
1136                         $row->attributes['class'] .= ' lastrow';
1137                     }
1139                     $output .= html_writer::start_tag('tr', array('class' => trim($row->attributes['class']), 'style' => $row->style, 'id' => $row->id)) . "\n";
1140                     $keys2 = array_keys($row->cells);
1141                     $lastkey = end($keys2);
1143                     foreach ($row->cells as $key => $cell) {
1144                         if (!($cell instanceof html_table_cell)) {
1145                             $mycell = new html_table_cell();
1146                             $mycell->text = $cell;
1147                             $cell = $mycell;
1148                         }
1150                         if (($cell->header === true) && empty($cell->scope)) {
1151                             $cell->scope = 'row';
1152                         }
1154                         if (isset($table->colclasses[$key])) {
1155                             $cell->attributes['class'] .= ' ' . $table->colclasses[$key];
1156                         }
1158                         $cell->attributes['class'] .= ' cell c' . $key;
1159                         if ($key == $lastkey) {
1160                             $cell->attributes['class'] .= ' lastcol';
1161                         }
1162                         $tdstyle = '';
1163                         $tdstyle .= isset($table->align[$key]) ? $table->align[$key] : '';
1164                         $tdstyle .= isset($table->size[$key]) ? $table->size[$key] : '';
1165                         $tdstyle .= isset($table->wrap[$key]) ? $table->wrap[$key] : '';
1166                         $cell->attributes['class'] = trim($cell->attributes['class']);
1167                         $tdattributes = array_merge($cell->attributes, array(
1168                                 'style' => $tdstyle . $cell->style,
1169                                 'colspan' => $cell->colspan,
1170                                 'rowspan' => $cell->rowspan,
1171                                 'id' => $cell->id,
1172                                 'abbr' => $cell->abbr,
1173                                 'scope' => $cell->scope,
1174                             ));
1175                         $tagtype = 'td';
1176                         if ($cell->header === true) {
1177                             $tagtype = 'th';
1178                         }
1179                         $output .= html_writer::tag($tagtype, $cell->text, $tdattributes) . "\n";
1180                     }
1181                 }
1182                 $output .= html_writer::end_tag('tr') . "\n";
1183             }
1184             $output .= html_writer::end_tag('tbody') . "\n";
1185         }
1186         $output .= html_writer::end_tag('table') . "\n";
1188         return $output;
1189     }
1193 // ==== JS writer and helper classes, will be probably moved elsewhere ======
1195 /**
1196  * Simple javascript output class
1197  * @copyright 2010 Petr Skoda
1198  */
1199 class js_writer {
1200     /**
1201      * Returns javascript code calling the function
1202      * @param string $function function name, can be complex lin Y.Event.purgeElement
1203      * @param array $arguments parameters
1204      * @param int $delay execution delay in seconds
1205      * @return string JS code fragment
1206      */
1207     public function function_call($function, array $arguments = null, $delay=0) {
1208         if ($arguments) {
1209             $arguments = array_map('json_encode', $arguments);
1210             $arguments = implode(', ', $arguments);
1211         } else {
1212             $arguments = '';
1213         }
1214         $js = "$function($arguments);";
1216         if ($delay) {
1217             $delay = $delay * 1000; // in miliseconds
1218             $js = "setTimeout(function() { $js }, $delay);";
1219         }
1220         return $js . "\n";
1221     }
1223     /**
1224      * Special function which adds Y as first argument of fucntion call.
1225      * @param string $function
1226      * @param array $extraarguments
1227      * @return string
1228      */
1229     public function function_call_with_Y($function, array $extraarguments = null) {
1230         if ($extraarguments) {
1231             $extraarguments = array_map('json_encode', $extraarguments);
1232             $arguments = 'Y, ' . implode(', ', $extraarguments);
1233         } else {
1234             $arguments = 'Y';
1235         }
1236         return "$function($arguments);\n";
1237     }
1239     /**
1240      * Returns JavaScript code to initialise a new object
1241      * @param string|null $var If it is null then no var is assigned the new object
1242      * @param string $class
1243      * @param array $arguments
1244      * @param array $requirements
1245      * @param int $delay
1246      * @return string
1247      */
1248     public function object_init($var, $class, array $arguments = null, array $requirements = null, $delay=0) {
1249         if (is_array($arguments)) {
1250             $arguments = array_map('json_encode', $arguments);
1251             $arguments = implode(', ', $arguments);
1252         }
1254         if ($var === null) {
1255             $js = "new $class(Y, $arguments);";
1256         } else if (strpos($var, '.')!==false) {
1257             $js = "$var = new $class(Y, $arguments);";
1258         } else {
1259             $js = "var $var = new $class(Y, $arguments);";
1260         }
1262         if ($delay) {
1263             $delay = $delay * 1000; // in miliseconds
1264             $js = "setTimeout(function() { $js }, $delay);";
1265         }
1267         if (count($requirements) > 0) {
1268             $requirements = implode("', '", $requirements);
1269             $js = "Y.use('$requirements', function(Y){ $js });";
1270         }
1271         return $js."\n";
1272     }
1274     /**
1275      * Returns code setting value to variable
1276      * @param string $name
1277      * @param mixed $value json serialised value
1278      * @param bool $usevar add var definition, ignored for nested properties
1279      * @return string JS code fragment
1280      */
1281     public function set_variable($name, $value, $usevar=true) {
1282         $output = '';
1284         if ($usevar) {
1285             if (strpos($name, '.')) {
1286                 $output .= '';
1287             } else {
1288                 $output .= 'var ';
1289             }
1290         }
1292         $output .= "$name = ".json_encode($value).";";
1294         return $output;
1295     }
1297     /**
1298      * Writes event handler attaching code
1299      * @param mixed $selector standard YUI selector for elemnts, may be array or string, element id is in the form "#idvalue"
1300      * @param string $event A valid DOM event (click, mousedown, change etc.)
1301      * @param string $function The name of the function to call
1302      * @param array  $arguments An optional array of argument parameters to pass to the function
1303      * @return string JS code fragment
1304      */
1305     public function event_handler($selector, $event, $function, array $arguments = null) {
1306         $selector = json_encode($selector);
1307         $output = "Y.on('$event', $function, $selector, null";
1308         if (!empty($arguments)) {
1309             $output .= ', ' . json_encode($arguments);
1310         }
1311         return $output . ");\n";
1312     }
1315 /**
1316  * Holds all the information required to render a <table> by {@see core_renderer::table()}
1317  *
1318  * Example of usage:
1319  * $t = new html_table();
1320  * ... // set various properties of the object $t as described below
1321  * echo html_writer::table($t);
1322  *
1323  * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
1324  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1325  * @since     Moodle 2.0
1326  */
1327 class html_table {
1328     /**
1329      * @var string value to use for the id attribute of the table
1330      */
1331     public $id = null;
1332     /**
1333      * @var array attributes of HTML attributes for the <table> element
1334      */
1335     public $attributes = array();
1336     /**
1337      * For more control over the rendering of the headers, an array of html_table_cell objects
1338      * can be passed instead of an array of strings.
1339      * @var array of headings. The n-th array item is used as a heading of the n-th column.
1340      *
1341      * Example of usage:
1342      * $t->head = array('Student', 'Grade');
1343      */
1344     public $head;
1345     /**
1346      * @var array can be used to make a heading span multiple columns
1347      *
1348      * Example of usage:
1349      * $t->headspan = array(2,1);
1350      *
1351      * In this example, {@see html_table:$data} is supposed to have three columns. For the first two columns,
1352      * the same heading is used. Therefore, {@see html_table::$head} should consist of two items.
1353      */
1354     public $headspan;
1355     /**
1356      * @var array of column alignments. The value is used as CSS 'text-align' property. Therefore, possible
1357      * values are 'left', 'right', 'center' and 'justify'. Specify 'right' or 'left' from the perspective
1358      * of a left-to-right (LTR) language. For RTL, the values are flipped automatically.
1359      *
1360      * Examples of usage:
1361      * $t->align = array(null, 'right');
1362      * or
1363      * $t->align[1] = 'right';
1364      *
1365      */
1366     public $align;
1367     /**
1368      * @var array of column sizes. The value is used as CSS 'size' property.
1369      *
1370      * Examples of usage:
1371      * $t->size = array('50%', '50%');
1372      * or
1373      * $t->size[1] = '120px';
1374      */
1375     public $size;
1376     /**
1377      * @var array of wrapping information. The only possible value is 'nowrap' that sets the
1378      * CSS property 'white-space' to the value 'nowrap' in the given column.
1379      *
1380      * Example of usage:
1381      * $t->wrap = array(null, 'nowrap');
1382      */
1383     public $wrap;
1384     /**
1385      * @var array of arrays or html_table_row objects containing the data. Alternatively, if you have
1386      * $head specified, the string 'hr' (for horizontal ruler) can be used
1387      * instead of an array of cells data resulting in a divider rendered.
1388      *
1389      * Example of usage with array of arrays:
1390      * $row1 = array('Harry Potter', '76 %');
1391      * $row2 = array('Hermione Granger', '100 %');
1392      * $t->data = array($row1, $row2);
1393      *
1394      * Example with array of html_table_row objects: (used for more fine-grained control)
1395      * $cell1 = new html_table_cell();
1396      * $cell1->text = 'Harry Potter';
1397      * $cell1->colspan = 2;
1398      * $row1 = new html_table_row();
1399      * $row1->cells[] = $cell1;
1400      * $cell2 = new html_table_cell();
1401      * $cell2->text = 'Hermione Granger';
1402      * $cell3 = new html_table_cell();
1403      * $cell3->text = '100 %';
1404      * $row2 = new html_table_row();
1405      * $row2->cells = array($cell2, $cell3);
1406      * $t->data = array($row1, $row2);
1407      */
1408     public $data;
1409     /**
1410      * @var string width of the table, percentage of the page preferred. Defaults to 80%
1411      * @deprecated since Moodle 2.0. Styling should be in the CSS.
1412      */
1413     public $width = null;
1414     /**
1415      * @var string alignment the whole table. Can be 'right', 'left' or 'center' (default).
1416      * @deprecated since Moodle 2.0. Styling should be in the CSS.
1417      */
1418     public $tablealign = null;
1419     /**
1420      * @var int padding on each cell, in pixels
1421      * @deprecated since Moodle 2.0. Styling should be in the CSS.
1422      */
1423     public $cellpadding = null;
1424     /**
1425      * @var int spacing between cells, in pixels
1426      * @deprecated since Moodle 2.0. Styling should be in the CSS.
1427      */
1428     public $cellspacing = null;
1429     /**
1430      * @var array classes to add to particular rows, space-separated string.
1431      * Classes 'r0' or 'r1' are added automatically for every odd or even row,
1432      * respectively. Class 'lastrow' is added automatically for the last row
1433      * in the table.
1434      *
1435      * Example of usage:
1436      * $t->rowclasses[9] = 'tenth'
1437      */
1438     public $rowclasses;
1439     /**
1440      * @var array classes to add to every cell in a particular column,
1441      * space-separated string. Class 'cell' is added automatically by the renderer.
1442      * Classes 'c0' or 'c1' are added automatically for every odd or even column,
1443      * respectively. Class 'lastcol' is added automatically for all last cells
1444      * in a row.
1445      *
1446      * Example of usage:
1447      * $t->colclasses = array(null, 'grade');
1448      */
1449     public $colclasses;
1450     /**
1451      * @var string description of the contents for screen readers.
1452      */
1453     public $summary;
1455     /**
1456      * Constructor
1457      */
1458     public function __construct() {
1459         $this->attributes['class'] = '';
1460     }
1464 /**
1465  * Component representing a table row.
1466  *
1467  * @copyright 2009 Nicolas Connault
1468  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1469  * @since     Moodle 2.0
1470  */
1471 class html_table_row {
1472     /**
1473      * @var string value to use for the id attribute of the row
1474      */
1475     public $id = null;
1476     /**
1477      * @var array $cells Array of html_table_cell objects
1478      */
1479     public $cells = array();
1480     /**
1481      * @var string $style value to use for the style attribute of the table row
1482      */
1483     public $style = null;
1484     /**
1485      * @var array attributes of additional HTML attributes for the <tr> element
1486      */
1487     public $attributes = array();
1489     /**
1490      * Constructor
1491      * @param array $cells
1492      */
1493     public function __construct(array $cells=null) {
1494         $this->attributes['class'] = '';
1495         $cells = (array)$cells;
1496         foreach ($cells as $cell) {
1497             if ($cell instanceof html_table_cell) {
1498                 $this->cells[] = $cell;
1499             } else {
1500                 $this->cells[] = new html_table_cell($cell);
1501             }
1502         }
1503     }
1507 /**
1508  * Component representing a table cell.
1509  *
1510  * @copyright 2009 Nicolas Connault
1511  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1512  * @since     Moodle 2.0
1513  */
1514 class html_table_cell {
1515     /**
1516      * @var string value to use for the id attribute of the cell
1517      */
1518     public $id = null;
1519     /**
1520      * @var string $text The contents of the cell
1521      */
1522     public $text;
1523     /**
1524      * @var string $abbr Abbreviated version of the contents of the cell
1525      */
1526     public $abbr = null;
1527     /**
1528      * @var int $colspan Number of columns this cell should span
1529      */
1530     public $colspan = null;
1531     /**
1532      * @var int $rowspan Number of rows this cell should span
1533      */
1534     public $rowspan = null;
1535     /**
1536      * @var string $scope Defines a way to associate header cells and data cells in a table
1537      */
1538     public $scope = null;
1539     /**
1540      * @var boolean $header Whether or not this cell is a header cell
1541      */
1542     public $header = null;
1543     /**
1544      * @var string $style value to use for the style attribute of the table cell
1545      */
1546     public $style = null;
1547     /**
1548      * @var array attributes of additional HTML attributes for the <tr> element
1549      */
1550     public $attributes = array();
1552     public function __construct($text = null) {
1553         $this->text = $text;
1554         $this->attributes['class'] = '';
1555     }
1559 /// Complex components aggregating simpler components
1562 /**
1563  * Component representing a paging bar.
1564  *
1565  * @copyright 2009 Nicolas Connault
1566  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1567  * @since     Moodle 2.0
1568  */
1569 class paging_bar implements renderable {
1570     /**
1571      * @var int $maxdisplay The maximum number of pagelinks to display
1572      */
1573     public $maxdisplay = 18;
1574     /**
1575      * @var int $totalcount post or get
1576      */
1577     public $totalcount;
1578     /**
1579      * @var int $page The page you are currently viewing
1580      */
1581     public $page;
1582     /**
1583      * @var int $perpage The number of entries that should be shown per page
1584      */
1585     public $perpage;
1586     /**
1587      * @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.
1588      *      If this is a moodle_url object then the pagevar param will be replaced by the page no, for each page.
1589      */
1590     public $baseurl;
1591     /**
1592      * @var string $pagevar This is the variable name that you use for the page number in your code (ie. 'tablepage', 'blogpage', etc)
1593      */
1594     public $pagevar;
1595     /**
1596      * @var string $previouslink A HTML link representing the "previous" page
1597      */
1598     public $previouslink = null;
1599     /**
1600      * @var tring $nextlink A HTML link representing the "next" page
1601      */
1602     public $nextlink = null;
1603     /**
1604      * @var tring $firstlink A HTML link representing the first page
1605      */
1606     public $firstlink = null;
1607     /**
1608      * @var tring $lastlink A HTML link representing the last page
1609      */
1610     public $lastlink = null;
1611     /**
1612      * @var array $pagelinks An array of strings. One of them is just a string: the current page
1613      */
1614     public $pagelinks = array();
1616     /**
1617      * Constructor paging_bar with only the required params.
1618      *
1619      * @param int $totalcount Thetotal number of entries available to be paged through
1620      * @param int $page The page you are currently viewing
1621      * @param int $perpage The number of entries that should be shown per page
1622      * @param string|moodle_url $baseurl url of the current page, the $pagevar parameter is added
1623      * @param string $pagevar name of page parameter that holds the page number
1624      */
1625     public function __construct($totalcount, $page, $perpage, $baseurl, $pagevar = 'page') {
1626         $this->totalcount = $totalcount;
1627         $this->page       = $page;
1628         $this->perpage    = $perpage;
1629         $this->baseurl    = $baseurl;
1630         $this->pagevar    = $pagevar;
1631     }
1633     /**
1634      * @return void
1635      */
1636     public function prepare(renderer_base $output, moodle_page $page, $target) {
1637         if (!isset($this->totalcount) || is_null($this->totalcount)) {
1638             throw new coding_exception('paging_bar requires a totalcount value.');
1639         }
1640         if (!isset($this->page) || is_null($this->page)) {
1641             throw new coding_exception('paging_bar requires a page value.');
1642         }
1643         if (empty($this->perpage)) {
1644             throw new coding_exception('paging_bar requires a perpage value.');
1645         }
1646         if (empty($this->baseurl)) {
1647             throw new coding_exception('paging_bar requires a baseurl value.');
1648         }
1650         if ($this->totalcount > $this->perpage) {
1651             $pagenum = $this->page - 1;
1653             if ($this->page > 0) {
1654                 $this->previouslink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$pagenum)), get_string('previous'), array('class'=>'previous'));
1655             }
1657             if ($this->perpage > 0) {
1658                 $lastpage = ceil($this->totalcount / $this->perpage);
1659             } else {
1660                 $lastpage = 1;
1661             }
1663             if ($this->page > 15) {
1664                 $startpage = $this->page - 10;
1666                 $this->firstlink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>0)), '1', array('class'=>'first'));
1667             } else {
1668                 $startpage = 0;
1669             }
1671             $currpage = $startpage;
1672             $displaycount = $displaypage = 0;
1674             while ($displaycount < $this->maxdisplay and $currpage < $lastpage) {
1675                 $displaypage = $currpage + 1;
1677                 if ($this->page == $currpage) {
1678                     $this->pagelinks[] = $displaypage;
1679                 } else {
1680                     $pagelink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$currpage)), $displaypage);
1681                     $this->pagelinks[] = $pagelink;
1682                 }
1684                 $displaycount++;
1685                 $currpage++;
1686             }
1688             if ($currpage < $lastpage) {
1689                 $lastpageactual = $lastpage - 1;
1690                 $this->lastlink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$lastpageactual)), $lastpage, array('class'=>'last'));
1691             }
1693             $pagenum = $this->page + 1;
1695             if ($pagenum != $displaypage) {
1696                 $this->nextlink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$pagenum)), get_string('next'), array('class'=>'next'));
1697             }
1698         }
1699     }
1703 /**
1704  * This class represents how a block appears on a page.
1705  *
1706  * During output, each block instance is asked to return a block_contents object,
1707  * those are then passed to the $OUTPUT->block function for display.
1708  *
1709  * {@link $contents} should probably be generated using a moodle_block_..._renderer.
1710  *
1711  * Other block-like things that need to appear on the page, for example the
1712  * add new block UI, are also represented as block_contents objects.
1713  *
1714  * @copyright 2009 Tim Hunt
1715  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1716  * @since     Moodle 2.0
1717  */
1718 class block_contents {
1719     /** @var int used to set $skipid. */
1720     protected static $idcounter = 1;
1722     const NOT_HIDEABLE = 0;
1723     const VISIBLE = 1;
1724     const HIDDEN = 2;
1726     /**
1727      * @var integer $skipid All the blocks (or things that look like blocks)
1728      * printed on a page are given a unique number that can be used to construct
1729      * id="" attributes. This is set automatically be the {@link prepare()} method.
1730      * Do not try to set it manually.
1731      */
1732     public $skipid;
1734     /**
1735      * @var integer If this is the contents of a real block, this should be set to
1736      * the block_instance.id. Otherwise this should be set to 0.
1737      */
1738     public $blockinstanceid = 0;
1740     /**
1741      * @var integer if this is a real block instance, and there is a corresponding
1742      * block_position.id for the block on this page, this should be set to that id.
1743      * Otherwise it should be 0.
1744      */
1745     public $blockpositionid = 0;
1747     /**
1748      * @param array $attributes an array of attribute => value pairs that are put on the
1749      * outer div of this block. {@link $id} and {@link $classes} attributes should be set separately.
1750      */
1751     public $attributes;
1753     /**
1754      * @param string $title The title of this block. If this came from user input,
1755      * it should already have had format_string() processing done on it. This will
1756      * be output inside <h2> tags. Please do not cause invalid XHTML.
1757      */
1758     public $title = '';
1760     /**
1761      * @param string $content HTML for the content
1762      */
1763     public $content = '';
1765     /**
1766      * @param array $list an alternative to $content, it you want a list of things with optional icons.
1767      */
1768     public $footer = '';
1770     /**
1771      * Any small print that should appear under the block to explain to the
1772      * teacher about the block, for example 'This is a sticky block that was
1773      * added in the system context.'
1774      * @var string
1775      */
1776     public $annotation = '';
1778     /**
1779      * @var integer one of the constants NOT_HIDEABLE, VISIBLE, HIDDEN. Whether
1780      * the user can toggle whether this block is visible.
1781      */
1782     public $collapsible = self::NOT_HIDEABLE;
1784     /**
1785      * A (possibly empty) array of editing controls. Each element of this array
1786      * should be an array('url' => $url, 'icon' => $icon, 'caption' => $caption).
1787      * $icon is the icon name. Fed to $OUTPUT->pix_url.
1788      * @var array
1789      */
1790     public $controls = array();
1793     /**
1794      * Create new instance of block content
1795      * @param array $attributes
1796      */
1797     public function __construct(array $attributes=null) {
1798         $this->skipid = self::$idcounter;
1799         self::$idcounter += 1;
1801         if ($attributes) {
1802             // standard block
1803             $this->attributes = $attributes;
1804         } else {
1805             // simple "fake" blocks used in some modules and "Add new block" block
1806             $this->attributes = array('class'=>'sideblock');
1807         }
1808     }
1810     /**
1811      * Add html class to block
1812      * @param string $class
1813      * @return void
1814      */
1815     public function add_class($class) {
1816         $this->attributes['class'] .= ' '.$class;
1817     }
1821 /**
1822  * This class represents a target for where a block can go when it is being moved.
1823  *
1824  * This needs to be rendered as a form with the given hidden from fields, and
1825  * clicking anywhere in the form should submit it. The form action should be
1826  * $PAGE->url.
1827  *
1828  * @copyright 2009 Tim Hunt
1829  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1830  * @since     Moodle 2.0
1831  */
1832 class block_move_target {
1833     /**
1834      * Move url
1835      * @var moodle_url
1836      */
1837     public $url;
1838     /**
1839      * label
1840      * @var string
1841      */
1842     public $text;
1844     /**
1845      * Cosntructor
1846      * @param string $text
1847      * @param moodle_url $url
1848      */
1849     public function __construct($text, moodle_url $url) {
1850         $this->text = $text;
1851         $this->url  = $url;
1852     }