Updated the HEAD build version to 20100604
[moodle.git] / lib / outputcomponents.php
CommitLineData
d9c8f425 1<?php
2
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/>.
17
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 */
28
5d0c95a5
PS
29
30/**
31 * Interface marking other classes as suitable for renderer_base::render()
32 * @author 2010 Petr Skoda (skodak) info@skodak.org
33 */
34interface renderable {
35 // intentionally empty
36}
37
b80ef420
DC
38/**
39 * Data structure representing a file tree viewer
40 *
41 * @copyright 2010 Dongsheng Cai
42 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
43 * @since Moodle 2.0
44 */
45class file_tree_viewer implements renderable {
46 public $dir;
47 public $result;
48 public $filearea;
49 /**
50 * Constructor of file_tree_viewer class
51 * @param int $contextid
52 * @param string $area, file area
53 * @param int $itemid
54 * @param string $urlbase, file serving url base
55 */
56 public function __construct($contextid, $area, $itemid, $urlbase='') {
57 global $CFG;
58 $fs = get_file_storage();
59 if (empty($urlbase)) {
60 $this->urlbase = "$CFG->wwwroot/pluginfile.php";
61 } else {
62 $this->urlbase = $urlbase;
63 }
64 $this->contextid = $contextid;
65 $this->filearea = $area;
66 $this->itemid = $itemid;
67 $this->dir = $fs->get_area_tree($contextid, $area, $itemid);
68 $this->tree_view_parser($this->dir);
69 }
70 /**
71 * Pre-process file tree, generate file url
72 * @param array $dir file tree
73 */
74 public function tree_view_parser($dir) {
75 if (empty($dir['subdirs']) and empty($dir['files'])) {
76 return null;
77 }
78 foreach ($dir['subdirs'] as $subdir) {
79 $this->tree_view_parser($subdir);
80 }
81 foreach ($dir['files'] as $file) {
82 $path = '/'.$this->contextid.'/'.$this->filearea.'/'.$this->itemid.$file->get_filepath().$file->get_filename();
83 $downloadurl = file_encode_url($this->urlbase, $path, true);
84 $file->fileurl = $downloadurl;
85 }
86 }
87}
bb496de7
DC
88
89/**
90 * Data structure representing a file picker.
91 *
92 * @copyright 2010 Dongsheng Cai
93 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
94 * @since Moodle 2.0
95 */
96class file_picker implements renderable {
97 public $options;
98 public function __construct(stdClass $options) {
99 global $CFG, $USER, $PAGE;
100 require_once($CFG->dirroot. '/repository/lib.php');
101 $defaults = array(
102 'accepted_types'=>'*',
103 'context'=>$PAGE->context,
104 'return_types'=>FILE_INTERNAL,
105 'env' => 'filepicker',
106 'client_id' => uniqid(),
107 'itemid' => 0,
108 'maxbytes'=>-1,
109 'maxfiles'=>1,
110 );
111 foreach ($defaults as $key=>$value) {
112 if (empty($options->$key)) {
113 $options->$key = $value;
114 }
115 }
116
117 $options->currentfile = '';
118 if (!empty($options->itemid)) {
119 $fs = get_file_storage();
120 $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
e4256380
DC
121 if (empty($options->filename)) {
122 if ($files = $fs->get_area_files($usercontext->id, 'user_draft', $options->itemid, 'id DESC', false)) {
123 $file = reset($files);
124 }
125 } else {
126 $file = $fs->get_file($usercontext->id, 'user_draft', $options->itemid, $options->filepath, $options->filename);
127 }
128 if (!empty($file)) {
bb496de7
DC
129 $options->currentfile = html_writer::link(file_encode_url($CFG->wwwroot.'/draftfile.php/', $usercontext->id.'/user_draft/'.$file->get_itemid().'/'.$file->get_filename()), $file->get_filename());
130 }
131 }
132
bb496de7
DC
133 // initilise options, getting files in root path
134 $this->options = initialise_filepicker($options);
135
136 // copying other options
137 foreach ($options as $name=>$value) {
138 $this->options->$name = $value;
139 }
140 }
141}
142
4d2ee4c2
DC
143/**
144 * Data structure representing a file manager.
145 *
146 * @copyright 2010 Dongsheng Cai
147 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
148 * @since Moodle 2.0
149 */
150class file_manager implements renderable {
151 public $options;
152 public function __construct(stdClass $options) {
153 global $CFG, $USER, $PAGE;
154 require_once($CFG->dirroot. '/repository/lib.php');
155 $defaults = array(
156 'maxbytes'=>-1,
157 'maxfiles'=>-1,
158 'filearea'=>'user_draft',
159 'itemid'=>0,
160 'subdirs'=>0,
161 'client_id'=>uniqid(),
162 'accepted_types'=>'*',
163 'return_types'=>FILE_INTERNAL,
164 'context'=>$PAGE->context
165 );
166 foreach ($defaults as $key=>$value) {
167 if (empty($options->$key)) {
168 $options->$key = $value;
169 }
170 }
171
172 $fs = get_file_storage();
173
174 // initilise options, getting files in root path
175 $this->options = file_get_user_area_files($options->itemid, '/', $options->filearea);
176
177 // calculate file count
178 $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
179 $files = $fs->get_area_files($usercontext->id, $options->filearea, $options->itemid, 'id', false);
180 $filecount = count($files);
181 $this->options->filecount = $filecount;
182
183 // copying other options
184 foreach ($options as $name=>$value) {
185 $this->options->$name = $value;
186 }
187
188 // building file picker options
189 $params = new stdclass;
190 $params->accepted_types = $options->accepted_types;
191 $params->return_types = $options->return_types;
192 $params->context = $options->context;
193 $params->env = 'filemanager';
6bf197b3 194 $params->disable_types = !empty($options->disable_types)?$options->disable_types:array();
4d2ee4c2
DC
195 $filepicker_options = initialise_filepicker($params);
196 $this->options->filepicker = $filepicker_options;
197 }
198}
5d0c95a5
PS
199
200/**
bf11293a 201 * Data structure representing a user picture.
5d0c95a5
PS
202 *
203 * @copyright 2009 Nicolas Connault, 2010 Petr Skoda
204 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
205 * @since Moodle 2.0
206 */
207class user_picture implements renderable {
208 /**
209 * List of mandatory fields in user record here.
210 * @var string
211 */
212 const FIELDS = 'id,picture,firstname,lastname,imagealt';
213
214 /**
215 * @var object $user A user object with at least fields id, picture, imagealt, firstname and lastname set.
216 */
217 public $user;
218 /**
219 * @var int $courseid The course id. Used when constructing the link to the user's profile,
220 * page course id used if not specified.
221 */
222 public $courseid;
223 /**
224 * @var bool $link add course profile link to image
225 */
226 public $link = true;
227 /**
228 * @var int $size Size in pixels. Special values are (true/1 = 100px) and (false/0 = 35px) for backward compatibility
229 */
230 public $size = 35;
231 /**
232 * @var boolean $alttext add non-blank alt-text to the image.
233 * Default true, set to false when image alt just duplicates text in screenreaders.
234 */
235 public $alttext = true;
236 /**
237 * @var boolean $popup Whether or not to open the link in a popup window.
238 */
239 public $popup = false;
240 /**
241 * @var string Image class attribute
242 */
243 public $class = 'userpicture';
244
245 /**
246 * User picture constructor.
247 *
248 * @param object $user user record with at least id, picture, imagealt, firstname and lastname set.
249 * @param array $options such as link, size, link, ...
250 */
251 public function __construct(stdClass $user) {
252 global $DB;
253
254 static $fields = null;
255 if (is_null($fields)) {
256 $fields = explode(',', self::FIELDS);
257 }
258
259 if (empty($user->id)) {
260 throw new coding_exception('User id is required when printing user avatar image.');
261 }
262
263 // only touch the DB if we are missing data and complain loudly...
264 $needrec = false;
265 foreach ($fields as $field) {
266 if (!array_key_exists($field, $user)) {
267 $needrec = true;
268 debugging('Missing '.$field.' property in $user object, this is a performance problem that needs to be fixed by a developer. '
269 .'Please use user_picture::fields() to get the full list of required fields.', DEBUG_DEVELOPER);
270 break;
271 }
272 }
273
274 if ($needrec) {
275 $this->user = $DB->get_record('user', array('id'=>$user->id), self::FIELDS, MUST_EXIST);
276 } else {
277 $this->user = clone($user);
278 }
279 }
280
281 /**
1a10840e 282 * Returns a list of required user fields, useful when fetching required user info from db.
f3afba4e
PS
283 *
284 * In some cases we have to fetch the user data together with some other information,
285 * the idalias is useful there because the id would otherwise override the main
286 * id of the result record. Please note it has to be converted back to id before rendering.
287 *
5d0c95a5 288 * @param string $tableprefix name of database table prefix in query
f3afba4e 289 * @param string $idalias alias of id field
5d0c95a5
PS
290 * @return string
291 */
f3afba4e
PS
292 public static function fields($tableprefix = '', $idalias = '') {
293 if ($tableprefix === '' and $idalias === '') {
5d0c95a5 294 return self::FIELDS;
5d0c95a5 295 }
f3afba4e
PS
296 $fields = explode(',', self::FIELDS);
297 foreach ($fields as $key=>$field) {
298 if ($field === 'id' and $idalias !== '') {
299 $field = "$field AS $idalias";
300 }
301 $fields[$key] = "$tableprefix.$field";
302 }
303 return implode(',', $fields);
5d0c95a5
PS
304 }
305}
306
bf11293a
PS
307
308/**
309 * Data structure representing a help icon.
310 *
311 * @copyright 2009 Nicolas Connault, 2010 Petr Skoda
312 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
313 * @since Moodle 2.0
314 */
596509e4 315class old_help_icon implements renderable {
bf11293a 316 /**
49f0d481 317 * @var string $helpidentifier lang pack identifier
bf11293a 318 */
53a78cef 319 public $helpidentifier;
bf11293a
PS
320 /**
321 * @var string $title A descriptive text for title tooltip
322 */
97c10099 323 public $title = null;
bf11293a
PS
324 /**
325 * @var string $component Component name, the same as in get_string()
326 */
327 public $component = 'moodle';
328 /**
329 * @var string $linktext Extra descriptive text next to the icon
330 */
97c10099 331 public $linktext = null;
bf11293a
PS
332
333 /**
334 * Constructor: sets up the other components in case they are needed
53a78cef 335 * @param string $helpidentifier The keyword that defines a help page
1a10840e 336 * @param string $title A descriptive text for accessibility only
bf11293a
PS
337 * @param string $component
338 * @param bool $linktext add extra text to icon
339 * @return void
340 */
53a78cef 341 public function __construct($helpidentifier, $title, $component = 'moodle') {
bf11293a
PS
342 if (empty($title)) {
343 throw new coding_exception('A help_icon object requires a $text parameter');
344 }
53a78cef
PS
345 if (empty($helpidentifier)) {
346 throw new coding_exception('A help_icon object requires a $helpidentifier parameter');
bf11293a
PS
347 }
348
53a78cef
PS
349 $this->helpidentifier = $helpidentifier;
350 $this->title = $title;
351 $this->component = $component;
bf11293a
PS
352 }
353}
354
49f0d481
PS
355/**
356 * Data structure representing a help icon.
357 *
358 * @copyright 2010 Petr Skoda (info@skodak.org)
359 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
360 * @since Moodle 2.0
361 */
362class help_icon implements renderable {
363 /**
5435c9dc
MD
364 * @var string $identifier lang pack identifier (without the "_help" suffix),
365 * both get_string($identifier, $component) and get_string($identifier.'_help', $component)
49f0d481
PS
366 * must exist.
367 */
368 public $identifier;
369 /**
370 * @var string $component Component name, the same as in get_string()
371 */
372 public $component;
373 /**
374 * @var string $linktext Extra descriptive text next to the icon
375 */
376 public $linktext = null;
377
378 /**
379 * Constructor
380 * @param string $identifier string for help page title,
5435c9dc
MD
381 * string with _help suffix is used for the actual help text.
382 * string with _link suffix is used to create a link to further info (if it exists)
49f0d481
PS
383 * @param string $component
384 */
259c165d
PS
385 public function __construct($identifier, $component) {
386 $this->identifier = $identifier;
49f0d481
PS
387 $this->component = $component;
388 }
259c165d
PS
389
390 /**
391 * Verifies that both help strings exists, shows debug warnings if not
392 */
393 public function diag_strings() {
394 $sm = get_string_manager();
395 if (!$sm->string_exists($this->identifier, $this->component)) {
396 debugging("Help title string does not exist: [$this->identifier, $this->component]");
397 }
5435c9dc 398 if (!$sm->string_exists($this->identifier.'_help', $this->component)) {
876521ac 399 debugging("Help contents string does not exist: [{$this->identifier}_help, $this->component]");
259c165d
PS
400 }
401 }
49f0d481
PS
402}
403
bf11293a 404
000c278c
PS
405/**
406 * Data structure representing an icon.
407 *
408 * @copyright 2010 Petr Skoda
409 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
410 * @since Moodle 2.0
411 */
412class pix_icon implements renderable {
413 var $pix;
414 var $component;
415 var $attributes = array();
416
417 /**
418 * Constructor
419 * @param string $pix short icon name
420 * @param string $component component name
421 * @param array $attributes html attributes
422 */
423 public function __construct($pix, $alt, $component='moodle', array $attributes = null) {
c80877aa
PS
424 $this->pix = $pix;
425 $this->component = $component;
000c278c
PS
426 $this->attributes = (array)$attributes;
427
428 $this->attributes['alt'] = $alt;
429 if (empty($this->attributes['class'])) {
430 $this->attributes['class'] = 'smallicon';
431 }
432 if (!isset($this->attributes['title'])) {
433 $this->attributes['title'] = $this->attributes['alt'];
434 }
435 }
436}
437
438
3ba60ee1
PS
439/**
440 * Data structure representing a simple form with only one button.
441 *
442 * @copyright 2009 Petr Skoda
443 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
444 * @since Moodle 2.0
445 */
446class single_button implements renderable {
574fbea4
PS
447 /**
448 * Target url
449 * @var moodle_url
450 */
3ba60ee1 451 var $url;
574fbea4
PS
452 /**
453 * Button label
454 * @var string
455 */
3ba60ee1 456 var $label;
574fbea4
PS
457 /**
458 * Form submit method
459 * @var string post or get
460 */
3ba60ee1 461 var $method = 'post';
574fbea4
PS
462 /**
463 * Wrapping div class
464 * @var string
465 * */
3ba60ee1 466 var $class = 'singlebutton';
574fbea4
PS
467 /**
468 * True if button disabled, false if normal
469 * @var boolean
470 */
3ba60ee1 471 var $disabled = false;
574fbea4
PS
472 /**
473 * Button tooltip
474 * @var string
475 */
97c10099 476 var $tooltip = null;
574fbea4
PS
477 /**
478 * Form id
479 * @var string
480 */
3ba60ee1 481 var $formid;
574fbea4
PS
482 /**
483 * List of attached actions
484 * @var array of component_action
485 */
3ba60ee1
PS
486 var $actions = array();
487
488 /**
489 * Constructor
574fbea4 490 * @param string|moodle_url $url
3ba60ee1
PS
491 * @param string $label button text
492 * @param string $method get or post submit method
3ba60ee1
PS
493 */
494 public function __construct(moodle_url $url, $label, $method='post') {
495 $this->url = clone($url);
496 $this->label = $label;
497 $this->method = $method;
498 }
499
500 /**
574fbea4 501 * Shortcut for adding a JS confirm dialog when the button is clicked.
3ba60ee1
PS
502 * The message must be a yes/no question.
503 * @param string $message The yes/no confirmation question. If "Yes" is clicked, the original action will occur.
504 * @return void
505 */
506 public function add_confirm_action($confirmmessage) {
20fb563e 507 $this->add_action(new component_action('click', 'M.util.show_confirm_dialog', array('message' => $confirmmessage)));
3ba60ee1
PS
508 }
509
574fbea4
PS
510 /**
511 * Add action to the button.
512 * @param component_action $action
513 * @return void
514 */
3ba60ee1
PS
515 public function add_action(component_action $action) {
516 $this->actions[] = $action;
517 }
518}
519
520
a9967cf5
PS
521/**
522 * Simple form with just one select field that gets submitted automatically.
523 * If JS not enabled small go button is printed too.
524 *
525 * @copyright 2009 Petr Skoda
526 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
527 * @since Moodle 2.0
528 */
529class single_select implements renderable {
530 /**
531 * Target url - includes hidden fields
532 * @var moodle_url
533 */
534 var $url;
535 /**
536 * Name of the select element.
537 * @var string
538 */
539 var $name;
540 /**
541 * @var array $options associative array value=>label ex.:
542 * array(1=>'One, 2=>Two)
543 * it is also possible to specify optgroup as complex label array ex.:
544 * array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
545 * array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
546 */
547 var $options;
548 /**
549 * Selected option
550 * @var string
551 */
552 var $selected;
553 /**
554 * Nothing selected
555 * @var array
556 */
557 var $nothing;
558 /**
559 * Extra select field attributes
560 * @var array
561 */
562 var $attributes = array();
563 /**
564 * Button label
565 * @var string
566 */
567 var $label = '';
568 /**
569 * Form submit method
570 * @var string post or get
571 */
572 var $method = 'get';
573 /**
574 * Wrapping div class
575 * @var string
576 * */
577 var $class = 'singleselect';
578 /**
579 * True if button disabled, false if normal
580 * @var boolean
581 */
582 var $disabled = false;
583 /**
584 * Button tooltip
585 * @var string
586 */
587 var $tooltip = null;
588 /**
589 * Form id
590 * @var string
591 */
592 var $formid = null;
593 /**
594 * List of attached actions
595 * @var array of component_action
596 */
597 var $helpicon = null;
598 /**
599 * Constructor
600 * @param moodle_url $url form action target, includes hidden fields
601 * @param string $name name of selection field - the changing parameter in url
602 * @param array $options list of options
603 * @param string $selected selected element
604 * @param array $nothing
f8dab966 605 * @param string $formid
a9967cf5 606 */
f8dab966 607 public function __construct(moodle_url $url, $name, array $options, $selected='', $nothing=array(''=>'choosedots'), $formid=null) {
a9967cf5
PS
608 $this->url = $url;
609 $this->name = $name;
610 $this->options = $options;
611 $this->selected = $selected;
612 $this->nothing = $nothing;
f8dab966 613 $this->formid = $formid;
a9967cf5
PS
614 }
615
616 /**
617 * Shortcut for adding a JS confirm dialog when the button is clicked.
618 * The message must be a yes/no question.
619 * @param string $message The yes/no confirmation question. If "Yes" is clicked, the original action will occur.
620 * @return void
621 */
622 public function add_confirm_action($confirmmessage) {
623 $this->add_action(new component_action('submit', 'M.util.show_confirm_dialog', array('message' => $confirmmessage)));
624 }
625
626 /**
627 * Add action to the button.
628 * @param component_action $action
629 * @return void
630 */
631 public function add_action(component_action $action) {
632 $this->actions[] = $action;
633 }
f8dab966
PS
634
635 /**
259c165d 636 * Adds help icon.
f8dab966 637 * @param string $page The keyword that defines a help page
1a10840e 638 * @param string $title A descriptive text for accessibility only
f8dab966
PS
639 * @param string $component
640 * @param bool $linktext add extra text to icon
641 * @return void
642 */
596509e4
PS
643 public function set_old_help_icon($helppage, $title, $component = 'moodle') {
644 $this->helpicon = new old_help_icon($helppage, $title, $component);
f8dab966
PS
645 }
646
259c165d
PS
647 /**
648 * Adds help icon.
649 * @param string $identifier The keyword that defines a help page
650 * @param string $component
651 * @param bool $linktext add extra text to icon
652 * @return void
653 */
654 public function set_help_icon($identifier, $component = 'moodle') {
9c7b24bf 655 $this->helpicon = new help_icon($identifier, $component);
259c165d
PS
656 }
657
f8dab966
PS
658 /**
659 * Set's select lable
660 * @param string $label
661 * @return void
662 */
663 public function set_label($label) {
664 $this->label = $label;
665 }
a9967cf5
PS
666}
667
668
4d10e579
PS
669/**
670 * Simple URL selection widget description.
671 * @copyright 2009 Petr Skoda
672 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
673 * @since Moodle 2.0
674 */
675class url_select implements renderable {
676 /**
677 * @var array $urls associative array value=>label ex.:
678 * array(1=>'One, 2=>Two)
679 * it is also possible to specify optgroup as complex label array ex.:
680 * array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
681 * array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
682 */
683 var $urls;
684 /**
685 * Selected option
686 * @var string
687 */
688 var $selected;
689 /**
690 * Nothing selected
691 * @var array
692 */
693 var $nothing;
694 /**
695 * Extra select field attributes
696 * @var array
697 */
698 var $attributes = array();
699 /**
700 * Button label
701 * @var string
702 */
703 var $label = '';
704 /**
705 * Wrapping div class
706 * @var string
707 * */
708 var $class = 'urlselect';
709 /**
710 * True if button disabled, false if normal
711 * @var boolean
712 */
713 var $disabled = false;
714 /**
715 * Button tooltip
716 * @var string
717 */
718 var $tooltip = null;
719 /**
720 * Form id
721 * @var string
722 */
723 var $formid = null;
724 /**
725 * List of attached actions
726 * @var array of component_action
727 */
728 var $helpicon = null;
729 /**
730 * Constructor
731 * @param array $urls list of options
732 * @param string $selected selected element
733 * @param array $nothing
734 * @param string $formid
735 */
736 public function __construct(array $urls, $selected='', $nothing=array(''=>'choosedots'), $formid=null) {
737 $this->urls = $urls;
738 $this->selected = $selected;
739 $this->nothing = $nothing;
740 $this->formid = $formid;
741 }
742
743 /**
259c165d 744 * Adds help icon.
4d10e579 745 * @param string $page The keyword that defines a help page
1a10840e 746 * @param string $title A descriptive text for accessibility only
4d10e579
PS
747 * @param string $component
748 * @param bool $linktext add extra text to icon
749 * @return void
750 */
596509e4
PS
751 public function set_old_help_icon($helppage, $title, $component = 'moodle') {
752 $this->helpicon = new old_help_icon($helppage, $title, $component);
4d10e579
PS
753 }
754
259c165d
PS
755 /**
756 * Adds help icon.
757 * @param string $identifier The keyword that defines a help page
758 * @param string $component
759 * @param bool $linktext add extra text to icon
760 * @return void
761 */
762 public function set_help_icon($identifier, $component = 'moodle') {
9c7b24bf 763 $this->helpicon = new help_icon($identifier, $component);
259c165d
PS
764 }
765
4d10e579
PS
766 /**
767 * Set's select lable
768 * @param string $label
769 * @return void
770 */
771 public function set_label($label) {
772 $this->label = $label;
773 }
774}
775
776
574fbea4
PS
777/**
778 * Data structure describing html link with special action attached.
779 * @copyright 2010 Petr Skoda
780 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
781 * @since Moodle 2.0
782 */
783class action_link implements renderable {
784 /**
785 * Href url
786 * @var moodle_url
787 */
788 var $url;
789 /**
790 * Link text
791 * @var string HTML fragment
792 */
793 var $text;
794 /**
795 * HTML attributes
796 * @var array
797 */
798 var $attributes;
799 /**
800 * List of actions attached to link
801 * @var array of component_action
802 */
803 var $actions;
804
805 /**
806 * Constructor
807 * @param string|moodle_url $url
808 * @param string $text HTML fragment
809 * @param component_action $action
11820bac 810 * @param array $attributes associative array of html link attributes + disabled
574fbea4
PS
811 */
812 public function __construct(moodle_url $url, $text, component_action $action=null, array $attributes=null) {
813 $this->url = clone($url);
814 $this->text = $text;
b0fef57b 815 $this->attributes = (array)$attributes;
f14b641b 816 if ($action) {
574fbea4
PS
817 $this->add_action($action);
818 }
819 }
820
821 /**
822 * Add action to the link.
823 * @param component_action $action
824 * @return void
825 */
826 public function add_action(component_action $action) {
827 $this->actions[] = $action;
828 }
c63923bd
PS
829
830 public function add_class($class) {
67da0bf7
DM
831 if (empty($this->attributes['class'])) {
832 $this->attributes['class'] = $class;
c63923bd 833 } else {
67da0bf7 834 $this->attributes['class'] .= ' ' . $class;
c63923bd
PS
835 }
836 }
574fbea4 837}
3ba60ee1 838
227255b8 839// ==== HTML writer and helper classes, will be probably moved elsewhere ======
5d0c95a5
PS
840
841/**
842 * Simple html output class
843 * @copyright 2009 Tim Hunt, 2010 Petr Skoda
844 */
845class html_writer {
846 /**
847 * Outputs a tag with attributes and contents
848 * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
5d0c95a5 849 * @param string $contents What goes between the opening and closing tags
26acc814 850 * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
5d0c95a5
PS
851 * @return string HTML fragment
852 */
26acc814 853 public static function tag($tagname, $contents, array $attributes = null) {
5d0c95a5
PS
854 return self::start_tag($tagname, $attributes) . $contents . self::end_tag($tagname);
855 }
856
857 /**
858 * Outputs an opening tag with attributes
859 * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
860 * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
861 * @return string HTML fragment
862 */
863 public static function start_tag($tagname, array $attributes = null) {
864 return '<' . $tagname . self::attributes($attributes) . '>';
865 }
866
867 /**
868 * Outputs a closing tag
869 * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
870 * @return string HTML fragment
871 */
872 public static function end_tag($tagname) {
873 return '</' . $tagname . '>';
874 }
875
876 /**
877 * Outputs an empty tag with attributes
878 * @param string $tagname The name of tag ('input', 'img', 'br' etc.)
879 * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
880 * @return string HTML fragment
881 */
882 public static function empty_tag($tagname, array $attributes = null) {
883 return '<' . $tagname . self::attributes($attributes) . ' />';
884 }
885
836c47d7
TH
886 /**
887 * Outputs a tag, but only if the contents are not empty
888 * @param string $tagname The name of tag ('a', 'img', 'span' etc.)
889 * @param string $contents What goes between the opening and closing tags
890 * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
891 * @return string HTML fragment
892 */
893 public static function nonempty_tag($tagname, $contents, array $attributes = null) {
894 if ($contents === '' || is_null($contents)) {
895 return '';
896 }
897 return self::tag($tagname, $contents, $attributes);
898 }
899
5d0c95a5
PS
900 /**
901 * Outputs a HTML attribute and value
902 * @param string $name The name of the attribute ('src', 'href', 'class' etc.)
903 * @param string $value The value of the attribute. The value will be escaped with {@link s()}
904 * @return string HTML fragment
905 */
906 public static function attribute($name, $value) {
907 if (is_array($value)) {
908 debugging("Passed an array for the HTML attribute $name", DEBUG_DEVELOPER);
909 }
bf11293a
PS
910 if ($value instanceof moodle_url) {
911 return ' ' . $name . '="' . $value->out() . '"';
912 }
97c10099
PS
913
914 // special case, we do not want these in output
915 if ($value === null) {
916 return '';
5d0c95a5 917 }
97c10099
PS
918
919 // no sloppy trimming here!
920 return ' ' . $name . '="' . s($value) . '"';
5d0c95a5
PS
921 }
922
923 /**
924 * Outputs a list of HTML attributes and values
925 * @param array $attributes The tag attributes (array('src' => $url, 'class' => 'class1') etc.)
926 * The values will be escaped with {@link s()}
927 * @return string HTML fragment
928 */
929 public static function attributes(array $attributes = null) {
930 $attributes = (array)$attributes;
931 $output = '';
932 foreach ($attributes as $name => $value) {
933 $output .= self::attribute($name, $value);
934 }
935 return $output;
936 }
937
938 /**
939 * Generates random html element id.
940 * @param string $base
941 * @return string
942 */
943 public static function random_id($base='random') {
944 return uniqid($base);
945 }
0f4c64b7
PS
946
947 /**
948 * Generates a simple html link
949 * @param string|moodle_url $url
950 * @param string $text link txt
951 * @param array $attributes extra html attributes
952 * @return string HTML fragment
953 */
954 public static function link($url, $text, array $attributes = null) {
955 $attributes = (array)$attributes;
956 $attributes['href'] = $url;
26acc814 957 return self::tag('a', $text, $attributes);
0f4c64b7 958 }
3ff163c5 959
14dce022
PS
960 /**
961 * generates a simple checkbox with optional label
962 * @param string $name
963 * @param string $value
964 * @param bool $checked
965 * @param string $label
966 * @param array $attributes
967 * @return string html fragment
968 */
969 public static function checkbox($name, $value, $checked = true, $label = '', array $attributes = null) {
970 $attributes = (array)$attributes;
971 $output = '';
972
973 if ($label !== '' and !is_null($label)) {
974 if (empty($attributes['id'])) {
975 $attributes['id'] = self::random_id('checkbox_');
976 }
977 }
53868425
PS
978 $attributes['type'] = 'checkbox';
979 $attributes['value'] = $value;
980 $attributes['name'] = $name;
14dce022 981 $attributes['checked'] = $checked ? 'selected' : null;
53868425 982
14dce022
PS
983 $output .= self::empty_tag('input', $attributes);
984
985 if ($label !== '' and !is_null($label)) {
26acc814 986 $output .= self::tag('label', $label, array('for'=>$attributes['id']));
14dce022
PS
987 }
988
989 return $output;
990 }
991
78bdac64
PS
992 /**
993 * Generates a simple select yes/no form field
994 * @param string $name name of select element
995 * @param bool $selected
996 * @param array $attributes - html select element attributes
997 * @return string HRML fragment
998 */
19f3bbb2 999 public static function select_yes_no($name, $selected=true, array $attributes = null) {
78bdac64
PS
1000 $options = array('1'=>get_string('yes'), '0'=>get_string('no'));
1001 return self::select($options, $name, $selected, null, $attributes);
1002 }
1003
3ff163c5
PS
1004 /**
1005 * Generates a simple select form field
6770330d
PS
1006 * @param array $options associative array value=>label ex.:
1007 * array(1=>'One, 2=>Two)
1008 * it is also possible to specify optgroup as complex label array ex.:
bde156b3 1009 * array(array('Odd'=>array(1=>'One', 3=>'Three)), array('Even'=>array(2=>'Two')))
6770330d 1010 * array(1=>'One', '--1uniquekey'=>array('More'=>array(2=>'Two', 3=>'Three')))
3ff163c5 1011 * @param string $name name of select element
1a10840e 1012 * @param string|array $selected value or array of values depending on multiple attribute
3ff163c5
PS
1013 * @param array|bool $nothing, add nothing selected option, or false of not added
1014 * @param array $attributes - html select element attributes
78bdac64 1015 * @return string HTML fragment
3ff163c5 1016 */
aa2dea70 1017 public static function select(array $options, $name, $selected = '', $nothing = array(''=>'choosedots'), array $attributes = null) {
3ff163c5
PS
1018 $attributes = (array)$attributes;
1019 if (is_array($nothing)) {
1020 foreach ($nothing as $k=>$v) {
4b9210f3 1021 if ($v === 'choose' or $v === 'choosedots') {
3ff163c5
PS
1022 $nothing[$k] = get_string('choosedots');
1023 }
1024 }
1025 $options = $nothing + $options; // keep keys, do not override
3750c3bd
PS
1026
1027 } else if (is_string($nothing) and $nothing !== '') {
1028 // BC
1029 $options = array(''=>$nothing) + $options;
bde156b3 1030 }
3ff163c5
PS
1031
1032 // we may accept more values if multiple attribute specified
1033 $selected = (array)$selected;
1034 foreach ($selected as $k=>$v) {
1035 $selected[$k] = (string)$v;
1036 }
1037
1038 if (!isset($attributes['id'])) {
1039 $id = 'menu'.$name;
1040 // name may contaion [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading
1041 $id = str_replace('[', '', $id);
1042 $id = str_replace(']', '', $id);
1043 $attributes['id'] = $id;
1044 }
1045
1046 if (!isset($attributes['class'])) {
1047 $class = 'menu'.$name;
1048 // name may contaion [], which would make an invalid class. e.g. numeric question type editing form, assignment quickgrading
1049 $class = str_replace('[', '', $class);
1050 $class = str_replace(']', '', $class);
1051 $attributes['class'] = $class;
1052 }
1053 $attributes['class'] = 'select ' . $attributes['class']; /// Add 'select' selector always
1054
1055 $attributes['name'] = $name;
1056
1057 $output = '';
1058 foreach ($options as $value=>$label) {
6770330d
PS
1059 if (is_array($label)) {
1060 // ignore key, it just has to be unique
1061 $output .= self::select_optgroup(key($label), current($label), $selected);
1062 } else {
1063 $output .= self::select_option($label, $value, $selected);
3ff163c5 1064 }
3ff163c5 1065 }
26acc814 1066 return self::tag('select', $output, $attributes);
3ff163c5 1067 }
6770330d
PS
1068
1069 private static function select_option($label, $value, array $selected) {
1070 $attributes = array();
1071 $value = (string)$value;
1072 if (in_array($value, $selected, true)) {
1073 $attributes['selected'] = 'selected';
1074 }
1075 $attributes['value'] = $value;
26acc814 1076 return self::tag('option', $label, $attributes);
6770330d
PS
1077 }
1078
1079 private static function select_optgroup($groupname, $options, array $selected) {
1080 if (empty($options)) {
1081 return '';
1082 }
1083 $attributes = array('label'=>$groupname);
1084 $output = '';
1085 foreach ($options as $value=>$label) {
1086 $output .= self::select_option($label, $value, $selected);
1087 }
26acc814 1088 return self::tag('optgroup', $output, $attributes);
6770330d 1089 }
6ea66ff3 1090
f83b9b63
PS
1091 /**
1092 * This is a shortcut for making an hour selector menu.
1093 * @param string $type The type of selector (years, months, days, hours, minutes)
1094 * @param string $name fieldname
1095 * @param int $currenttime A default timestamp in GMT
1096 * @param int $step minute spacing
1097 * @param array $attributes - html select element attributes
1098 * @return HTML fragment
1099 */
1100 public static function select_time($type, $name, $currenttime=0, $step=5, array $attributes=null) {
1101 if (!$currenttime) {
1102 $currenttime = time();
1103 }
1104 $currentdate = usergetdate($currenttime);
1105 $userdatetype = $type;
1106 $timeunits = array();
1107
1108 switch ($type) {
1109 case 'years':
1110 for ($i=1970; $i<=2020; $i++) {
1111 $timeunits[$i] = $i;
1112 }
1113 $userdatetype = 'year';
1114 break;
1115 case 'months':
1116 for ($i=1; $i<=12; $i++) {
1117 $timeunits[$i] = userdate(gmmktime(12,0,0,$i,15,2000), "%B");
1118 }
1119 $userdatetype = 'month';
1120 $currentdate['month'] = $currentdate['mon'];
1121 break;
1122 case 'days':
1123 for ($i=1; $i<=31; $i++) {
1124 $timeunits[$i] = $i;
1125 }
1126 $userdatetype = 'mday';
1127 break;
1128 case 'hours':
1129 for ($i=0; $i<=23; $i++) {
1130 $timeunits[$i] = sprintf("%02d",$i);
1131 }
1132 break;
1133 case 'minutes':
1134 if ($step != 1) {
1135 $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
1136 }
1137
1138 for ($i=0; $i<=59; $i+=$step) {
1139 $timeunits[$i] = sprintf("%02d",$i);
1140 }
1141 break;
1142 default:
1143 throw new coding_exception("Time type $type is not supported by html_writer::select_time().");
1144 }
1145
1146 if (empty($attributes['id'])) {
1147 $attributes['id'] = self::random_id('ts_');
1148 }
1149 $timerselector = self::select($timeunits, $name, $currentdate[$userdatetype], null, array('id'=>$attributes['id']));
26acc814 1150 $label = self::tag('label', get_string(substr($type, 0, -1), 'form'), array('for'=>$attributes['id'], 'class'=>'accesshide'));
f83b9b63
PS
1151
1152 return $label.$timerselector;
1153 }
1154
5be262b6
PS
1155 /**
1156 * Shortcut for quick making of lists
1157 * @param array $items
1158 * @param string $tag ul or ol
1159 * @param array $attributes
1160 * @return string
1161 */
1162 public static function alist(array $items, array $attributes = null, $tag = 'ul') {
1163 //note: 'list' is a reserved keyword ;-)
1164
1165 $output = '';
1166
1167 foreach ($items as $item) {
1168 $output .= html_writer::start_tag('li') . "\n";
1169 $output .= $item . "\n";
1170 $output .= html_writer::end_tag('li') . "\n";
1171 }
1172
26acc814 1173 return html_writer::tag($tag, $output, $attributes);
5be262b6
PS
1174 }
1175
6ea66ff3
PS
1176 /**
1177 * Returns hidden input fields created from url parameters.
1178 * @param moodle_url $url
1179 * @param array $exclude list of excluded parameters
1180 * @return string HTML fragment
1181 */
1182 public static function input_hidden_params(moodle_url $url, array $exclude = null) {
1183 $exclude = (array)$exclude;
1184 $params = $url->params();
1185 foreach ($exclude as $key) {
1186 unset($params[$key]);
1187 }
1188
1189 $output = '';
bde156b3 1190 foreach ($params as $key => $value) {
6ea66ff3
PS
1191 $attributes = array('type'=>'hidden', 'name'=>$key, 'value'=>$value);
1192 $output .= self::empty_tag('input', $attributes)."\n";
1193 }
1194 return $output;
1195 }
77774f6a
PS
1196
1197 /**
1198 * Generate a script tag containing the the specified code.
1199 *
1200 * @param string $js the JavaScript code
e50b4c89 1201 * @param moodle_url|string optional url of the external script, $code ignored if specified
77774f6a
PS
1202 * @return string HTML, the code wrapped in <script> tags.
1203 */
e50b4c89 1204 public static function script($jscode, $url=null) {
77774f6a 1205 if ($jscode) {
e50b4c89 1206 $attributes = array('type'=>'text/javascript');
26acc814 1207 return self::tag('script', "\n//<![CDATA[\n$jscode\n//]]>\n", $attributes) . "\n";
e50b4c89
PS
1208
1209 } else if ($url) {
1210 $attributes = array('type'=>'text/javascript', 'src'=>$url);
26acc814 1211 return self::tag('script', '', $attributes) . "\n";
a9967cf5 1212
77774f6a
PS
1213 } else {
1214 return '';
1215 }
1216 }
16be8974
DM
1217
1218 /**
1219 * Renders HTML table
1220 *
1221 * This method may modify the passed instance by adding some default properties if they are not set yet.
1222 * If this is not what you want, you should make a full clone of your data before passing them to this
1223 * method. In most cases this is not an issue at all so we do not clone by default for performance
1224 * and memory consumption reasons.
1225 *
1226 * @param html_table $table data to be rendered
1227 * @return string HTML code
1228 */
1229 public static function table(html_table $table) {
1230 // prepare table data and populate missing properties with reasonable defaults
1231 if (!empty($table->align)) {
1232 foreach ($table->align as $key => $aa) {
1233 if ($aa) {
1234 $table->align[$key] = 'text-align:'. fix_align_rtl($aa) .';'; // Fix for RTL languages
1235 } else {
1236 $table->align[$key] = null;
1237 }
1238 }
1239 }
1240 if (!empty($table->size)) {
1241 foreach ($table->size as $key => $ss) {
1242 if ($ss) {
1243 $table->size[$key] = 'width:'. $ss .';';
1244 } else {
1245 $table->size[$key] = null;
1246 }
1247 }
1248 }
1249 if (!empty($table->wrap)) {
1250 foreach ($table->wrap as $key => $ww) {
1251 if ($ww) {
1252 $table->wrap[$key] = 'white-space:nowrap;';
1253 } else {
1254 $table->wrap[$key] = '';
1255 }
1256 }
1257 }
1258 if (!empty($table->head)) {
1259 foreach ($table->head as $key => $val) {
1260 if (!isset($table->align[$key])) {
1261 $table->align[$key] = null;
1262 }
1263 if (!isset($table->size[$key])) {
1264 $table->size[$key] = null;
1265 }
1266 if (!isset($table->wrap[$key])) {
1267 $table->wrap[$key] = null;
1268 }
1269
1270 }
1271 }
1272 if (empty($table->attributes['class'])) {
1273 $table->attributes['class'] = 'generaltable';
1274 }
1275 if (!empty($table->tablealign)) {
1276 $table->attributes['class'] .= ' boxalign' . $table->tablealign;
1277 }
1278
1279 // explicitly assigned properties override those defined via $table->attributes
e126c0cc 1280 $table->attributes['class'] = trim($table->attributes['class']);
16be8974
DM
1281 $attributes = array_merge($table->attributes, array(
1282 'id' => $table->id,
1283 'width' => $table->width,
1284 'summary' => $table->summary,
1285 'cellpadding' => $table->cellpadding,
1286 'cellspacing' => $table->cellspacing,
1287 ));
1288 $output = html_writer::start_tag('table', $attributes) . "\n";
1289
1290 $countcols = 0;
1291
1292 if (!empty($table->head)) {
1293 $countcols = count($table->head);
1294 $output .= html_writer::start_tag('thead', array()) . "\n";
1295 $output .= html_writer::start_tag('tr', array()) . "\n";
1296 $keys = array_keys($table->head);
1297 $lastkey = end($keys);
1298
1299 foreach ($table->head as $key => $heading) {
1300 // Convert plain string headings into html_table_cell objects
1301 if (!($heading instanceof html_table_cell)) {
1302 $headingtext = $heading;
1303 $heading = new html_table_cell();
1304 $heading->text = $headingtext;
1305 $heading->header = true;
1306 }
1307
1308 if ($heading->header !== false) {
1309 $heading->header = true;
1310 }
1311
e126c0cc
DM
1312 if ($heading->header && empty($heading->scope)) {
1313 $heading->scope = 'col';
1314 }
1315
16be8974
DM
1316 $heading->attributes['class'] .= ' header c' . $key;
1317 if (isset($table->headspan[$key]) && $table->headspan[$key] > 1) {
1318 $heading->colspan = $table->headspan[$key];
1319 $countcols += $table->headspan[$key] - 1;
1320 }
1321
1322 if ($key == $lastkey) {
1323 $heading->attributes['class'] .= ' lastcol';
1324 }
1325 if (isset($table->colclasses[$key])) {
1326 $heading->attributes['class'] .= ' ' . $table->colclasses[$key];
1327 }
e126c0cc 1328 $heading->attributes['class'] = trim($heading->attributes['class']);
16be8974
DM
1329 $attributes = array_merge($heading->attributes, array(
1330 'style' => $table->align[$key] . $table->size[$key] . $heading->style,
1331 'scope' => $heading->scope,
1332 'colspan' => $heading->colspan,
1333 ));
1334
1335 $tagtype = 'td';
1336 if ($heading->header === true) {
1337 $tagtype = 'th';
1338 }
1339 $output .= html_writer::tag($tagtype, $heading->text, $attributes) . "\n";
1340 }
1341 $output .= html_writer::end_tag('tr') . "\n";
1342 $output .= html_writer::end_tag('thead') . "\n";
1343
1344 if (empty($table->data)) {
1345 // For valid XHTML strict every table must contain either a valid tr
1346 // or a valid tbody... both of which must contain a valid td
1347 $output .= html_writer::start_tag('tbody', array('class' => 'empty'));
1348 $output .= html_writer::tag('tr', html_writer::tag('td', '', array('colspan'=>count($table->head))));
1349 $output .= html_writer::end_tag('tbody');
1350 }
1351 }
1352
1353 if (!empty($table->data)) {
1354 $oddeven = 1;
1355 $keys = array_keys($table->data);
1356 $lastrowkey = end($keys);
1357 $output .= html_writer::start_tag('tbody', array());
1358
1359 foreach ($table->data as $key => $row) {
1360 if (($row === 'hr') && ($countcols)) {
1361 $output .= html_writer::tag('td', html_writer::tag('div', '', array('class' => 'tabledivider')), array('colspan' => $countcols));
1362 } else {
1363 // Convert array rows to html_table_rows and cell strings to html_table_cell objects
1364 if (!($row instanceof html_table_row)) {
1365 $newrow = new html_table_row();
1366
e126c0cc 1367 foreach ($row as $item) {
16be8974
DM
1368 $cell = new html_table_cell();
1369 $cell->text = $item;
1370 $newrow->cells[] = $cell;
1371 }
1372 $row = $newrow;
1373 }
1374
1375 $oddeven = $oddeven ? 0 : 1;
1376 if (isset($table->rowclasses[$key])) {
1377 $row->attributes['class'] .= ' ' . $table->rowclasses[$key];
1378 }
1379
1380 $row->attributes['class'] .= ' r' . $oddeven;
1381 if ($key == $lastrowkey) {
1382 $row->attributes['class'] .= ' lastrow';
1383 }
1384
e126c0cc 1385 $output .= html_writer::start_tag('tr', array('class' => trim($row->attributes['class']), 'style' => $row->style, 'id' => $row->id)) . "\n";
16be8974
DM
1386 $keys2 = array_keys($row->cells);
1387 $lastkey = end($keys2);
1388
1389 foreach ($row->cells as $key => $cell) {
1390 if (!($cell instanceof html_table_cell)) {
1391 $mycell = new html_table_cell();
1392 $mycell->text = $cell;
1393 $cell = $mycell;
1394 }
1395
e126c0cc
DM
1396 if (($cell->header === true) && empty($cell->scope)) {
1397 $cell->scope = 'row';
1398 }
1399
16be8974
DM
1400 if (isset($table->colclasses[$key])) {
1401 $cell->attributes['class'] .= ' ' . $table->colclasses[$key];
1402 }
1403
1404 $cell->attributes['class'] .= ' cell c' . $key;
1405 if ($key == $lastkey) {
1406 $cell->attributes['class'] .= ' lastcol';
1407 }
1408 $tdstyle = '';
1409 $tdstyle .= isset($table->align[$key]) ? $table->align[$key] : '';
1410 $tdstyle .= isset($table->size[$key]) ? $table->size[$key] : '';
1411 $tdstyle .= isset($table->wrap[$key]) ? $table->wrap[$key] : '';
e126c0cc 1412 $cell->attributes['class'] = trim($cell->attributes['class']);
16be8974
DM
1413 $tdattributes = array_merge($cell->attributes, array(
1414 'style' => $tdstyle . $cell->style,
1415 'colspan' => $cell->colspan,
1416 'rowspan' => $cell->rowspan,
1417 'id' => $cell->id,
1418 'abbr' => $cell->abbr,
1419 'scope' => $cell->scope,
1420 ));
1421 $tagtype = 'td';
1422 if ($cell->header === true) {
1423 $tagtype = 'th';
1424 }
1425 $output .= html_writer::tag($tagtype, $cell->text, $tdattributes) . "\n";
1426 }
1427 }
1428 $output .= html_writer::end_tag('tr') . "\n";
1429 }
1430 $output .= html_writer::end_tag('tbody') . "\n";
1431 }
1432 $output .= html_writer::end_tag('table') . "\n";
1433
1434 return $output;
1435 }
1436
5d0c95a5
PS
1437}
1438
227255b8
PS
1439// ==== JS writer and helper classes, will be probably moved elsewhere ======
1440
1441/**
1442 * Simple javascript output class
1443 * @copyright 2010 Petr Skoda
1444 */
1445class js_writer {
1446 /**
1447 * Returns javascript code calling the function
1a10840e 1448 * @param string $function function name, can be complex like Y.Event.purgeElement
227255b8
PS
1449 * @param array $arguments parameters
1450 * @param int $delay execution delay in seconds
1451 * @return string JS code fragment
1452 */
1453 public function function_call($function, array $arguments = null, $delay=0) {
1b4e41af
PS
1454 if ($arguments) {
1455 $arguments = array_map('json_encode', $arguments);
1456 $arguments = implode(', ', $arguments);
1457 } else {
1458 $arguments = '';
1459 }
227255b8
PS
1460 $js = "$function($arguments);";
1461
1462 if ($delay) {
1463 $delay = $delay * 1000; // in miliseconds
1464 $js = "setTimeout(function() { $js }, $delay);";
1465 }
1466 return $js . "\n";
1467 }
1468
3b01539c
PS
1469 /**
1470 * Special function which adds Y as first argument of fucntion call.
1471 * @param string $function
1472 * @param array $extraarguments
1473 * @return string
1474 */
1475 public function function_call_with_Y($function, array $extraarguments = null) {
1476 if ($extraarguments) {
1477 $extraarguments = array_map('json_encode', $extraarguments);
1478 $arguments = 'Y, ' . implode(', ', $extraarguments);
1479 } else {
1480 $arguments = 'Y';
1481 }
1482 return "$function($arguments);\n";
1483 }
1484
1ce15fda
SH
1485 /**
1486 * Returns JavaScript code to initialise a new object
1487 * @param string|null $var If it is null then no var is assigned the new object
1488 * @param string $class
1489 * @param array $arguments
1490 * @param array $requirements
1491 * @param int $delay
1492 * @return string
1493 */
1494 public function object_init($var, $class, array $arguments = null, array $requirements = null, $delay=0) {
1495 if (is_array($arguments)) {
1496 $arguments = array_map('json_encode', $arguments);
1497 $arguments = implode(', ', $arguments);
1498 }
1499
1500 if ($var === null) {
53fc3e70 1501 $js = "new $class(Y, $arguments);";
1ce15fda 1502 } else if (strpos($var, '.')!==false) {
53fc3e70 1503 $js = "$var = new $class(Y, $arguments);";
1ce15fda 1504 } else {
53fc3e70 1505 $js = "var $var = new $class(Y, $arguments);";
1ce15fda
SH
1506 }
1507
1508 if ($delay) {
1509 $delay = $delay * 1000; // in miliseconds
1510 $js = "setTimeout(function() { $js }, $delay);";
1511 }
1512
1513 if (count($requirements) > 0) {
1514 $requirements = implode("', '", $requirements);
53fc3e70 1515 $js = "Y.use('$requirements', function(Y){ $js });";
1ce15fda
SH
1516 }
1517 return $js."\n";
1518 }
1519
227255b8
PS
1520 /**
1521 * Returns code setting value to variable
1522 * @param string $name
1523 * @param mixed $value json serialised value
1524 * @param bool $usevar add var definition, ignored for nested properties
1525 * @return string JS code fragment
1526 */
1527 public function set_variable($name, $value, $usevar=true) {
1528 $output = '';
1529
1530 if ($usevar) {
1531 if (strpos($name, '.')) {
1532 $output .= '';
1533 } else {
1534 $output .= 'var ';
1535 }
1536 }
1537
1538 $output .= "$name = ".json_encode($value).";";
1539
1540 return $output;
1541 }
1542
1543 /**
1544 * Writes event handler attaching code
1a10840e 1545 * @param mixed $selector standard YUI selector for elements, may be array or string, element id is in the form "#idvalue"
227255b8
PS
1546 * @param string $event A valid DOM event (click, mousedown, change etc.)
1547 * @param string $function The name of the function to call
1548 * @param array $arguments An optional array of argument parameters to pass to the function
1549 * @return string JS code fragment
1550 */
1551 public function event_handler($selector, $event, $function, array $arguments = null) {
1552 $selector = json_encode($selector);
1553 $output = "Y.on('$event', $function, $selector, null";
1554 if (!empty($arguments)) {
1555 $output .= ', ' . json_encode($arguments);
1556 }
1557 return $output . ");\n";
1558 }
1559}
1560
d9c8f425 1561/**
16be8974 1562 * Holds all the information required to render a <table> by {@see core_renderer::table()}
d9c8f425 1563 *
16be8974
DM
1564 * Example of usage:
1565 * $t = new html_table();
1566 * ... // set various properties of the object $t as described below
1567 * echo html_writer::table($t);
d9c8f425 1568 *
16be8974 1569 * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
d9c8f425 1570 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1571 * @since Moodle 2.0
1572 */
16be8974 1573class html_table {
d9c8f425 1574 /**
16be8974 1575 * @var string value to use for the id attribute of the table
d9c8f425 1576 */
97c10099 1577 public $id = null;
d9c8f425 1578 /**
16be8974 1579 * @var array attributes of HTML attributes for the <table> element
d9c8f425 1580 */
16be8974 1581 public $attributes = array();
7b1f2c82 1582 /**
a0ead5eb 1583 * For more control over the rendering of the headers, an array of html_table_cell objects
54a007e8 1584 * can be passed instead of an array of strings.
7b1f2c82 1585 * @var array of headings. The n-th array item is used as a heading of the n-th column.
1586 *
1587 * Example of usage:
1588 * $t->head = array('Student', 'Grade');
1589 */
1590 public $head;
1591 /**
1592 * @var array can be used to make a heading span multiple columns
1593 *
1594 * Example of usage:
1595 * $t->headspan = array(2,1);
1596 *
1597 * In this example, {@see html_table:$data} is supposed to have three columns. For the first two columns,
1598 * the same heading is used. Therefore, {@see html_table::$head} should consist of two items.
1599 */
1600 public $headspan;
1601 /**
1602 * @var array of column alignments. The value is used as CSS 'text-align' property. Therefore, possible
1603 * values are 'left', 'right', 'center' and 'justify'. Specify 'right' or 'left' from the perspective
1604 * of a left-to-right (LTR) language. For RTL, the values are flipped automatically.
1605 *
beb56299 1606 * Examples of usage:
1607 * $t->align = array(null, 'right');
1608 * or
1609 * $t->align[1] = 'right';
1610 *
d9c8f425 1611 */
beb56299 1612 public $align;
d9c8f425 1613 /**
beb56299 1614 * @var array of column sizes. The value is used as CSS 'size' property.
1615 *
1616 * Examples of usage:
1617 * $t->size = array('50%', '50%');
1618 * or
1619 * $t->size[1] = '120px';
d9c8f425 1620 */
beb56299 1621 public $size;
d9c8f425 1622 /**
beb56299 1623 * @var array of wrapping information. The only possible value is 'nowrap' that sets the
1624 * CSS property 'white-space' to the value 'nowrap' in the given column.
1625 *
1626 * Example of usage:
1627 * $t->wrap = array(null, 'nowrap');
d9c8f425 1628 */
beb56299 1629 public $wrap;
d9c8f425 1630 /**
beb56299 1631 * @var array of arrays or html_table_row objects containing the data. Alternatively, if you have
1632 * $head specified, the string 'hr' (for horizontal ruler) can be used
1633 * instead of an array of cells data resulting in a divider rendered.
d9c8f425 1634 *
beb56299 1635 * Example of usage with array of arrays:
1636 * $row1 = array('Harry Potter', '76 %');
1637 * $row2 = array('Hermione Granger', '100 %');
1638 * $t->data = array($row1, $row2);
d9c8f425 1639 *
beb56299 1640 * Example with array of html_table_row objects: (used for more fine-grained control)
1641 * $cell1 = new html_table_cell();
1642 * $cell1->text = 'Harry Potter';
1643 * $cell1->colspan = 2;
1644 * $row1 = new html_table_row();
1645 * $row1->cells[] = $cell1;
1646 * $cell2 = new html_table_cell();
1647 * $cell2->text = 'Hermione Granger';
1648 * $cell3 = new html_table_cell();
1649 * $cell3->text = '100 %';
1650 * $row2 = new html_table_row();
1651 * $row2->cells = array($cell2, $cell3);
1652 * $t->data = array($row1, $row2);
1653 */
1654 public $data;
1655 /**
16be8974 1656 * @var string width of the table, percentage of the page preferred. Defaults to 80%
beb56299 1657 * @deprecated since Moodle 2.0. Styling should be in the CSS.
1658 */
1659 public $width = null;
1660 /**
1661 * @var string alignment the whole table. Can be 'right', 'left' or 'center' (default).
1662 * @deprecated since Moodle 2.0. Styling should be in the CSS.
1663 */
1664 public $tablealign = null;
1665 /**
1666 * @var int padding on each cell, in pixels
1667 * @deprecated since Moodle 2.0. Styling should be in the CSS.
1668 */
1669 public $cellpadding = null;
1670 /**
1671 * @var int spacing between cells, in pixels
1672 * @deprecated since Moodle 2.0. Styling should be in the CSS.
1673 */
1674 public $cellspacing = null;
1675 /**
1676 * @var array classes to add to particular rows, space-separated string.
1677 * Classes 'r0' or 'r1' are added automatically for every odd or even row,
1678 * respectively. Class 'lastrow' is added automatically for the last row
1679 * in the table.
d9c8f425 1680 *
beb56299 1681 * Example of usage:
1682 * $t->rowclasses[9] = 'tenth'
1683 */
1684 public $rowclasses;
1685 /**
1686 * @var array classes to add to every cell in a particular column,
1687 * space-separated string. Class 'cell' is added automatically by the renderer.
1688 * Classes 'c0' or 'c1' are added automatically for every odd or even column,
1689 * respectively. Class 'lastcol' is added automatically for all last cells
1690 * in a row.
d9c8f425 1691 *
beb56299 1692 * Example of usage:
1693 * $t->colclasses = array(null, 'grade');
d9c8f425 1694 */
beb56299 1695 public $colclasses;
1696 /**
1697 * @var string description of the contents for screen readers.
1698 */
1699 public $summary;
d9c8f425 1700
1701 /**
16be8974 1702 * Constructor
d9c8f425 1703 */
16be8974
DM
1704 public function __construct() {
1705 $this->attributes['class'] = '';
d9c8f425 1706 }
d9c8f425 1707}
1708
34059565 1709
d9c8f425 1710/**
7b1f2c82 1711 * Component representing a table row.
d9c8f425 1712 *
1713 * @copyright 2009 Nicolas Connault
1714 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1715 * @since Moodle 2.0
1716 */
16be8974
DM
1717class html_table_row {
1718 /**
1719 * @var string value to use for the id attribute of the row
1720 */
1721 public $id = null;
d9c8f425 1722 /**
7b1f2c82 1723 * @var array $cells Array of html_table_cell objects
d9c8f425 1724 */
7b1f2c82 1725 public $cells = array();
beb56299 1726 /**
16be8974 1727 * @var string $style value to use for the style attribute of the table row
beb56299 1728 */
16be8974
DM
1729 public $style = null;
1730 /**
1731 * @var array attributes of additional HTML attributes for the <tr> element
1732 */
1733 public $attributes = array();
a0ead5eb 1734
54a007e8 1735 /**
8cea545e 1736 * Constructor
54a007e8 1737 * @param array $cells
8cea545e
PS
1738 */
1739 public function __construct(array $cells=null) {
16be8974 1740 $this->attributes['class'] = '';
8cea545e
PS
1741 $cells = (array)$cells;
1742 foreach ($cells as $cell) {
1743 if ($cell instanceof html_table_cell) {
1744 $this->cells[] = $cell;
a019627a 1745 } else {
8cea545e 1746 $this->cells[] = new html_table_cell($cell);
a019627a 1747 }
1748 }
54a007e8 1749 }
d9c8f425 1750}
1751
34059565 1752
d9c8f425 1753/**
7b1f2c82 1754 * Component representing a table cell.
d9c8f425 1755 *
1756 * @copyright 2009 Nicolas Connault
1757 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1758 * @since Moodle 2.0
1759 */
16be8974
DM
1760class html_table_cell {
1761 /**
1762 * @var string value to use for the id attribute of the cell
1763 */
1764 public $id = null;
d9c8f425 1765 /**
7b1f2c82 1766 * @var string $text The contents of the cell
d9c8f425 1767 */
7b1f2c82 1768 public $text;
d9c8f425 1769 /**
7b1f2c82 1770 * @var string $abbr Abbreviated version of the contents of the cell
d9c8f425 1771 */
97c10099 1772 public $abbr = null;
d9c8f425 1773 /**
7b1f2c82 1774 * @var int $colspan Number of columns this cell should span
d9c8f425 1775 */
97c10099 1776 public $colspan = null;
d9c8f425 1777 /**
7b1f2c82 1778 * @var int $rowspan Number of rows this cell should span
d9c8f425 1779 */
97c10099 1780 public $rowspan = null;
d9c8f425 1781 /**
7b1f2c82 1782 * @var string $scope Defines a way to associate header cells and data cells in a table
d9c8f425 1783 */
97c10099 1784 public $scope = null;
1ae3767a 1785 /**
1786 * @var boolean $header Whether or not this cell is a header cell
1787 */
a4998d01 1788 public $header = null;
16be8974
DM
1789 /**
1790 * @var string $style value to use for the style attribute of the table cell
1791 */
1792 public $style = null;
1793 /**
1794 * @var array attributes of additional HTML attributes for the <tr> element
1795 */
1796 public $attributes = array();
d9c8f425 1797
8cea545e
PS
1798 public function __construct($text = null) {
1799 $this->text = $text;
16be8974 1800 $this->attributes['class'] = '';
d9c8f425 1801 }
1802}
1803
34059565 1804
7b1f2c82 1805/// Complex components aggregating simpler components
1806
34059565 1807
d9c8f425 1808/**
beb56299 1809 * Component representing a paging bar.
d9c8f425 1810 *
1811 * @copyright 2009 Nicolas Connault
1812 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1813 * @since Moodle 2.0
1814 */
929d7a83 1815class paging_bar implements renderable {
d9c8f425 1816 /**
beb56299 1817 * @var int $maxdisplay The maximum number of pagelinks to display
d9c8f425 1818 */
beb56299 1819 public $maxdisplay = 18;
d9c8f425 1820 /**
beb56299 1821 * @var int $totalcount post or get
d9c8f425 1822 */
beb56299 1823 public $totalcount;
d9c8f425 1824 /**
beb56299 1825 * @var int $page The page you are currently viewing
d9c8f425 1826 */
929d7a83 1827 public $page;
d9c8f425 1828 /**
beb56299 1829 * @var int $perpage The number of entries that should be shown per page
d9c8f425 1830 */
beb56299 1831 public $perpage;
d9c8f425 1832 /**
beb56299 1833 * @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.
1834 * If this is a moodle_url object then the pagevar param will be replaced by the page no, for each page.
d9c8f425 1835 */
beb56299 1836 public $baseurl;
d9c8f425 1837 /**
beb56299 1838 * @var string $pagevar This is the variable name that you use for the page number in your code (ie. 'tablepage', 'blogpage', etc)
d9c8f425 1839 */
929d7a83 1840 public $pagevar;
beb56299 1841 /**
56ddb719 1842 * @var string $previouslink A HTML link representing the "previous" page
beb56299 1843 */
1844 public $previouslink = null;
1845 /**
56ddb719 1846 * @var tring $nextlink A HTML link representing the "next" page
beb56299 1847 */
1848 public $nextlink = null;
1849 /**
56ddb719 1850 * @var tring $firstlink A HTML link representing the first page
beb56299 1851 */
1852 public $firstlink = null;
1853 /**
56ddb719 1854 * @var tring $lastlink A HTML link representing the last page
beb56299 1855 */
1856 public $lastlink = null;
1857 /**
56ddb719 1858 * @var array $pagelinks An array of strings. One of them is just a string: the current page
beb56299 1859 */
1860 public $pagelinks = array();
d9c8f425 1861
929d7a83
PS
1862 /**
1863 * Constructor paging_bar with only the required params.
1864 *
1a10840e 1865 * @param int $totalcount The total number of entries available to be paged through
929d7a83
PS
1866 * @param int $page The page you are currently viewing
1867 * @param int $perpage The number of entries that should be shown per page
1868 * @param string|moodle_url $baseurl url of the current page, the $pagevar parameter is added
1869 * @param string $pagevar name of page parameter that holds the page number
1870 */
1871 public function __construct($totalcount, $page, $perpage, $baseurl, $pagevar = 'page') {
1872 $this->totalcount = $totalcount;
1873 $this->page = $page;
1874 $this->perpage = $perpage;
1875 $this->baseurl = $baseurl;
1876 $this->pagevar = $pagevar;
1877 }
1878
d9c8f425 1879 /**
d9c8f425 1880 * @return void
1881 */
34059565 1882 public function prepare(renderer_base $output, moodle_page $page, $target) {
1c1f64a2 1883 if (!isset($this->totalcount) || is_null($this->totalcount)) {
929d7a83 1884 throw new coding_exception('paging_bar requires a totalcount value.');
beb56299 1885 }
1886 if (!isset($this->page) || is_null($this->page)) {
929d7a83 1887 throw new coding_exception('paging_bar requires a page value.');
beb56299 1888 }
1889 if (empty($this->perpage)) {
929d7a83 1890 throw new coding_exception('paging_bar requires a perpage value.');
beb56299 1891 }
1892 if (empty($this->baseurl)) {
929d7a83 1893 throw new coding_exception('paging_bar requires a baseurl value.');
beb56299 1894 }
d9c8f425 1895
beb56299 1896 if ($this->totalcount > $this->perpage) {
1897 $pagenum = $this->page - 1;
d9c8f425 1898
beb56299 1899 if ($this->page > 0) {
929d7a83 1900 $this->previouslink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$pagenum)), get_string('previous'), array('class'=>'previous'));
beb56299 1901 }
d9c8f425 1902
beb56299 1903 if ($this->perpage > 0) {
1904 $lastpage = ceil($this->totalcount / $this->perpage);
1905 } else {
1906 $lastpage = 1;
1907 }
1908
1909 if ($this->page > 15) {
1910 $startpage = $this->page - 10;
1911
929d7a83 1912 $this->firstlink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>0)), '1', array('class'=>'first'));
beb56299 1913 } else {
1914 $startpage = 0;
1915 }
1916
1917 $currpage = $startpage;
1918 $displaycount = $displaypage = 0;
1919
1920 while ($displaycount < $this->maxdisplay and $currpage < $lastpage) {
1921 $displaypage = $currpage + 1;
1922
f43cdceb 1923 if ($this->page == $currpage) {
beb56299 1924 $this->pagelinks[] = $displaypage;
1925 } else {
56ddb719 1926 $pagelink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$currpage)), $displaypage);
beb56299 1927 $this->pagelinks[] = $pagelink;
1928 }
1929
1930 $displaycount++;
1931 $currpage++;
1932 }
1933
1934 if ($currpage < $lastpage) {
1935 $lastpageactual = $lastpage - 1;
abdac127 1936 $this->lastlink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$lastpageactual)), $lastpage, array('class'=>'last'));
beb56299 1937 }
1938
1939 $pagenum = $this->page + 1;
1940
1941 if ($pagenum != $displaypage) {
abdac127 1942 $this->nextlink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$pagenum)), get_string('next'), array('class'=>'next'));
beb56299 1943 }
d9c8f425 1944 }
1945 }
d9c8f425 1946}
1947
34059565 1948
d9c8f425 1949/**
beb56299 1950 * This class represents how a block appears on a page.
d9c8f425 1951 *
beb56299 1952 * During output, each block instance is asked to return a block_contents object,
1953 * those are then passed to the $OUTPUT->block function for display.
1954 *
1955 * {@link $contents} should probably be generated using a moodle_block_..._renderer.
1956 *
1957 * Other block-like things that need to appear on the page, for example the
1958 * add new block UI, are also represented as block_contents objects.
1959 *
1960 * @copyright 2009 Tim Hunt
d9c8f425 1961 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1962 * @since Moodle 2.0
1963 */
dd72b308 1964class block_contents {
beb56299 1965 /** @var int used to set $skipid. */
1966 protected static $idcounter = 1;
1967
1968 const NOT_HIDEABLE = 0;
1969 const VISIBLE = 1;
1970 const HIDDEN = 2;
1971
d9c8f425 1972 /**
dd72b308 1973 * @var integer $skipid All the blocks (or things that look like blocks)
beb56299 1974 * printed on a page are given a unique number that can be used to construct
1975 * id="" attributes. This is set automatically be the {@link prepare()} method.
1976 * Do not try to set it manually.
d9c8f425 1977 */
beb56299 1978 public $skipid;
d9c8f425 1979
1980 /**
beb56299 1981 * @var integer If this is the contents of a real block, this should be set to
1982 * the block_instance.id. Otherwise this should be set to 0.
1983 */
1984 public $blockinstanceid = 0;
1985
1986 /**
1987 * @var integer if this is a real block instance, and there is a corresponding
1988 * block_position.id for the block on this page, this should be set to that id.
1989 * Otherwise it should be 0.
1990 */
1991 public $blockpositionid = 0;
1992
1993 /**
1994 * @param array $attributes an array of attribute => value pairs that are put on the
1995 * outer div of this block. {@link $id} and {@link $classes} attributes should be set separately.
1996 */
dd72b308 1997 public $attributes;
beb56299 1998
1999 /**
2000 * @param string $title The title of this block. If this came from user input,
2001 * it should already have had format_string() processing done on it. This will
2002 * be output inside <h2> tags. Please do not cause invalid XHTML.
2003 */
2004 public $title = '';
2005
2006 /**
2007 * @param string $content HTML for the content
2008 */
2009 public $content = '';
2010
2011 /**
2012 * @param array $list an alternative to $content, it you want a list of things with optional icons.
2013 */
2014 public $footer = '';
2015
2016 /**
2017 * Any small print that should appear under the block to explain to the
2018 * teacher about the block, for example 'This is a sticky block that was
2019 * added in the system context.'
2020 * @var string
2021 */
2022 public $annotation = '';
2023
2024 /**
2025 * @var integer one of the constants NOT_HIDEABLE, VISIBLE, HIDDEN. Whether
2026 * the user can toggle whether this block is visible.
2027 */
2028 public $collapsible = self::NOT_HIDEABLE;
2029
2030 /**
2031 * A (possibly empty) array of editing controls. Each element of this array
2032 * should be an array('url' => $url, 'icon' => $icon, 'caption' => $caption).
b5d0cafc 2033 * $icon is the icon name. Fed to $OUTPUT->pix_url.
beb56299 2034 * @var array
2035 */
2036 public $controls = array();
2037
dd72b308 2038
beb56299 2039 /**
dd72b308
PS
2040 * Create new instance of block content
2041 * @param array $attributes
d9c8f425 2042 */
dd72b308 2043 public function __construct(array $attributes=null) {
beb56299 2044 $this->skipid = self::$idcounter;
2045 self::$idcounter += 1;
dd72b308
PS
2046
2047 if ($attributes) {
2048 // standard block
2049 $this->attributes = $attributes;
2050 } else {
2051 // simple "fake" blocks used in some modules and "Add new block" block
6605ff8c 2052 $this->attributes = array('class'=>'block');
beb56299 2053 }
dd72b308
PS
2054 }
2055
2056 /**
2057 * Add html class to block
2058 * @param string $class
2059 * @return void
2060 */
2061 public function add_class($class) {
2062 $this->attributes['class'] .= ' '.$class;
d9c8f425 2063 }
2064}
beb56299 2065
34059565 2066
beb56299 2067/**
2068 * This class represents a target for where a block can go when it is being moved.
2069 *
2070 * This needs to be rendered as a form with the given hidden from fields, and
2071 * clicking anywhere in the form should submit it. The form action should be
2072 * $PAGE->url.
2073 *
2074 * @copyright 2009 Tim Hunt
2075 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2076 * @since Moodle 2.0
2077 */
dd72b308 2078class block_move_target {
beb56299 2079 /**
dd72b308
PS
2080 * Move url
2081 * @var moodle_url
beb56299 2082 */
dd72b308 2083 public $url;
beb56299 2084 /**
dd72b308
PS
2085 * label
2086 * @var string
beb56299 2087 */
dd72b308
PS
2088 public $text;
2089
2090 /**
1a10840e 2091 * Constructor
dd72b308
PS
2092 * @param string $text
2093 * @param moodle_url $url
2094 */
2095 public function __construct($text, moodle_url $url) {
2096 $this->text = $text;
2097 $this->url = $url;
2098 }
beb56299 2099}
d2dbd0c0
SH
2100
2101/**
2102 * Custom menu item
2103 *
2104 * This class is used to represent one item within a custom menu that may or may
2105 * not have children.
2106 *
2107 * @copyright 2010 Sam Hemelryk
2108 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2109 * @since Moodle 2.0
2110 */
2111class custom_menu_item implements renderable {
2112 /**
2113 * The text to show for the item
2114 * @var string
2115 */
2116 protected $text;
2117 /**
2118 * The link to give the icon if it has no children
2119 * @var moodle_url
2120 */
2121 protected $url;
2122 /**
2123 * A title to apply to the item. By default the text
2124 * @var string
2125 */
2126 protected $title;
2127 /**
1a10840e 2128 * A sort order for the item, not necessary if you order things in the CFG var
d2dbd0c0
SH
2129 * @var int
2130 */
2131 protected $sort;
2132 /**
2133 * A reference to the parent for this item or NULL if it is a top level item
2134 * @var custom_menu_item
2135 */
2136 protected $parent;
2137 /**
2138 * A array in which to store children this item has.
2139 * @var array
2140 */
2141 protected $children = array();
2142 /**
2143 * A reference to the sort var of the last child that was added
2144 * @var int
2145 */
2146 protected $lastsort = 0;
2147 /**
2148 * Constructs the new custom menu item
2149 *
2150 * @param string $text
2151 * @param moodle_url $url A moodle url to apply as the link for this item [Optional]
2152 * @param string $title A title to apply to this item [Optional]
2153 * @param int $sort A sort or to use if we need to sort differently [Optional]
2154 * @param custom_menu_item $parent A reference to the parent custom_menu_item this child
2155 * belongs to, only if the child has a parent. [Optional]
2156 */
2157 public function __construct($text, moodle_url $url=null, $title=null, $sort = null, custom_menu_item $parent=null) {
2158 $this->text = $text;
2159 $this->url = $url;
2160 $this->title = $title;
2161 $this->sort = (int)$sort;
2162 $this->parent = $parent;
2163 }
2164
2165 /**
2166 * Adds a custom menu item as a child of this node given its properties.
2167 *
2168 * @param string $text
2169 * @param moodle_url $url
2170 * @param string $title
2171 * @param int $sort
2172 * @return custom_menu_item
2173 */
2174 public function add($text, moodle_url $url=null, $title=null, $sort = null) {
2175 $key = count($this->children);
2176 if (empty($sort)) {
2177 $sort = $this->lastsort + 1;
2178 }
2179 $this->children[$key] = new custom_menu_item($text, $url, $title, $sort, $this);
2180 $this->lastsort = (int)$sort;
2181 return $this->children[$key];
2182 }
2183 /**
2184 * Returns the text for this item
2185 * @return string
2186 */
2187 public function get_text() {
2188 return $this->text;
2189 }
2190 /**
2191 * Returns the url for this item
2192 * @return moodle_url
2193 */
2194 public function get_url() {
2195 return $this->url;
2196 }
2197 /**
2198 * Returns the title for this item
2199 * @return string
2200 */
2201 public function get_title() {
2202 return $this->title;
2203 }
2204 /**
2205 * Sorts and returns the children for this item
2206 * @return array
2207 */
2208 public function get_children() {
2209 $this->sort();
2210 return $this->children;
2211 }
2212 /**
2213 * Gets the sort order for this child
2214 * @return int
2215 */
2216 public function get_sort_order() {
2217 return $this->sort;
2218 }
2219 /**
2220 * Gets the parent this child belong to
2221 * @return custom_menu_item
2222 */
2223 public function get_parent() {
2224 return $this->parent;
2225 }
2226 /**
2227 * Sorts the children this item has
2228 */
2229 public function sort() {
2230 usort($this->children, array('custom_menu','sort_custom_menu_items'));
2231 }
2232 /**
2233 * Returns true if this item has any children
2234 * @return bool
2235 */
2236 public function has_children() {
2237 return (count($this->children) > 0);
2238 }
2239}
2240
2241/**
2242 * Custom menu class
2243 *
2244 * This class is used to operate a custom menu that can be rendered for the page.
2245 * The custom menu is built using $CFG->custommenuitems and is a structured collection
2246 * of custom_menu_item nodes that can be rendered by the core renderer.
2247 *
2248 * To configure the custom menu:
2249 * Settings: Administration > Appearance > Themes > Theme settings
2250 *
2251 * @copyright 2010 Sam Hemelryk
2252 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2253 * @since Moodle 2.0
2254 */
2255class custom_menu extends custom_menu_item {
2256 /**
2257 * Creates the custom menu
2258 * @param string $text Sets the text for this custom menu, never gets used and is optional
2259 */
2260 public function __construct($text='base') {
2261 global $CFG;
2262 parent::__construct($text);
12cc75ae
SH
2263 if (!empty($CFG->custommenuitems)) {
2264 $this->override_children(self::convert_text_to_menu_nodes($CFG->custommenuitems));
2265 }
d2dbd0c0
SH
2266 }
2267
2268 /**
2269 * Overrides the children of this custom menu. Useful when getting children
2270 * from $CFG->custommenuitems
2271 */
2272 public function override_children(array $children) {
2273 $this->children = array();
2274 foreach ($children as $child) {
2275 if ($child instanceof custom_menu_item) {
2276 $this->children[] = $child;
2277 }
2278 }
2279 }
2280
2281 /**
2282 * Converts a string into a structured array of custom_menu_items which can
2283 * then be added to a custom menu.
2284 *
2285 * Structure:
2286 * text|url|title
2287 * The number of hyphens at the start determines the depth of the item
2288 *
2289 * Example structure:
2290 * First level first item|http://www.moodle.com/
2291 * -Second level first item|http://www.moodle.com/partners/
2292 * -Second level second item|http://www.moodle.com/hq/
2293 * --Third level first item|http://www.moodle.com/jobs/
2294 * -Second level third item|http://www.moodle.com/development/
2295 * First level second item|http://www.moodle.com/feedback/
2296 * First level third item
4d2ee4c2 2297 *
d2dbd0c0
SH
2298 * @static
2299 * @param string $text
2300 * @return array
2301 */
2302 public static function convert_text_to_menu_nodes($text) {
2303 $lines = explode("\n", $text);
2304 $children = array();
2305 $lastchild = null;
2306 $lastdepth = null;
2307 $lastsort = 0;
2308 foreach ($lines as $line) {
2309 $line = trim($line);
2310 $bits = explode('|', $line ,4); // name|url|title|sort
2311 if (!array_key_exists(0, $bits) || empty($bits[0])) {
2312 // Every item must have a name to be valid
2313 continue;
2314 } else {
2315 $bits[0] = ltrim($bits[0],'-');
2316 }
2317 if (!array_key_exists(1, $bits)) {
2318 // Set the url to null
2319 $bits[1] = null;
2320 } else {
2321 // Make sure the url is a moodle url
2322 $bits[1] = new moodle_url($bits[1]);
2323 }
2324 if (!array_key_exists(2, $bits)) {
2325 // Set the title to null seeing as there isn't one
2326 $bits[2] = $bits[0];
2327 }
2328 // Set an incremental sort order to keep it simple.
2329 $bits[3] = $lastsort;
2330 $lastsort = $bits[3]+1;
2331 if (preg_match('/^(\-*)/', $line, $match) && $lastchild != null && $lastdepth !== null) {
2332 $depth = strlen($match[1]);
2333 if ($depth < $lastdepth) {
2334 if ($lastdepth > 1) {
2335 $depth = $lastdepth - 1;
2336 $lastchild = $lastchild->get_parent()->get_parent()->add($bits[0], $bits[1], $bits[2], $bits[3]);
2337 } else {
2338 $depth = 0;
2339 $lastchild = new custom_menu_item($bits[0], $bits[1], $bits[2], $bits[3]);
2340 $children[] = $lastchild;
2341 }
2342 } else if ($depth > $lastdepth) {
2343 $depth = $lastdepth + 1;
2344 $lastchild = $lastchild->add($bits[0], $bits[1], $bits[2], $bits[3]);
2345 } else {
2346 if ($depth == 0) {
2347 $lastchild = new custom_menu_item($bits[0], $bits[1], $bits[2], $bits[3]);
2348 $children[] = $lastchild;
2349 } else {
2350 $lastchild = $lastchild->get_parent()->add($bits[0], $bits[1], $bits[2], $bits[3]);
2351 }
2352 }
2353 } else {
2354 $depth = 0;
2355 $lastchild = new custom_menu_item($bits[0], $bits[1], $bits[2], $bits[3]);
2356 $children[] = $lastchild;
2357 }
2358 $lastdepth = $depth;
2359 }
2360 return $children;
2361 }
2362
2363 /**
2364 * Sorts two custom menu items
2365 *
2366 * This function is designed to be used with the usort method
2367 * usort($this->children, array('custom_menu','sort_custom_menu_items'));
2368 *
2369 * @param custom_menu_item $itema
2370 * @param custom_menu_item $itemb
2371 * @return int
2372 */
2373 public static function sort_custom_menu_items(custom_menu_item $itema, custom_menu_item $itemb) {
2374 $itema = $itema->get_sort_order();
2375 $itemb = $itemb->get_sort_order();
2376 if ($itema == $itemb) {
2377 return 0;
2378 }
2379 return ($itema > $itemb) ? +1 : -1;
2380 }
4d2ee4c2 2381}