|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00003 // This file is part of Moodle - http://moodle.org/ 00004 // 00005 // Moodle is free software: you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation, either version 3 of the License, or 00008 // (at your option) any later version. 00009 // 00010 // Moodle is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00017 00030 defined('MOODLE_INTERNAL') || die(); 00031 00036 interface renderable { 00037 // intentionally empty 00038 } 00039 00047 class file_picker implements renderable { 00048 public $options; 00049 public function __construct(stdClass $options) { 00050 global $CFG, $USER, $PAGE; 00051 require_once($CFG->dirroot. '/repository/lib.php'); 00052 $defaults = array( 00053 'accepted_types'=>'*', 00054 'return_types'=>FILE_INTERNAL, 00055 'env' => 'filepicker', 00056 'client_id' => uniqid(), 00057 'itemid' => 0, 00058 'maxbytes'=>-1, 00059 'maxfiles'=>1, 00060 'buttonname'=>false 00061 ); 00062 foreach ($defaults as $key=>$value) { 00063 if (empty($options->$key)) { 00064 $options->$key = $value; 00065 } 00066 } 00067 00068 $options->currentfile = ''; 00069 if (!empty($options->itemid)) { 00070 $fs = get_file_storage(); 00071 $usercontext = get_context_instance(CONTEXT_USER, $USER->id); 00072 if (empty($options->filename)) { 00073 if ($files = $fs->get_area_files($usercontext->id, 'user', 'draft', $options->itemid, 'id DESC', false)) { 00074 $file = reset($files); 00075 } 00076 } else { 00077 $file = $fs->get_file($usercontext->id, 'user', 'draft', $options->itemid, $options->filepath, $options->filename); 00078 } 00079 if (!empty($file)) { 00080 $options->currentfile = html_writer::link(moodle_url::make_draftfile_url($file->get_itemid(), $file->get_filepath(), $file->get_filename()), $file->get_filename()); 00081 } 00082 } 00083 00084 // initilise options, getting files in root path 00085 $this->options = initialise_filepicker($options); 00086 00087 // copying other options 00088 foreach ($options as $name=>$value) { 00089 if (!isset($this->options->$name)) { 00090 $this->options->$name = $value; 00091 } 00092 } 00093 } 00094 } 00095 00103 class user_picture implements renderable { 00107 protected static $fields = array('id', 'picture', 'firstname', 'lastname', 'imagealt', 'email'); //TODO: add deleted 00108 00112 public $user; 00117 public $courseid; 00121 public $link = true; 00125 public $size = 35; 00130 public $alttext = true; 00134 public $popup = false; 00138 public $class = 'userpicture'; 00139 00146 public function __construct(stdClass $user) { 00147 global $DB; 00148 00149 if (empty($user->id)) { 00150 throw new coding_exception('User id is required when printing user avatar image.'); 00151 } 00152 00153 // only touch the DB if we are missing data and complain loudly... 00154 $needrec = false; 00155 foreach (self::$fields as $field) { 00156 if (!array_key_exists($field, $user)) { 00157 $needrec = true; 00158 debugging('Missing '.$field.' property in $user object, this is a performance problem that needs to be fixed by a developer. ' 00159 .'Please use user_picture::fields() to get the full list of required fields.', DEBUG_DEVELOPER); 00160 break; 00161 } 00162 } 00163 00164 if ($needrec) { 00165 $this->user = $DB->get_record('user', array('id'=>$user->id), self::fields(), MUST_EXIST); 00166 } else { 00167 $this->user = clone($user); 00168 } 00169 } 00170 00184 public static function fields($tableprefix = '', array $extrafields = NULL, $idalias = 'id', $fieldprefix = '') { 00185 if (!$tableprefix and !$extrafields and !$idalias) { 00186 return implode(',', self::$fields); 00187 } 00188 if ($tableprefix) { 00189 $tableprefix .= '.'; 00190 } 00191 $fields = array(); 00192 foreach (self::$fields as $field) { 00193 if ($field === 'id' and $idalias and $idalias !== 'id') { 00194 $fields[$field] = "$tableprefix$field AS $idalias"; 00195 } else { 00196 if ($fieldprefix and $field !== 'id') { 00197 $fields[$field] = "$tableprefix$field AS $fieldprefix$field"; 00198 } else { 00199 $fields[$field] = "$tableprefix$field"; 00200 } 00201 } 00202 } 00203 // add extra fields if not already there 00204 if ($extrafields) { 00205 foreach ($extrafields as $e) { 00206 if ($e === 'id' or isset($fields[$e])) { 00207 continue; 00208 } 00209 if ($fieldprefix) { 00210 $fields[$e] = "$tableprefix$e AS $fieldprefix$e"; 00211 } else { 00212 $fields[$e] = "$tableprefix$e"; 00213 } 00214 } 00215 } 00216 return implode(',', $fields); 00217 } 00218 00231 public static function unalias(stdClass $record, array $extrafields=null, $idalias='id', $fieldprefix='') { 00232 00233 if (empty($idalias)) { 00234 $idalias = 'id'; 00235 } 00236 00237 $return = new stdClass(); 00238 00239 foreach (self::$fields as $field) { 00240 if ($field === 'id') { 00241 if (property_exists($record, $idalias)) { 00242 $return->id = $record->{$idalias}; 00243 } 00244 } else { 00245 if (property_exists($record, $fieldprefix.$field)) { 00246 $return->{$field} = $record->{$fieldprefix.$field}; 00247 } 00248 } 00249 } 00250 // add extra fields if not already there 00251 if ($extrafields) { 00252 foreach ($extrafields as $e) { 00253 if ($e === 'id' or property_exists($return, $e)) { 00254 continue; 00255 } 00256 $return->{$e} = $record->{$fieldprefix.$e}; 00257 } 00258 } 00259 00260 return $return; 00261 } 00262 00272 public function get_url(moodle_page $page, renderer_base $renderer = null) { 00273 global $CFG; 00274 00275 if (is_null($renderer)) { 00276 $renderer = $page->get_renderer('core'); 00277 } 00278 00279 if ((!empty($CFG->forcelogin) and !isloggedin()) || 00280 (!empty($CFG->forceloginforprofileimage) && (!isloggedin() || isguestuser()))) { 00281 // protect images if login required and not logged in; 00282 // also if login is required for profile images and is not logged in or guest 00283 // do not use require_login() because it is expensive and not suitable here anyway 00284 return $renderer->pix_url('u/f1'); 00285 } 00286 00287 // Sort out the filename and size. Size is only required for the gravatar 00288 // implementation presently. 00289 if (empty($this->size)) { 00290 $filename = 'f2'; 00291 $size = 35; 00292 } else if ($this->size === true or $this->size == 1) { 00293 $filename = 'f1'; 00294 $size = 100; 00295 } else if ($this->size >= 50) { 00296 $filename = 'f1'; 00297 $size = (int)$this->size; 00298 } else { 00299 $filename = 'f2'; 00300 $size = (int)$this->size; 00301 } 00302 00303 // First we need to determine whether the user has uploaded a profile 00304 // picture of not. 00305 if (!empty($this->user->deleted) or !$context = context_user::instance($this->user->id, IGNORE_MISSING)) { 00306 $hasuploadedfile = false; 00307 } else { 00308 $fs = get_file_storage(); 00309 $hasuploadedfile = ($fs->file_exists($context->id, 'user', 'icon', 0, '/', $filename.'/.png') || $fs->file_exists($context->id, 'user', 'icon', 0, '/', $filename.'/.jpg')); 00310 } 00311 00312 $imageurl = $renderer->pix_url('u/'.$filename); 00313 if ($hasuploadedfile && $this->user->picture == 1) { 00314 $path = '/'; 00315 if (clean_param($page->theme->name, PARAM_THEME) == $page->theme->name) { 00316 // We append the theme name to the file path if we have it so that 00317 // in the circumstance that the profile picture is not available 00318 // when the user actually requests it they still get the profile 00319 // picture for the correct theme. 00320 $path .= $page->theme->name.'/'; 00321 } 00322 // Set the image URL to the URL for the uploaded file. 00323 $imageurl = moodle_url::make_pluginfile_url($context->id, 'user', 'icon', NULL, $path, $filename); 00324 } else if (!empty($CFG->enablegravatar)) { 00325 // Normalise the size variable to acceptable bounds 00326 if ($size < 1 || $size > 512) { 00327 $size = 35; 00328 } 00329 // Hash the users email address 00330 $md5 = md5(strtolower(trim($this->user->email))); 00331 // Build a gravatar URL with what we know. 00332 // If the currently requested page is https then we'll return an 00333 // https gravatar page. 00334 if (strpos($CFG->httpswwwroot, 'https:') === 0) { 00335 $imageurl = new moodle_url("https://secure.gravatar.com/avatar/{$md5}", array('s' => $size, 'd' => $imageurl->out(false))); 00336 } else { 00337 $imageurl = new moodle_url("http://www.gravatar.com/avatar/{$md5}", array('s' => $size, 'd' => $imageurl->out(false))); 00338 } 00339 } 00340 00341 // Return the URL that has been generated. 00342 return $imageurl; 00343 } 00344 } 00345 00353 class old_help_icon implements renderable { 00357 public $helpidentifier; 00361 public $title = null; 00365 public $component = 'moodle'; 00369 public $linktext = null; 00370 00379 public function __construct($helpidentifier, $title, $component = 'moodle') { 00380 if (empty($title)) { 00381 throw new coding_exception('A help_icon object requires a $text parameter'); 00382 } 00383 if (empty($helpidentifier)) { 00384 throw new coding_exception('A help_icon object requires a $helpidentifier parameter'); 00385 } 00386 00387 $this->helpidentifier = $helpidentifier; 00388 $this->title = $title; 00389 $this->component = $component; 00390 } 00391 } 00392 00400 class help_icon implements renderable { 00406 public $identifier; 00410 public $component; 00414 public $linktext = null; 00415 00423 public function __construct($identifier, $component) { 00424 $this->identifier = $identifier; 00425 $this->component = $component; 00426 } 00427 00431 public function diag_strings() { 00432 $sm = get_string_manager(); 00433 if (!$sm->string_exists($this->identifier, $this->component)) { 00434 debugging("Help title string does not exist: [$this->identifier, $this->component]"); 00435 } 00436 if (!$sm->string_exists($this->identifier.'_help', $this->component)) { 00437 debugging("Help contents string does not exist: [{$this->identifier}_help, $this->component]"); 00438 } 00439 } 00440 } 00441 00442 00450 class pix_icon implements renderable { 00451 var $pix; 00452 var $component; 00453 var $attributes = array(); 00454 00462 public function __construct($pix, $alt, $component='moodle', array $attributes = null) { 00463 $this->pix = $pix; 00464 $this->component = $component; 00465 $this->attributes = (array)$attributes; 00466 00467 $this->attributes['alt'] = $alt; 00468 if (empty($this->attributes['class'])) { 00469 $this->attributes['class'] = 'smallicon'; 00470 } 00471 if (!isset($this->attributes['title'])) { 00472 $this->attributes['title'] = $this->attributes['alt']; 00473 } 00474 } 00475 } 00476 00482 class pix_emoticon extends pix_icon implements renderable { 00483 00491 public function __construct($pix, $alt, $component = 'moodle', array $attributes = array()) { 00492 if (empty($attributes['class'])) { 00493 $attributes['class'] = 'emoticon'; 00494 } 00495 parent::__construct($pix, $alt, $component, $attributes); 00496 } 00497 } 00498 00506 class single_button implements renderable { 00511 var $url; 00516 var $label; 00521 var $method = 'post'; 00526 var $class = 'singlebutton'; 00531 var $disabled = false; 00536 var $tooltip = null; 00541 var $formid; 00546 var $actions = array(); 00547 00554 public function __construct(moodle_url $url, $label, $method='post') { 00555 $this->url = clone($url); 00556 $this->label = $label; 00557 $this->method = $method; 00558 } 00559 00566 public function add_confirm_action($confirmmessage) { 00567 $this->add_action(new confirm_action($confirmmessage)); 00568 } 00569 00575 public function add_action(component_action $action) { 00576 $this->actions[] = $action; 00577 } 00578 } 00579 00580 00589 class single_select implements renderable { 00594 var $url; 00599 var $name; 00607 var $options; 00612 var $selected; 00617 var $nothing; 00622 var $attributes = array(); 00627 var $label = ''; 00632 var $method = 'get'; 00637 var $class = 'singleselect'; 00642 var $disabled = false; 00647 var $tooltip = null; 00652 var $formid = null; 00657 var $helpicon = null; 00667 public function __construct(moodle_url $url, $name, array $options, $selected='', $nothing=array(''=>'choosedots'), $formid=null) { 00668 $this->url = $url; 00669 $this->name = $name; 00670 $this->options = $options; 00671 $this->selected = $selected; 00672 $this->nothing = $nothing; 00673 $this->formid = $formid; 00674 } 00675 00682 public function add_confirm_action($confirmmessage) { 00683 $this->add_action(new component_action('submit', 'M.util.show_confirm_dialog', array('message' => $confirmmessage))); 00684 } 00685 00691 public function add_action(component_action $action) { 00692 $this->actions[] = $action; 00693 } 00694 00703 public function set_old_help_icon($helppage, $title, $component = 'moodle') { 00704 $this->helpicon = new old_help_icon($helppage, $title, $component); 00705 } 00706 00714 public function set_help_icon($identifier, $component = 'moodle') { 00715 $this->helpicon = new help_icon($identifier, $component); 00716 } 00717 00723 public function set_label($label) { 00724 $this->label = $label; 00725 } 00726 } 00727 00728 00735 class url_select implements renderable { 00743 var $urls; 00748 var $selected; 00753 var $nothing; 00758 var $attributes = array(); 00763 var $label = ''; 00768 var $class = 'urlselect'; 00773 var $disabled = false; 00778 var $tooltip = null; 00783 var $formid = null; 00788 var $helpicon = null; 00792 var $showbutton = null; 00802 public function __construct(array $urls, $selected='', $nothing=array(''=>'choosedots'), 00803 $formid=null, $showbutton=null) { 00804 $this->urls = $urls; 00805 $this->selected = $selected; 00806 $this->nothing = $nothing; 00807 $this->formid = $formid; 00808 $this->showbutton = $showbutton; 00809 } 00810 00819 public function set_old_help_icon($helppage, $title, $component = 'moodle') { 00820 $this->helpicon = new old_help_icon($helppage, $title, $component); 00821 } 00822 00830 public function set_help_icon($identifier, $component = 'moodle') { 00831 $this->helpicon = new help_icon($identifier, $component); 00832 } 00833 00839 public function set_label($label) { 00840 $this->label = $label; 00841 } 00842 } 00843 00844 00851 class action_link implements renderable { 00856 var $url; 00861 var $text; 00866 var $attributes; 00871 var $actions; 00872 00880 public function __construct(moodle_url $url, $text, component_action $action=null, array $attributes=null) { 00881 $this->url = clone($url); 00882 $this->text = $text; 00883 $this->attributes = (array)$attributes; 00884 if ($action) { 00885 $this->add_action($action); 00886 } 00887 } 00888 00894 public function add_action(component_action $action) { 00895 $this->actions[] = $action; 00896 } 00897 00898 public function add_class($class) { 00899 if (empty($this->attributes['class'])) { 00900 $this->attributes['class'] = $class; 00901 } else { 00902 $this->attributes['class'] .= ' ' . $class; 00903 } 00904 } 00905 } 00906 00907 // ==== HTML writer and helper classes, will be probably moved elsewhere ====== 00908 00913 class html_writer { 00921 public static function tag($tagname, $contents, array $attributes = null) { 00922 return self::start_tag($tagname, $attributes) . $contents . self::end_tag($tagname); 00923 } 00924 00931 public static function start_tag($tagname, array $attributes = null) { 00932 return '<' . $tagname . self::attributes($attributes) . '>'; 00933 } 00934 00940 public static function end_tag($tagname) { 00941 return '</' . $tagname . '>'; 00942 } 00943 00950 public static function empty_tag($tagname, array $attributes = null) { 00951 return '<' . $tagname . self::attributes($attributes) . ' />'; 00952 } 00953 00961 public static function nonempty_tag($tagname, $contents, array $attributes = null) { 00962 if ($contents === '' || is_null($contents)) { 00963 return ''; 00964 } 00965 return self::tag($tagname, $contents, $attributes); 00966 } 00967 00974 public static function attribute($name, $value) { 00975 if (is_array($value)) { 00976 debugging("Passed an array for the HTML attribute $name", DEBUG_DEVELOPER); 00977 } 00978 if ($value instanceof moodle_url) { 00979 return ' ' . $name . '="' . $value->out() . '"'; 00980 } 00981 00982 // special case, we do not want these in output 00983 if ($value === null) { 00984 return ''; 00985 } 00986 00987 // no sloppy trimming here! 00988 return ' ' . $name . '="' . s($value) . '"'; 00989 } 00990 00997 public static function attributes(array $attributes = null) { 00998 $attributes = (array)$attributes; 00999 $output = ''; 01000 foreach ($attributes as $name => $value) { 01001 $output .= self::attribute($name, $value); 01002 } 01003 return $output; 01004 } 01005 01011 public static function random_id($base='random') { 01012 static $counter = 0; 01013 static $uniq; 01014 01015 if (!isset($uniq)) { 01016 $uniq = uniqid(); 01017 } 01018 01019 $counter++; 01020 return $base.$uniq.$counter; 01021 } 01022 01030 public static function link($url, $text, array $attributes = null) { 01031 $attributes = (array)$attributes; 01032 $attributes['href'] = $url; 01033 return self::tag('a', $text, $attributes); 01034 } 01035 01045 public static function checkbox($name, $value, $checked = true, $label = '', array $attributes = null) { 01046 $attributes = (array)$attributes; 01047 $output = ''; 01048 01049 if ($label !== '' and !is_null($label)) { 01050 if (empty($attributes['id'])) { 01051 $attributes['id'] = self::random_id('checkbox_'); 01052 } 01053 } 01054 $attributes['type'] = 'checkbox'; 01055 $attributes['value'] = $value; 01056 $attributes['name'] = $name; 01057 $attributes['checked'] = $checked ? 'checked' : null; 01058 01059 $output .= self::empty_tag('input', $attributes); 01060 01061 if ($label !== '' and !is_null($label)) { 01062 $output .= self::tag('label', $label, array('for'=>$attributes['id'])); 01063 } 01064 01065 return $output; 01066 } 01067 01075 public static function select_yes_no($name, $selected=true, array $attributes = null) { 01076 $options = array('1'=>get_string('yes'), '0'=>get_string('no')); 01077 return self::select($options, $name, $selected, null, $attributes); 01078 } 01079 01093 public static function select(array $options, $name, $selected = '', $nothing = array(''=>'choosedots'), array $attributes = null) { 01094 $attributes = (array)$attributes; 01095 if (is_array($nothing)) { 01096 foreach ($nothing as $k=>$v) { 01097 if ($v === 'choose' or $v === 'choosedots') { 01098 $nothing[$k] = get_string('choosedots'); 01099 } 01100 } 01101 $options = $nothing + $options; // keep keys, do not override 01102 01103 } else if (is_string($nothing) and $nothing !== '') { 01104 // BC 01105 $options = array(''=>$nothing) + $options; 01106 } 01107 01108 // we may accept more values if multiple attribute specified 01109 $selected = (array)$selected; 01110 foreach ($selected as $k=>$v) { 01111 $selected[$k] = (string)$v; 01112 } 01113 01114 if (!isset($attributes['id'])) { 01115 $id = 'menu'.$name; 01116 // name may contaion [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading 01117 $id = str_replace('[', '', $id); 01118 $id = str_replace(']', '', $id); 01119 $attributes['id'] = $id; 01120 } 01121 01122 if (!isset($attributes['class'])) { 01123 $class = 'menu'.$name; 01124 // name may contaion [], which would make an invalid class. e.g. numeric question type editing form, assignment quickgrading 01125 $class = str_replace('[', '', $class); 01126 $class = str_replace(']', '', $class); 01127 $attributes['class'] = $class; 01128 } 01129 $attributes['class'] = 'select ' . $attributes['class']; 01130 01131 $attributes['name'] = $name; 01132 01133 if (!empty($attributes['disabled'])) { 01134 $attributes['disabled'] = 'disabled'; 01135 } else { 01136 unset($attributes['disabled']); 01137 } 01138 01139 $output = ''; 01140 foreach ($options as $value=>$label) { 01141 if (is_array($label)) { 01142 // ignore key, it just has to be unique 01143 $output .= self::select_optgroup(key($label), current($label), $selected); 01144 } else { 01145 $output .= self::select_option($label, $value, $selected); 01146 } 01147 } 01148 return self::tag('select', $output, $attributes); 01149 } 01150 01151 private static function select_option($label, $value, array $selected) { 01152 $attributes = array(); 01153 $value = (string)$value; 01154 if (in_array($value, $selected, true)) { 01155 $attributes['selected'] = 'selected'; 01156 } 01157 $attributes['value'] = $value; 01158 return self::tag('option', $label, $attributes); 01159 } 01160 01161 private static function select_optgroup($groupname, $options, array $selected) { 01162 if (empty($options)) { 01163 return ''; 01164 } 01165 $attributes = array('label'=>$groupname); 01166 $output = ''; 01167 foreach ($options as $value=>$label) { 01168 $output .= self::select_option($label, $value, $selected); 01169 } 01170 return self::tag('optgroup', $output, $attributes); 01171 } 01172 01182 public static function select_time($type, $name, $currenttime=0, $step=5, array $attributes=null) { 01183 if (!$currenttime) { 01184 $currenttime = time(); 01185 } 01186 $currentdate = usergetdate($currenttime); 01187 $userdatetype = $type; 01188 $timeunits = array(); 01189 01190 switch ($type) { 01191 case 'years': 01192 for ($i=1970; $i<=2020; $i++) { 01193 $timeunits[$i] = $i; 01194 } 01195 $userdatetype = 'year'; 01196 break; 01197 case 'months': 01198 for ($i=1; $i<=12; $i++) { 01199 $timeunits[$i] = userdate(gmmktime(12,0,0,$i,15,2000), "%B"); 01200 } 01201 $userdatetype = 'month'; 01202 $currentdate['month'] = (int)$currentdate['mon']; 01203 break; 01204 case 'days': 01205 for ($i=1; $i<=31; $i++) { 01206 $timeunits[$i] = $i; 01207 } 01208 $userdatetype = 'mday'; 01209 break; 01210 case 'hours': 01211 for ($i=0; $i<=23; $i++) { 01212 $timeunits[$i] = sprintf("%02d",$i); 01213 } 01214 break; 01215 case 'minutes': 01216 if ($step != 1) { 01217 $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step; 01218 } 01219 01220 for ($i=0; $i<=59; $i+=$step) { 01221 $timeunits[$i] = sprintf("%02d",$i); 01222 } 01223 break; 01224 default: 01225 throw new coding_exception("Time type $type is not supported by html_writer::select_time()."); 01226 } 01227 01228 if (empty($attributes['id'])) { 01229 $attributes['id'] = self::random_id('ts_'); 01230 } 01231 $timerselector = self::select($timeunits, $name, $currentdate[$userdatetype], null, array('id'=>$attributes['id'])); 01232 $label = self::tag('label', get_string(substr($type, 0, -1), 'form'), array('for'=>$attributes['id'], 'class'=>'accesshide')); 01233 01234 return $label.$timerselector; 01235 } 01236 01244 public static function alist(array $items, array $attributes = null, $tag = 'ul') { 01245 //note: 'list' is a reserved keyword ;-) 01246 01247 $output = ''; 01248 01249 foreach ($items as $item) { 01250 $output .= html_writer::start_tag('li') . "\n"; 01251 $output .= $item . "\n"; 01252 $output .= html_writer::end_tag('li') . "\n"; 01253 } 01254 01255 return html_writer::tag($tag, $output, $attributes); 01256 } 01257 01264 public static function input_hidden_params(moodle_url $url, array $exclude = null) { 01265 $exclude = (array)$exclude; 01266 $params = $url->params(); 01267 foreach ($exclude as $key) { 01268 unset($params[$key]); 01269 } 01270 01271 $output = ''; 01272 foreach ($params as $key => $value) { 01273 $attributes = array('type'=>'hidden', 'name'=>$key, 'value'=>$value); 01274 $output .= self::empty_tag('input', $attributes)."\n"; 01275 } 01276 return $output; 01277 } 01278 01286 public static function script($jscode, $url=null) { 01287 if ($jscode) { 01288 $attributes = array('type'=>'text/javascript'); 01289 return self::tag('script', "\n//<![CDATA[\n$jscode\n//]]>\n", $attributes) . "\n"; 01290 01291 } else if ($url) { 01292 $attributes = array('type'=>'text/javascript', 'src'=>$url); 01293 return self::tag('script', '', $attributes) . "\n"; 01294 01295 } else { 01296 return ''; 01297 } 01298 } 01299 01311 public static function table(html_table $table) { 01312 // prepare table data and populate missing properties with reasonable defaults 01313 if (!empty($table->align)) { 01314 foreach ($table->align as $key => $aa) { 01315 if ($aa) { 01316 $table->align[$key] = 'text-align:'. fix_align_rtl($aa) .';'; // Fix for RTL languages 01317 } else { 01318 $table->align[$key] = null; 01319 } 01320 } 01321 } 01322 if (!empty($table->size)) { 01323 foreach ($table->size as $key => $ss) { 01324 if ($ss) { 01325 $table->size[$key] = 'width:'. $ss .';'; 01326 } else { 01327 $table->size[$key] = null; 01328 } 01329 } 01330 } 01331 if (!empty($table->wrap)) { 01332 foreach ($table->wrap as $key => $ww) { 01333 if ($ww) { 01334 $table->wrap[$key] = 'white-space:nowrap;'; 01335 } else { 01336 $table->wrap[$key] = ''; 01337 } 01338 } 01339 } 01340 if (!empty($table->head)) { 01341 foreach ($table->head as $key => $val) { 01342 if (!isset($table->align[$key])) { 01343 $table->align[$key] = null; 01344 } 01345 if (!isset($table->size[$key])) { 01346 $table->size[$key] = null; 01347 } 01348 if (!isset($table->wrap[$key])) { 01349 $table->wrap[$key] = null; 01350 } 01351 01352 } 01353 } 01354 if (empty($table->attributes['class'])) { 01355 $table->attributes['class'] = 'generaltable'; 01356 } 01357 if (!empty($table->tablealign)) { 01358 $table->attributes['class'] .= ' boxalign' . $table->tablealign; 01359 } 01360 01361 // explicitly assigned properties override those defined via $table->attributes 01362 $table->attributes['class'] = trim($table->attributes['class']); 01363 $attributes = array_merge($table->attributes, array( 01364 'id' => $table->id, 01365 'width' => $table->width, 01366 'summary' => $table->summary, 01367 'cellpadding' => $table->cellpadding, 01368 'cellspacing' => $table->cellspacing, 01369 )); 01370 $output = html_writer::start_tag('table', $attributes) . "\n"; 01371 01372 $countcols = 0; 01373 01374 if (!empty($table->head)) { 01375 $countcols = count($table->head); 01376 01377 $output .= html_writer::start_tag('thead', array()) . "\n"; 01378 $output .= html_writer::start_tag('tr', array()) . "\n"; 01379 $keys = array_keys($table->head); 01380 $lastkey = end($keys); 01381 01382 foreach ($table->head as $key => $heading) { 01383 // Convert plain string headings into html_table_cell objects 01384 if (!($heading instanceof html_table_cell)) { 01385 $headingtext = $heading; 01386 $heading = new html_table_cell(); 01387 $heading->text = $headingtext; 01388 $heading->header = true; 01389 } 01390 01391 if ($heading->header !== false) { 01392 $heading->header = true; 01393 } 01394 01395 if ($heading->header && empty($heading->scope)) { 01396 $heading->scope = 'col'; 01397 } 01398 01399 $heading->attributes['class'] .= ' header c' . $key; 01400 if (isset($table->headspan[$key]) && $table->headspan[$key] > 1) { 01401 $heading->colspan = $table->headspan[$key]; 01402 $countcols += $table->headspan[$key] - 1; 01403 } 01404 01405 if ($key == $lastkey) { 01406 $heading->attributes['class'] .= ' lastcol'; 01407 } 01408 if (isset($table->colclasses[$key])) { 01409 $heading->attributes['class'] .= ' ' . $table->colclasses[$key]; 01410 } 01411 $heading->attributes['class'] = trim($heading->attributes['class']); 01412 $attributes = array_merge($heading->attributes, array( 01413 'style' => $table->align[$key] . $table->size[$key] . $heading->style, 01414 'scope' => $heading->scope, 01415 'colspan' => $heading->colspan, 01416 )); 01417 01418 $tagtype = 'td'; 01419 if ($heading->header === true) { 01420 $tagtype = 'th'; 01421 } 01422 $output .= html_writer::tag($tagtype, $heading->text, $attributes) . "\n"; 01423 } 01424 $output .= html_writer::end_tag('tr') . "\n"; 01425 $output .= html_writer::end_tag('thead') . "\n"; 01426 01427 if (empty($table->data)) { 01428 // For valid XHTML strict every table must contain either a valid tr 01429 // or a valid tbody... both of which must contain a valid td 01430 $output .= html_writer::start_tag('tbody', array('class' => 'empty')); 01431 $output .= html_writer::tag('tr', html_writer::tag('td', '', array('colspan'=>count($table->head)))); 01432 $output .= html_writer::end_tag('tbody'); 01433 } 01434 } 01435 01436 if (!empty($table->data)) { 01437 $oddeven = 1; 01438 $keys = array_keys($table->data); 01439 $lastrowkey = end($keys); 01440 $output .= html_writer::start_tag('tbody', array()); 01441 01442 foreach ($table->data as $key => $row) { 01443 if (($row === 'hr') && ($countcols)) { 01444 $output .= html_writer::tag('td', html_writer::tag('div', '', array('class' => 'tabledivider')), array('colspan' => $countcols)); 01445 } else { 01446 // Convert array rows to html_table_rows and cell strings to html_table_cell objects 01447 if (!($row instanceof html_table_row)) { 01448 $newrow = new html_table_row(); 01449 01450 foreach ($row as $item) { 01451 $cell = new html_table_cell(); 01452 $cell->text = $item; 01453 $newrow->cells[] = $cell; 01454 } 01455 $row = $newrow; 01456 } 01457 01458 $oddeven = $oddeven ? 0 : 1; 01459 if (isset($table->rowclasses[$key])) { 01460 $row->attributes['class'] .= ' ' . $table->rowclasses[$key]; 01461 } 01462 01463 $row->attributes['class'] .= ' r' . $oddeven; 01464 if ($key == $lastrowkey) { 01465 $row->attributes['class'] .= ' lastrow'; 01466 } 01467 01468 $output .= html_writer::start_tag('tr', array('class' => trim($row->attributes['class']), 'style' => $row->style, 'id' => $row->id)) . "\n"; 01469 $keys2 = array_keys($row->cells); 01470 $lastkey = end($keys2); 01471 01472 $gotlastkey = false; //flag for sanity checking 01473 foreach ($row->cells as $key => $cell) { 01474 if ($gotlastkey) { 01475 //This should never happen. Why do we have a cell after the last cell? 01476 mtrace("A cell with key ($key) was found after the last key ($lastkey)"); 01477 } 01478 01479 if (!($cell instanceof html_table_cell)) { 01480 $mycell = new html_table_cell(); 01481 $mycell->text = $cell; 01482 $cell = $mycell; 01483 } 01484 01485 if (($cell->header === true) && empty($cell->scope)) { 01486 $cell->scope = 'row'; 01487 } 01488 01489 if (isset($table->colclasses[$key])) { 01490 $cell->attributes['class'] .= ' ' . $table->colclasses[$key]; 01491 } 01492 01493 $cell->attributes['class'] .= ' cell c' . $key; 01494 if ($key == $lastkey) { 01495 $cell->attributes['class'] .= ' lastcol'; 01496 $gotlastkey = true; 01497 } 01498 $tdstyle = ''; 01499 $tdstyle .= isset($table->align[$key]) ? $table->align[$key] : ''; 01500 $tdstyle .= isset($table->size[$key]) ? $table->size[$key] : ''; 01501 $tdstyle .= isset($table->wrap[$key]) ? $table->wrap[$key] : ''; 01502 $cell->attributes['class'] = trim($cell->attributes['class']); 01503 $tdattributes = array_merge($cell->attributes, array( 01504 'style' => $tdstyle . $cell->style, 01505 'colspan' => $cell->colspan, 01506 'rowspan' => $cell->rowspan, 01507 'id' => $cell->id, 01508 'abbr' => $cell->abbr, 01509 'scope' => $cell->scope, 01510 )); 01511 $tagtype = 'td'; 01512 if ($cell->header === true) { 01513 $tagtype = 'th'; 01514 } 01515 $output .= html_writer::tag($tagtype, $cell->text, $tdattributes) . "\n"; 01516 } 01517 } 01518 $output .= html_writer::end_tag('tr') . "\n"; 01519 } 01520 $output .= html_writer::end_tag('tbody') . "\n"; 01521 } 01522 $output .= html_writer::end_tag('table') . "\n"; 01523 01524 return $output; 01525 } 01526 01548 public static function label($text, $for, $colonize=true, array $attributes=array()) { 01549 if (!is_null($for)) { 01550 $attributes = array_merge($attributes, array('for' => $for)); 01551 } 01552 $text = trim($text); 01553 $label = self::tag('label', $text, $attributes); 01554 01555 /* 01556 // TODO $colonize disabled for now yet - see MDL-12192 for details 01557 if (!empty($text) and $colonize) { 01558 // the $text may end with the colon already, though it is bad string definition style 01559 $colon = get_string('labelsep', 'langconfig'); 01560 if (!empty($colon)) { 01561 $trimmed = trim($colon); 01562 if ((substr($text, -strlen($trimmed)) == $trimmed) or (substr($text, -1) == ':')) { 01563 //debugging('The label text should not end with colon or other label separator, 01564 // please fix the string definition.', DEBUG_DEVELOPER); 01565 } else { 01566 $label .= $colon; 01567 } 01568 } 01569 } 01570 */ 01571 01572 return $label; 01573 } 01574 } 01575 01576 // ==== JS writer and helper classes, will be probably moved elsewhere ====== 01577 01582 class js_writer { 01590 public static function function_call($function, array $arguments = null, $delay=0) { 01591 if ($arguments) { 01592 $arguments = array_map('json_encode', $arguments); 01593 $arguments = implode(', ', $arguments); 01594 } else { 01595 $arguments = ''; 01596 } 01597 $js = "$function($arguments);"; 01598 01599 if ($delay) { 01600 $delay = $delay * 1000; // in miliseconds 01601 $js = "setTimeout(function() { $js }, $delay);"; 01602 } 01603 return $js . "\n"; 01604 } 01605 01612 public static function function_call_with_Y($function, array $extraarguments = null) { 01613 if ($extraarguments) { 01614 $extraarguments = array_map('json_encode', $extraarguments); 01615 $arguments = 'Y, ' . implode(', ', $extraarguments); 01616 } else { 01617 $arguments = 'Y'; 01618 } 01619 return "$function($arguments);\n"; 01620 } 01621 01631 public static function object_init($var, $class, array $arguments = null, array $requirements = null, $delay=0) { 01632 if (is_array($arguments)) { 01633 $arguments = array_map('json_encode', $arguments); 01634 $arguments = implode(', ', $arguments); 01635 } 01636 01637 if ($var === null) { 01638 $js = "new $class(Y, $arguments);"; 01639 } else if (strpos($var, '.')!==false) { 01640 $js = "$var = new $class(Y, $arguments);"; 01641 } else { 01642 $js = "var $var = new $class(Y, $arguments);"; 01643 } 01644 01645 if ($delay) { 01646 $delay = $delay * 1000; // in miliseconds 01647 $js = "setTimeout(function() { $js }, $delay);"; 01648 } 01649 01650 if (count($requirements) > 0) { 01651 $requirements = implode("', '", $requirements); 01652 $js = "Y.use('$requirements', function(Y){ $js });"; 01653 } 01654 return $js."\n"; 01655 } 01656 01664 public static function set_variable($name, $value, $usevar=true) { 01665 $output = ''; 01666 01667 if ($usevar) { 01668 if (strpos($name, '.')) { 01669 $output .= ''; 01670 } else { 01671 $output .= 'var '; 01672 } 01673 } 01674 01675 $output .= "$name = ".json_encode($value).";"; 01676 01677 return $output; 01678 } 01679 01688 public static function event_handler($selector, $event, $function, array $arguments = null) { 01689 $selector = json_encode($selector); 01690 $output = "Y.on('$event', $function, $selector, null"; 01691 if (!empty($arguments)) { 01692 $output .= ', ' . json_encode($arguments); 01693 } 01694 return $output . ");\n"; 01695 } 01696 } 01697 01710 class html_table { 01714 public $id = null; 01718 public $attributes = array(); 01727 public $head; 01737 public $headspan; 01749 public $align; 01758 public $size; 01766 public $wrap; 01791 public $data; 01796 public $width = null; 01801 public $tablealign = null; 01806 public $cellpadding = null; 01811 public $cellspacing = null; 01821 public $rowclasses; 01832 public $colclasses; 01836 public $summary; 01837 01841 public function __construct() { 01842 $this->attributes['class'] = ''; 01843 } 01844 } 01845 01846 01854 class html_table_row { 01858 public $id = null; 01862 public $cells = array(); 01866 public $style = null; 01870 public $attributes = array(); 01871 01876 public function __construct(array $cells=null) { 01877 $this->attributes['class'] = ''; 01878 $cells = (array)$cells; 01879 foreach ($cells as $cell) { 01880 if ($cell instanceof html_table_cell) { 01881 $this->cells[] = $cell; 01882 } else { 01883 $this->cells[] = new html_table_cell($cell); 01884 } 01885 } 01886 } 01887 } 01888 01889 01897 class html_table_cell { 01901 public $id = null; 01905 public $text; 01909 public $abbr = null; 01913 public $colspan = null; 01917 public $rowspan = null; 01921 public $scope = null; 01925 public $header = null; 01929 public $style = null; 01933 public $attributes = array(); 01934 01935 public function __construct($text = null) { 01936 $this->text = $text; 01937 $this->attributes['class'] = ''; 01938 } 01939 } 01940 01941 01943 01944 01952 class paging_bar implements renderable { 01956 public $maxdisplay = 18; 01960 public $totalcount; 01964 public $page; 01968 public $perpage; 01973 public $baseurl; 01977 public $pagevar; 01981 public $previouslink = null; 01985 public $nextlink = null; 01989 public $firstlink = null; 01993 public $lastlink = null; 01997 public $pagelinks = array(); 01998 02008 public function __construct($totalcount, $page, $perpage, $baseurl, $pagevar = 'page') { 02009 $this->totalcount = $totalcount; 02010 $this->page = $page; 02011 $this->perpage = $perpage; 02012 $this->baseurl = $baseurl; 02013 $this->pagevar = $pagevar; 02014 } 02015 02019 public function prepare(renderer_base $output, moodle_page $page, $target) { 02020 if (!isset($this->totalcount) || is_null($this->totalcount)) { 02021 throw new coding_exception('paging_bar requires a totalcount value.'); 02022 } 02023 if (!isset($this->page) || is_null($this->page)) { 02024 throw new coding_exception('paging_bar requires a page value.'); 02025 } 02026 if (empty($this->perpage)) { 02027 throw new coding_exception('paging_bar requires a perpage value.'); 02028 } 02029 if (empty($this->baseurl)) { 02030 throw new coding_exception('paging_bar requires a baseurl value.'); 02031 } 02032 02033 if ($this->totalcount > $this->perpage) { 02034 $pagenum = $this->page - 1; 02035 02036 if ($this->page > 0) { 02037 $this->previouslink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$pagenum)), get_string('previous'), array('class'=>'previous')); 02038 } 02039 02040 if ($this->perpage > 0) { 02041 $lastpage = ceil($this->totalcount / $this->perpage); 02042 } else { 02043 $lastpage = 1; 02044 } 02045 02046 if ($this->page > 15) { 02047 $startpage = $this->page - 10; 02048 02049 $this->firstlink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>0)), '1', array('class'=>'first')); 02050 } else { 02051 $startpage = 0; 02052 } 02053 02054 $currpage = $startpage; 02055 $displaycount = $displaypage = 0; 02056 02057 while ($displaycount < $this->maxdisplay and $currpage < $lastpage) { 02058 $displaypage = $currpage + 1; 02059 02060 if ($this->page == $currpage) { 02061 $this->pagelinks[] = $displaypage; 02062 } else { 02063 $pagelink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$currpage)), $displaypage); 02064 $this->pagelinks[] = $pagelink; 02065 } 02066 02067 $displaycount++; 02068 $currpage++; 02069 } 02070 02071 if ($currpage < $lastpage) { 02072 $lastpageactual = $lastpage - 1; 02073 $this->lastlink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$lastpageactual)), $lastpage, array('class'=>'last')); 02074 } 02075 02076 $pagenum = $this->page + 1; 02077 02078 if ($pagenum != $displaypage) { 02079 $this->nextlink = html_writer::link(new moodle_url($this->baseurl, array($this->pagevar=>$pagenum)), get_string('next'), array('class'=>'next')); 02080 } 02081 } 02082 } 02083 } 02084 02085 02101 class block_contents { 02103 protected static $idcounter = 1; 02104 02105 const NOT_HIDEABLE = 0; 02106 const VISIBLE = 1; 02107 const HIDDEN = 2; 02108 02115 public $skipid; 02116 02121 public $blockinstanceid = 0; 02122 02128 public $blockpositionid = 0; 02129 02134 public $attributes; 02135 02141 public $title = ''; 02142 02146 public $content = ''; 02147 02151 public $footer = ''; 02152 02159 public $annotation = ''; 02160 02165 public $collapsible = self::NOT_HIDEABLE; 02166 02173 public $controls = array(); 02174 02175 02180 public function __construct(array $attributes=null) { 02181 $this->skipid = self::$idcounter; 02182 self::$idcounter += 1; 02183 02184 if ($attributes) { 02185 // standard block 02186 $this->attributes = $attributes; 02187 } else { 02188 // simple "fake" blocks used in some modules and "Add new block" block 02189 $this->attributes = array('class'=>'block'); 02190 } 02191 } 02192 02198 public function add_class($class) { 02199 $this->attributes['class'] .= ' '.$class; 02200 } 02201 } 02202 02203 02215 class block_move_target { 02220 public $url; 02225 public $text; 02226 02232 public function __construct($text, moodle_url $url) { 02233 $this->text = $text; 02234 $this->url = $url; 02235 } 02236 } 02237 02248 class custom_menu_item implements renderable { 02253 protected $text; 02258 protected $url; 02263 protected $title; 02268 protected $sort; 02273 protected $parent; 02278 protected $children = array(); 02283 protected $lastsort = 0; 02294 public function __construct($text, moodle_url $url=null, $title=null, $sort = null, custom_menu_item $parent=null) { 02295 $this->text = $text; 02296 $this->url = $url; 02297 $this->title = $title; 02298 $this->sort = (int)$sort; 02299 $this->parent = $parent; 02300 } 02301 02311 public function add($text, moodle_url $url=null, $title=null, $sort = null) { 02312 $key = count($this->children); 02313 if (empty($sort)) { 02314 $sort = $this->lastsort + 1; 02315 } 02316 $this->children[$key] = new custom_menu_item($text, $url, $title, $sort, $this); 02317 $this->lastsort = (int)$sort; 02318 return $this->children[$key]; 02319 } 02324 public function get_text() { 02325 return $this->text; 02326 } 02331 public function get_url() { 02332 return $this->url; 02333 } 02338 public function get_title() { 02339 return $this->title; 02340 } 02345 public function get_children() { 02346 $this->sort(); 02347 return $this->children; 02348 } 02353 public function get_sort_order() { 02354 return $this->sort; 02355 } 02360 public function get_parent() { 02361 return $this->parent; 02362 } 02366 public function sort() { 02367 usort($this->children, array('custom_menu','sort_custom_menu_items')); 02368 } 02373 public function has_children() { 02374 return (count($this->children) > 0); 02375 } 02376 02381 public function set_text($text) { 02382 $this->text = (string)$text; 02383 } 02384 02389 public function set_title($title) { 02390 $this->title = (string)$title; 02391 } 02392 02397 public function set_url(moodle_url $url) { 02398 $this->url = $url; 02399 } 02400 } 02401 02416 class custom_menu extends custom_menu_item { 02417 02419 protected $currentlanguage = null; 02420 02427 public function __construct($definition = '', $currentlanguage = null) { 02428 02429 $this->currentlanguage = $currentlanguage; 02430 parent::__construct('root'); // create virtual root element of the menu 02431 if (!empty($definition)) { 02432 $this->override_children(self::convert_text_to_menu_nodes($definition, $currentlanguage)); 02433 } 02434 } 02435 02440 public function override_children(array $children) { 02441 $this->children = array(); 02442 foreach ($children as $child) { 02443 if ($child instanceof custom_menu_item) { 02444 $this->children[] = $child; 02445 } 02446 } 02447 } 02448 02475 public static function convert_text_to_menu_nodes($text, $language = null) { 02476 $lines = explode("\n", $text); 02477 $children = array(); 02478 $lastchild = null; 02479 $lastdepth = null; 02480 $lastsort = 0; 02481 foreach ($lines as $line) { 02482 $line = trim($line); 02483 $bits = explode('|', $line, 4); // name|url|title|langs 02484 if (!array_key_exists(0, $bits) or empty($bits[0])) { 02485 // Every item must have a name to be valid 02486 continue; 02487 } else { 02488 $bits[0] = ltrim($bits[0],'-'); 02489 } 02490 if (!array_key_exists(1, $bits) or empty($bits[1])) { 02491 // Set the url to null 02492 $bits[1] = null; 02493 } else { 02494 // Make sure the url is a moodle url 02495 $bits[1] = new moodle_url(trim($bits[1])); 02496 } 02497 if (!array_key_exists(2, $bits) or empty($bits[2])) { 02498 // Set the title to null seeing as there isn't one 02499 $bits[2] = $bits[0]; 02500 } 02501 if (!array_key_exists(3, $bits) or empty($bits[3])) { 02502 // The item is valid for all languages 02503 $itemlangs = null; 02504 } else { 02505 $itemlangs = array_map('trim', explode(',', $bits[3])); 02506 } 02507 if (!empty($language) and !empty($itemlangs)) { 02508 // check that the item is intended for the current language 02509 if (!in_array($language, $itemlangs)) { 02510 continue; 02511 } 02512 } 02513 // Set an incremental sort order to keep it simple. 02514 $lastsort++; 02515 if (preg_match('/^(\-*)/', $line, $match) && $lastchild != null && $lastdepth !== null) { 02516 $depth = strlen($match[1]); 02517 if ($depth < $lastdepth) { 02518 $difference = $lastdepth - $depth; 02519 if ($lastdepth > 1 && $lastdepth != $difference) { 02520 $tempchild = $lastchild->get_parent(); 02521 for ($i =0; $i < $difference; $i++) { 02522 $tempchild = $tempchild->get_parent(); 02523 } 02524 $lastchild = $tempchild->add($bits[0], $bits[1], $bits[2], $lastsort); 02525 } else { 02526 $depth = 0; 02527 $lastchild = new custom_menu_item($bits[0], $bits[1], $bits[2], $lastsort); 02528 $children[] = $lastchild; 02529 } 02530 } else if ($depth > $lastdepth) { 02531 $depth = $lastdepth + 1; 02532 $lastchild = $lastchild->add($bits[0], $bits[1], $bits[2], $lastsort); 02533 } else { 02534 if ($depth == 0) { 02535 $lastchild = new custom_menu_item($bits[0], $bits[1], $bits[2], $lastsort); 02536 $children[] = $lastchild; 02537 } else { 02538 $lastchild = $lastchild->get_parent()->add($bits[0], $bits[1], $bits[2], $lastsort); 02539 } 02540 } 02541 } else { 02542 $depth = 0; 02543 $lastchild = new custom_menu_item($bits[0], $bits[1], $bits[2], $lastsort); 02544 $children[] = $lastchild; 02545 } 02546 $lastdepth = $depth; 02547 } 02548 return $children; 02549 } 02550 02561 public static function sort_custom_menu_items(custom_menu_item $itema, custom_menu_item $itemb) { 02562 $itema = $itema->get_sort_order(); 02563 $itemb = $itemb->get_sort_order(); 02564 if ($itema == $itemb) { 02565 return 0; 02566 } 02567 return ($itema > $itemb) ? +1 : -1; 02568 } 02569 }