|
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 00034 defined('MOODLE_INTERNAL') || die(); 00035 00037 00039 00043 define('FORMAT_MOODLE', '0'); // Does all sorts of transformations and filtering 00044 00048 define('FORMAT_HTML', '1'); // Plain HTML (with some tags stripped) 00049 00053 define('FORMAT_PLAIN', '2'); // Plain text (even tags are printed in full) 00054 00060 define('FORMAT_WIKI', '3'); // Wiki-formatted text 00061 00065 define('FORMAT_MARKDOWN', '4'); // Markdown-formatted text http://daringfireball.net/projects/markdown/ 00066 00070 define('URL_MATCH_BASE', 0); 00074 define('URL_MATCH_PARAMS', 1); 00078 define('URL_MATCH_EXACT', 2); 00079 00081 00094 function s($var, $obsolete = false) { 00095 00096 if ($var === '0' or $var === false or $var === 0) { 00097 return '0'; 00098 } 00099 00100 return preg_replace("/&#(\d+|x[0-7a-fA-F]+);/i", "&#$1;", htmlspecialchars($var, ENT_QUOTES, 'UTF-8', true)); 00101 } 00102 00116 function p($var, $obsolete = false) { 00117 echo s($var, $obsolete); 00118 } 00119 00128 function addslashes_js($var) { 00129 if (is_string($var)) { 00130 $var = str_replace('\\', '\\\\', $var); 00131 $var = str_replace(array('\'', '"', "\n", "\r", "\0"), array('\\\'', '\\"', '\\n', '\\r', '\\0'), $var); 00132 $var = str_replace('</', '<\/', $var); // XHTML compliance 00133 } else if (is_array($var)) { 00134 $var = array_map('addslashes_js', $var); 00135 } else if (is_object($var)) { 00136 $a = get_object_vars($var); 00137 foreach ($a as $key=>$value) { 00138 $a[$key] = addslashes_js($value); 00139 } 00140 $var = (object)$a; 00141 } 00142 return $var; 00143 } 00144 00153 function strip_querystring($url) { 00154 00155 if ($commapos = strpos($url, '?')) { 00156 return substr($url, 0, $commapos); 00157 } else { 00158 return $url; 00159 } 00160 } 00161 00169 function get_referer($stripquery=true) { 00170 if (isset($_SERVER['HTTP_REFERER'])) { 00171 if ($stripquery) { 00172 return strip_querystring($_SERVER['HTTP_REFERER']); 00173 } else { 00174 return $_SERVER['HTTP_REFERER']; 00175 } 00176 } else { 00177 return ''; 00178 } 00179 } 00180 00181 00193 function me() { 00194 global $ME; 00195 return $ME; 00196 } 00197 00212 function qualified_me() { 00213 global $FULLME; 00214 return $FULLME; 00215 } 00216 00235 class moodle_url { 00240 protected $scheme = ''; 00245 protected $host = ''; 00250 protected $port = ''; 00255 protected $user = ''; 00260 protected $pass = ''; 00265 protected $path = ''; 00270 protected $slashargument = ''; 00275 protected $anchor = null; 00280 protected $params = array(); // Associative array of query string params 00281 00293 public function __construct($url, array $params = null) { 00294 global $CFG; 00295 00296 if ($url instanceof moodle_url) { 00297 $this->scheme = $url->scheme; 00298 $this->host = $url->host; 00299 $this->port = $url->port; 00300 $this->user = $url->user; 00301 $this->pass = $url->pass; 00302 $this->path = $url->path; 00303 $this->slashargument = $url->slashargument; 00304 $this->params = $url->params; 00305 $this->anchor = $url->anchor; 00306 00307 } else { 00308 // detect if anchor used 00309 $apos = strpos($url, '#'); 00310 if ($apos !== false) { 00311 $anchor = substr($url, $apos); 00312 $anchor = ltrim($anchor, '#'); 00313 $this->set_anchor($anchor); 00314 $url = substr($url, 0, $apos); 00315 } 00316 00317 // normalise shortened form of our url ex.: '/course/view.php' 00318 if (strpos($url, '/') === 0) { 00319 // we must not use httpswwwroot here, because it might be url of other page, 00320 // devs have to use httpswwwroot explicitly when creating new moodle_url 00321 $url = $CFG->wwwroot.$url; 00322 } 00323 00324 // now fix the admin links if needed, no need to mess with httpswwwroot 00325 if ($CFG->admin !== 'admin') { 00326 if (strpos($url, "$CFG->wwwroot/admin/") === 0) { 00327 $url = str_replace("$CFG->wwwroot/admin/", "$CFG->wwwroot/$CFG->admin/", $url); 00328 } 00329 } 00330 00331 // parse the $url 00332 $parts = parse_url($url); 00333 if ($parts === false) { 00334 throw new moodle_exception('invalidurl'); 00335 } 00336 if (isset($parts['query'])) { 00337 // note: the values may not be correctly decoded, 00338 // url parameters should be always passed as array 00339 parse_str(str_replace('&', '&', $parts['query']), $this->params); 00340 } 00341 unset($parts['query']); 00342 foreach ($parts as $key => $value) { 00343 $this->$key = $value; 00344 } 00345 00346 // detect slashargument value from path - we do not support directory names ending with .php 00347 $pos = strpos($this->path, '.php/'); 00348 if ($pos !== false) { 00349 $this->slashargument = substr($this->path, $pos + 4); 00350 $this->path = substr($this->path, 0, $pos + 4); 00351 } 00352 } 00353 00354 $this->params($params); 00355 } 00356 00365 public function params(array $params = null) { 00366 $params = (array)$params; 00367 00368 foreach ($params as $key=>$value) { 00369 if (is_int($key)) { 00370 throw new coding_exception('Url parameters can not have numeric keys!'); 00371 } 00372 if (!is_string($value)) { 00373 if (is_array($value)) { 00374 throw new coding_exception('Url parameters values can not be arrays!'); 00375 } 00376 if (is_object($value) and !method_exists($value, '__toString')) { 00377 throw new coding_exception('Url parameters values can not be objects, unless __toString() is defined!'); 00378 } 00379 } 00380 $this->params[$key] = (string)$value; 00381 } 00382 return $this->params; 00383 } 00384 00396 public function remove_params($params = null) { 00397 if (!is_array($params)) { 00398 $params = func_get_args(); 00399 } 00400 foreach ($params as $param) { 00401 unset($this->params[$param]); 00402 } 00403 return $this->params; 00404 } 00405 00411 public function remove_all_params($params = null) { 00412 $this->params = array(); 00413 $this->slashargument = ''; 00414 } 00415 00425 public function param($paramname, $newvalue = '') { 00426 if (func_num_args() > 1) { 00427 // set new value 00428 $this->params(array($paramname=>$newvalue)); 00429 } 00430 if (isset($this->params[$paramname])) { 00431 return $this->params[$paramname]; 00432 } else { 00433 return null; 00434 } 00435 } 00436 00442 protected function merge_overrideparams(array $overrideparams = null) { 00443 $overrideparams = (array)$overrideparams; 00444 $params = $this->params; 00445 foreach ($overrideparams as $key=>$value) { 00446 if (is_int($key)) { 00447 throw new coding_exception('Overridden parameters can not have numeric keys!'); 00448 } 00449 if (is_array($value)) { 00450 throw new coding_exception('Overridden parameters values can not be arrays!'); 00451 } 00452 if (is_object($value) and !method_exists($value, '__toString')) { 00453 throw new coding_exception('Overridden parameters values can not be objects, unless __toString() is defined!'); 00454 } 00455 $params[$key] = (string)$value; 00456 } 00457 return $params; 00458 } 00459 00469 public function get_query_string($escaped = true, array $overrideparams = null) { 00470 $arr = array(); 00471 if ($overrideparams !== null) { 00472 $params = $this->merge_overrideparams($overrideparams); 00473 } else { 00474 $params = $this->params; 00475 } 00476 foreach ($params as $key => $val) { 00477 $arr[] = rawurlencode($key)."=".rawurlencode($val); 00478 } 00479 if ($escaped) { 00480 return implode('&', $arr); 00481 } else { 00482 return implode('&', $arr); 00483 } 00484 } 00485 00490 public function __toString() { 00491 return $this->out(true); 00492 } 00493 00504 public function out($escaped = true, array $overrideparams = null) { 00505 if (!is_bool($escaped)) { 00506 debugging('Escape parameter must be of type boolean, '.gettype($escaped).' given instead.'); 00507 } 00508 00509 $uri = $this->out_omit_querystring().$this->slashargument; 00510 00511 $querystring = $this->get_query_string($escaped, $overrideparams); 00512 if ($querystring !== '') { 00513 $uri .= '?' . $querystring; 00514 } 00515 if (!is_null($this->anchor)) { 00516 $uri .= '#'.$this->anchor; 00517 } 00518 00519 return $uri; 00520 } 00521 00528 public function out_omit_querystring($includeanchor = false) { 00529 00530 $uri = $this->scheme ? $this->scheme.':'.((strtolower($this->scheme) == 'mailto') ? '':'//'): ''; 00531 $uri .= $this->user ? $this->user.($this->pass? ':'.$this->pass:'').'@':''; 00532 $uri .= $this->host ? $this->host : ''; 00533 $uri .= $this->port ? ':'.$this->port : ''; 00534 $uri .= $this->path ? $this->path : ''; 00535 if ($includeanchor and !is_null($this->anchor)) { 00536 $uri .= '#' . $this->anchor; 00537 } 00538 00539 return $uri; 00540 } 00541 00549 public function compare(moodle_url $url, $matchtype = URL_MATCH_EXACT) { 00550 00551 $baseself = $this->out_omit_querystring(); 00552 $baseother = $url->out_omit_querystring(); 00553 00554 // Append index.php if there is no specific file 00555 if (substr($baseself,-1)=='/') { 00556 $baseself .= 'index.php'; 00557 } 00558 if (substr($baseother,-1)=='/') { 00559 $baseother .= 'index.php'; 00560 } 00561 00562 // Compare the two base URLs 00563 if ($baseself != $baseother) { 00564 return false; 00565 } 00566 00567 if ($matchtype == URL_MATCH_BASE) { 00568 return true; 00569 } 00570 00571 $urlparams = $url->params(); 00572 foreach ($this->params() as $param => $value) { 00573 if ($param == 'sesskey') { 00574 continue; 00575 } 00576 if (!array_key_exists($param, $urlparams) || $urlparams[$param] != $value) { 00577 return false; 00578 } 00579 } 00580 00581 if ($matchtype == URL_MATCH_PARAMS) { 00582 return true; 00583 } 00584 00585 foreach ($urlparams as $param => $value) { 00586 if ($param == 'sesskey') { 00587 continue; 00588 } 00589 if (!array_key_exists($param, $this->params()) || $this->param($param) != $value) { 00590 return false; 00591 } 00592 } 00593 00594 return true; 00595 } 00596 00601 public function set_anchor($anchor) { 00602 if (is_null($anchor)) { 00603 // remove 00604 $this->anchor = null; 00605 } else if ($anchor === '') { 00606 // special case, used as empty link 00607 $this->anchor = ''; 00608 } else if (preg_match('|[a-zA-Z\_\:][a-zA-Z0-9\_\-\.\:]*|', $anchor)) { 00609 // Match the anchor against the NMTOKEN spec 00610 $this->anchor = $anchor; 00611 } else { 00612 // bad luck, no valid anchor found 00613 $this->anchor = null; 00614 } 00615 } 00616 00624 public function set_slashargument($path, $parameter = 'file', $supported = NULL) { 00625 global $CFG; 00626 if (is_null($supported)) { 00627 $supported = $CFG->slasharguments; 00628 } 00629 00630 if ($supported) { 00631 $parts = explode('/', $path); 00632 $parts = array_map('rawurlencode', $parts); 00633 $path = implode('/', $parts); 00634 $this->slashargument = $path; 00635 unset($this->params[$parameter]); 00636 00637 } else { 00638 $this->slashargument = ''; 00639 $this->params[$parameter] = $path; 00640 } 00641 } 00642 00643 // == static factory methods == 00644 00652 public static function make_file_url($urlbase, $path, $forcedownload = false) { 00653 global $CFG; 00654 00655 $params = array(); 00656 if ($forcedownload) { 00657 $params['forcedownload'] = 1; 00658 } 00659 00660 $url = new moodle_url($urlbase, $params); 00661 $url->set_slashargument($path); 00662 00663 return $url; 00664 } 00665 00679 public static function make_pluginfile_url($contextid, $component, $area, $itemid, $pathname, $filename, $forcedownload = false) { 00680 global $CFG; 00681 $urlbase = "$CFG->httpswwwroot/pluginfile.php"; 00682 if ($itemid === NULL) { 00683 return self::make_file_url($urlbase, "/$contextid/$component/$area".$pathname.$filename, $forcedownload); 00684 } else { 00685 return self::make_file_url($urlbase, "/$contextid/$component/$area/$itemid".$pathname.$filename, $forcedownload); 00686 } 00687 } 00688 00698 public static function make_draftfile_url($draftid, $pathname, $filename, $forcedownload = false) { 00699 global $CFG, $USER; 00700 $urlbase = "$CFG->httpswwwroot/draftfile.php"; 00701 $context = get_context_instance(CONTEXT_USER, $USER->id); 00702 00703 return self::make_file_url($urlbase, "/$context->id/user/draft/$draftid".$pathname.$filename, $forcedownload); 00704 } 00705 00714 public static function make_legacyfile_url($courseid, $filepath, $forcedownload = false) { 00715 global $CFG; 00716 00717 $urlbase = "$CFG->wwwroot/file.php"; 00718 return self::make_file_url($urlbase, '/'.$courseid.'/'.$filepath, $forcedownload); 00719 } 00720 } 00721 00735 function data_submitted() { 00736 00737 if (empty($_POST)) { 00738 return false; 00739 } else { 00740 return (object)fix_utf8($_POST); 00741 } 00742 } 00743 00755 function break_up_long_words($string, $maxsize=20, $cutchar=' ') { 00756 00758 $textlib = textlib_get_instance(); 00759 00761 $tags = array(); 00762 filter_save_tags($string,$tags); 00763 00765 $output = ''; 00766 $length = $textlib->strlen($string); 00767 $wordlength = 0; 00768 00769 for ($i=0; $i<$length; $i++) { 00770 $char = $textlib->substr($string, $i, 1); 00771 if ($char == ' ' or $char == "\t" or $char == "\n" or $char == "\r" or $char == "<" or $char == ">") { 00772 $wordlength = 0; 00773 } else { 00774 $wordlength++; 00775 if ($wordlength > $maxsize) { 00776 $output .= $cutchar; 00777 $wordlength = 0; 00778 } 00779 } 00780 $output .= $char; 00781 } 00782 00784 if (!empty($tags)) { 00785 $output = str_replace(array_keys($tags), $tags, $output); 00786 } 00787 00788 return $output; 00789 } 00790 00802 function close_window($delay = 0, $reloadopener = false) { 00803 global $PAGE, $OUTPUT; 00804 00805 if (!$PAGE->headerprinted) { 00806 $PAGE->set_title(get_string('closewindow')); 00807 echo $OUTPUT->header(); 00808 } else { 00809 $OUTPUT->container_end_all(false); 00810 } 00811 00812 if ($reloadopener) { 00813 // Trigger the reload immediately, even if the reload is after a delay. 00814 $PAGE->requires->js_function_call('window.opener.location.reload', array(true)); 00815 } 00816 $OUTPUT->notification(get_string('windowclosing'), 'notifysuccess'); 00817 00818 $PAGE->requires->js_function_call('close_window', array(new stdClass()), false, $delay); 00819 00820 echo $OUTPUT->footer(); 00821 exit; 00822 } 00823 00834 function page_doc_link($text='') { 00835 global $CFG, $PAGE, $OUTPUT; 00836 00837 if (empty($CFG->docroot) || during_initial_install()) { 00838 return ''; 00839 } 00840 if (!has_capability('moodle/site:doclinks', $PAGE->context)) { 00841 return ''; 00842 } 00843 00844 $path = $PAGE->docspath; 00845 if (!$path) { 00846 return ''; 00847 } 00848 return $OUTPUT->doc_link($path, $text); 00849 } 00850 00851 00858 function validate_email($address) { 00859 00860 return (preg_match('#^[-!\#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+'. 00861 '(\.[-!\#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+)*'. 00862 '@'. 00863 '[-!\#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'. 00864 '[-!\#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$#', 00865 $address)); 00866 } 00867 00877 function get_file_argument() { 00878 global $SCRIPT; 00879 00880 $relativepath = optional_param('file', FALSE, PARAM_PATH); 00881 00882 if ($relativepath !== false and $relativepath !== '') { 00883 return $relativepath; 00884 } 00885 $relativepath = false; 00886 00887 // then try extract file from the slasharguments 00888 if (stripos($_SERVER['SERVER_SOFTWARE'], 'iis') !== false) { 00889 // NOTE: ISS tends to convert all file paths to single byte DOS encoding, 00890 // we can not use other methods because they break unicode chars, 00891 // the only way is to use URL rewriting 00892 if (isset($_SERVER['PATH_INFO']) and $_SERVER['PATH_INFO'] !== '') { 00893 // check that PATH_INFO works == must not contain the script name 00894 if (strpos($_SERVER['PATH_INFO'], $SCRIPT) === false) { 00895 $relativepath = clean_param(urldecode($_SERVER['PATH_INFO']), PARAM_PATH); 00896 } 00897 } 00898 } else { 00899 // all other apache-like servers depend on PATH_INFO 00900 if (isset($_SERVER['PATH_INFO'])) { 00901 if (isset($_SERVER['SCRIPT_NAME']) and strpos($_SERVER['PATH_INFO'], $_SERVER['SCRIPT_NAME']) === 0) { 00902 $relativepath = substr($_SERVER['PATH_INFO'], strlen($_SERVER['SCRIPT_NAME'])); 00903 } else { 00904 $relativepath = $_SERVER['PATH_INFO']; 00905 } 00906 $relativepath = clean_param($relativepath, PARAM_PATH); 00907 } 00908 } 00909 00910 00911 return $relativepath; 00912 } 00913 00923 function format_text_menu() { 00924 return array (FORMAT_MOODLE => get_string('formattext'), 00925 FORMAT_HTML => get_string('formathtml'), 00926 FORMAT_PLAIN => get_string('formatplain'), 00927 FORMAT_MARKDOWN => get_string('formatmarkdown')); 00928 } 00929 00962 function format_text($text, $format = FORMAT_MOODLE, $options = NULL, $courseid_do_not_use = NULL) { 00963 global $CFG, $COURSE, $DB, $PAGE; 00964 static $croncache = array(); 00965 00966 if ($text === '' || is_null($text)) { 00967 return ''; // no need to do any filters and cleaning 00968 } 00969 00970 $options = (array)$options; // detach object, we can not modify it 00971 00972 if (!isset($options['trusted'])) { 00973 $options['trusted'] = false; 00974 } 00975 if (!isset($options['noclean'])) { 00976 if ($options['trusted'] and trusttext_active()) { 00977 // no cleaning if text trusted and noclean not specified 00978 $options['noclean'] = true; 00979 } else { 00980 $options['noclean'] = false; 00981 } 00982 } 00983 if (!isset($options['nocache'])) { 00984 $options['nocache'] = false; 00985 } 00986 if (!isset($options['filter'])) { 00987 $options['filter'] = true; 00988 } 00989 if (!isset($options['para'])) { 00990 $options['para'] = true; 00991 } 00992 if (!isset($options['newlines'])) { 00993 $options['newlines'] = true; 00994 } 00995 if (!isset($options['overflowdiv'])) { 00996 $options['overflowdiv'] = false; 00997 } 00998 00999 // Calculate best context 01000 if (empty($CFG->version) or $CFG->version < 2010072800 or during_initial_install()) { 01001 // do not filter anything during installation or before upgrade completes 01002 $context = null; 01003 01004 } else if (isset($options['context'])) { // first by explicit passed context option 01005 if (is_object($options['context'])) { 01006 $context = $options['context']; 01007 } else { 01008 $context = get_context_instance_by_id($options['context']); 01009 } 01010 } else if ($courseid_do_not_use) { 01011 // legacy courseid 01012 $context = get_context_instance(CONTEXT_COURSE, $courseid_do_not_use); 01013 } else { 01014 // fallback to $PAGE->context this may be problematic in CLI and other non-standard pages :-( 01015 $context = $PAGE->context; 01016 } 01017 01018 if (!$context) { 01019 // either install/upgrade or something has gone really wrong because context does not exist (yet?) 01020 $options['nocache'] = true; 01021 $options['filter'] = false; 01022 } 01023 01024 if ($options['filter']) { 01025 $filtermanager = filter_manager::instance(); 01026 } else { 01027 $filtermanager = new null_filter_manager(); 01028 } 01029 01030 if (!empty($CFG->cachetext) and empty($options['nocache'])) { 01031 $hashstr = $text.'-'.$filtermanager->text_filtering_hash($context).'-'.$context->id.'-'.current_language().'-'. 01032 (int)$format.(int)$options['trusted'].(int)$options['noclean']. 01033 (int)$options['para'].(int)$options['newlines']; 01034 01035 $time = time() - $CFG->cachetext; 01036 $md5key = md5($hashstr); 01037 if (CLI_SCRIPT) { 01038 if (isset($croncache[$md5key])) { 01039 return $croncache[$md5key]; 01040 } 01041 } 01042 01043 if ($oldcacheitem = $DB->get_record('cache_text', array('md5key'=>$md5key), '*', IGNORE_MULTIPLE)) { 01044 if ($oldcacheitem->timemodified >= $time) { 01045 if (CLI_SCRIPT) { 01046 if (count($croncache) > 150) { 01047 reset($croncache); 01048 $key = key($croncache); 01049 unset($croncache[$key]); 01050 } 01051 $croncache[$md5key] = $oldcacheitem->formattedtext; 01052 } 01053 return $oldcacheitem->formattedtext; 01054 } 01055 } 01056 } 01057 01058 switch ($format) { 01059 case FORMAT_HTML: 01060 if (!$options['noclean']) { 01061 $text = clean_text($text, FORMAT_HTML, $options); 01062 } 01063 $text = $filtermanager->filter_text($text, $context, array('originalformat' => FORMAT_HTML, 'noclean' => $options['noclean'])); 01064 break; 01065 01066 case FORMAT_PLAIN: 01067 $text = s($text); // cleans dangerous JS 01068 $text = rebuildnolinktag($text); 01069 $text = str_replace(' ', ' ', $text); 01070 $text = nl2br($text); 01071 break; 01072 01073 case FORMAT_WIKI: 01074 // this format is deprecated 01075 $text = '<p>NOTICE: Wiki-like formatting has been removed from Moodle. You should not be seeing 01076 this message as all texts should have been converted to Markdown format instead. 01077 Please post a bug report to http://moodle.org/bugs with information about where you 01078 saw this message.</p>'.s($text); 01079 break; 01080 01081 case FORMAT_MARKDOWN: 01082 $text = markdown_to_html($text); 01083 if (!$options['noclean']) { 01084 $text = clean_text($text, FORMAT_HTML, $options); 01085 } 01086 $text = $filtermanager->filter_text($text, $context, array('originalformat' => FORMAT_MARKDOWN, 'noclean' => $options['noclean'])); 01087 break; 01088 01089 default: // FORMAT_MOODLE or anything else 01090 $text = text_to_html($text, null, $options['para'], $options['newlines']); 01091 if (!$options['noclean']) { 01092 $text = clean_text($text, FORMAT_HTML, $options); 01093 } 01094 $text = $filtermanager->filter_text($text, $context, array('originalformat' => $format, 'noclean' => $options['noclean'])); 01095 break; 01096 } 01097 if ($options['filter']) { 01098 // at this point there should not be any draftfile links any more, 01099 // this happens when developers forget to post process the text. 01100 // The only potential problem is that somebody might try to format 01101 // the text before storing into database which would be itself big bug. 01102 $text = str_replace("\"$CFG->httpswwwroot/draftfile.php", "\"$CFG->httpswwwroot/brokenfile.php#", $text); 01103 } 01104 01105 // Warn people that we have removed this old mechanism, just in case they 01106 // were stupid enough to rely on it. 01107 if (isset($CFG->currenttextiscacheable)) { 01108 debugging('Once upon a time, Moodle had a truly evil use of global variables ' . 01109 'called $CFG->currenttextiscacheable. The good news is that this no ' . 01110 'longer exists. The bad news is that you seem to be using a filter that '. 01111 'relies on it. Please seek out and destroy that filter code.', DEBUG_DEVELOPER); 01112 } 01113 01114 if (!empty($options['overflowdiv'])) { 01115 $text = html_writer::tag('div', $text, array('class'=>'no-overflow')); 01116 } 01117 01118 if (empty($options['nocache']) and !empty($CFG->cachetext)) { 01119 if (CLI_SCRIPT) { 01120 // special static cron cache - no need to store it in db if its not already there 01121 if (count($croncache) > 150) { 01122 reset($croncache); 01123 $key = key($croncache); 01124 unset($croncache[$key]); 01125 } 01126 $croncache[$md5key] = $text; 01127 return $text; 01128 } 01129 01130 $newcacheitem = new stdClass(); 01131 $newcacheitem->md5key = $md5key; 01132 $newcacheitem->formattedtext = $text; 01133 $newcacheitem->timemodified = time(); 01134 if ($oldcacheitem) { // See bug 4677 for discussion 01135 $newcacheitem->id = $oldcacheitem->id; 01136 try { 01137 $DB->update_record('cache_text', $newcacheitem); // Update existing record in the cache table 01138 } catch (dml_exception $e) { 01139 // It's unlikely that the cron cache cleaner could have 01140 // deleted this entry in the meantime, as it allows 01141 // some extra time to cover these cases. 01142 } 01143 } else { 01144 try { 01145 $DB->insert_record('cache_text', $newcacheitem); // Insert a new record in the cache table 01146 } catch (dml_exception $e) { 01147 // Again, it's possible that another user has caused this 01148 // record to be created already in the time that it took 01149 // to traverse this function. That's OK too, as the 01150 // call above handles duplicate entries, and eventually 01151 // the cron cleaner will delete them. 01152 } 01153 } 01154 } 01155 01156 return $text; 01157 } 01158 01166 function reset_text_filters_cache() { 01167 global $CFG, $DB; 01168 01169 $DB->delete_records('cache_text'); 01170 $purifdir = $CFG->cachedir.'/htmlpurifier'; 01171 remove_dir($purifdir, true); 01172 } 01173 01190 function format_string($string, $striplinks = true, $options = NULL) { 01191 global $CFG, $COURSE, $PAGE; 01192 01193 //We'll use a in-memory cache here to speed up repeated strings 01194 static $strcache = false; 01195 01196 if (empty($CFG->version) or $CFG->version < 2010072800 or during_initial_install()) { 01197 // do not filter anything during installation or before upgrade completes 01198 return $string = strip_tags($string); 01199 } 01200 01201 if ($strcache === false or count($strcache) > 2000) { // this number might need some tuning to limit memory usage in cron 01202 $strcache = array(); 01203 } 01204 01205 if (is_numeric($options)) { 01206 // legacy courseid usage 01207 $options = array('context'=>get_context_instance(CONTEXT_COURSE, $options)); 01208 } else { 01209 $options = (array)$options; // detach object, we can not modify it 01210 } 01211 01212 if (empty($options['context'])) { 01213 // fallback to $PAGE->context this may be problematic in CLI and other non-standard pages :-( 01214 $options['context'] = $PAGE->context; 01215 } else if (is_numeric($options['context'])) { 01216 $options['context'] = get_context_instance_by_id($options['context']); 01217 } 01218 01219 if (!$options['context']) { 01220 // we did not find any context? weird 01221 return $string = strip_tags($string); 01222 } 01223 01224 //Calculate md5 01225 $md5 = md5($string.'<+>'.$striplinks.'<+>'.$options['context']->id.'<+>'.current_language()); 01226 01227 //Fetch from cache if possible 01228 if (isset($strcache[$md5])) { 01229 return $strcache[$md5]; 01230 } 01231 01232 // First replace all ampersands not followed by html entity code 01233 // Regular expression moved to its own method for easier unit testing 01234 $string = replace_ampersands_not_followed_by_entity($string); 01235 01236 if (!empty($CFG->filterall)) { 01237 $string = filter_manager::instance()->filter_string($string, $options['context']); 01238 } 01239 01240 // If the site requires it, strip ALL tags from this string 01241 if (!empty($CFG->formatstringstriptags)) { 01242 $string = str_replace(array('<', '>'), array('<', '>'), strip_tags($string)); 01243 01244 } else { 01245 // Otherwise strip just links if that is required (default) 01246 if ($striplinks) { //strip links in string 01247 $string = strip_links($string); 01248 } 01249 $string = clean_text($string); 01250 } 01251 01252 //Store to cache 01253 $strcache[$md5] = $string; 01254 01255 return $string; 01256 } 01257 01266 function replace_ampersands_not_followed_by_entity($string) { 01267 return preg_replace("/\&(?![a-zA-Z0-9#]{1,8};)/", "&", $string); 01268 } 01269 01276 function strip_links($string) { 01277 return preg_replace('/(<a\s[^>]+?>)(.+?)(<\/a>)/is','$2',$string); 01278 } 01279 01286 function wikify_links($string) { 01287 return preg_replace('~(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)~i','$3 [ $2 ]', $string); 01288 } 01289 01304 function format_text_email($text, $format) { 01305 01306 switch ($format) { 01307 01308 case FORMAT_PLAIN: 01309 return $text; 01310 break; 01311 01312 case FORMAT_WIKI: 01313 // there should not be any of these any more! 01314 $text = wikify_links($text); 01315 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES))); 01316 break; 01317 01318 case FORMAT_HTML: 01319 return html_to_text($text); 01320 break; 01321 01322 case FORMAT_MOODLE: 01323 case FORMAT_MARKDOWN: 01324 default: 01325 $text = wikify_links($text); 01326 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES))); 01327 break; 01328 } 01329 } 01330 01342 function format_module_intro($module, $activity, $cmid, $filter=true) { 01343 global $CFG; 01344 require_once("$CFG->libdir/filelib.php"); 01345 $context = get_context_instance(CONTEXT_MODULE, $cmid); 01346 $options = array('noclean'=>true, 'para'=>false, 'filter'=>$filter, 'context'=>$context, 'overflowdiv'=>true); 01347 $intro = file_rewrite_pluginfile_urls($activity->intro, 'pluginfile.php', $context->id, 'mod_'.$module, 'intro', null); 01348 return trim(format_text($intro, $activity->introformat, $options, null)); 01349 } 01350 01358 function trusttext_strip($text) { 01359 while (true) { //removing nested TRUSTTEXT 01360 $orig = $text; 01361 $text = str_replace('#####TRUSTTEXT#####', '', $text); 01362 if (strcmp($orig, $text) === 0) { 01363 return $text; 01364 } 01365 } 01366 } 01367 01378 function trusttext_pre_edit($object, $field, $context) { 01379 $trustfield = $field.'trust'; 01380 $formatfield = $field.'format'; 01381 01382 if (!$object->$trustfield or !trusttext_trusted($context)) { 01383 $object->$field = clean_text($object->$field, $object->$formatfield); 01384 } 01385 01386 return $object; 01387 } 01388 01397 function trusttext_trusted($context) { 01398 return (trusttext_active() and has_capability('moodle/site:trustcontent', $context)); 01399 } 01400 01406 function trusttext_active() { 01407 global $CFG; 01408 01409 return !empty($CFG->enabletrusttext); 01410 } 01411 01427 function clean_text($text, $format = FORMAT_HTML, $options = array()) { 01428 if (empty($text) or is_numeric($text)) { 01429 return (string)$text; 01430 } 01431 01432 if ($format != FORMAT_HTML and $format != FORMAT_HTML) { 01433 // TODO: we need to standardise cleanup of text when loading it into editor first 01434 //debugging('clean_text() is designed to work only with html'); 01435 } 01436 01437 if ($format == FORMAT_PLAIN) { 01438 return $text; 01439 } 01440 01441 $text = purify_html($text, $options); 01442 01443 // Originally we tried to neutralise some script events here, it was a wrong approach because 01444 // it was trivial to work around that (for example using style based XSS exploits). 01445 // We must not give false sense of security here - all developers MUST understand how to use 01446 // rawurlencode(), htmlentities(), htmlspecialchars(), p(), s(), moodle_url, html_writer and friends!!! 01447 01448 return $text; 01449 } 01450 01459 function purify_html($text, $options = array()) { 01460 global $CFG; 01461 01462 $type = !empty($options['allowid']) ? 'allowid' : 'normal'; 01463 static $purifiers = array(); 01464 if (empty($purifiers[$type])) { 01465 01466 // make sure the serializer dir exists, it should be fine if it disappears later during cache reset 01467 $cachedir = $CFG->cachedir.'/htmlpurifier'; 01468 check_dir_exists($cachedir); 01469 01470 require_once $CFG->libdir.'/htmlpurifier/HTMLPurifier.safe-includes.php'; 01471 $config = HTMLPurifier_Config::createDefault(); 01472 01473 $config->set('HTML.DefinitionID', 'moodlehtml'); 01474 $config->set('HTML.DefinitionRev', 2); 01475 $config->set('Cache.SerializerPath', $cachedir); 01476 $config->set('Cache.SerializerPermissions', $CFG->directorypermissions); 01477 $config->set('Core.NormalizeNewlines', false); 01478 $config->set('Core.ConvertDocumentToFragment', true); 01479 $config->set('Core.Encoding', 'UTF-8'); 01480 $config->set('HTML.Doctype', 'XHTML 1.0 Transitional'); 01481 $config->set('URI.AllowedSchemes', array('http'=>true, 'https'=>true, 'ftp'=>true, 'irc'=>true, 'nntp'=>true, 'news'=>true, 'rtsp'=>true, 'teamspeak'=>true, 'gopher'=>true, 'mms'=>true, 'mailto'=>true)); 01482 $config->set('Attr.AllowedFrameTargets', array('_blank')); 01483 01484 if (!empty($CFG->allowobjectembed)) { 01485 $config->set('HTML.SafeObject', true); 01486 $config->set('Output.FlashCompat', true); 01487 $config->set('HTML.SafeEmbed', true); 01488 } 01489 01490 if ($type === 'allowid') { 01491 $config->set('Attr.EnableID', true); 01492 } 01493 01494 if ($def = $config->maybeGetRawHTMLDefinition()) { 01495 $def->addElement('nolink', 'Block', 'Flow', array()); // skip our filters inside 01496 $def->addElement('tex', 'Inline', 'Inline', array()); // tex syntax, equivalent to $$xx$$ 01497 $def->addElement('algebra', 'Inline', 'Inline', array()); // algebra syntax, equivalent to @@xx@@ 01498 $def->addElement('lang', 'Block', 'Flow', array(), array('lang'=>'CDATA')); // old and future style multilang - only our hacked lang attribute 01499 $def->addAttribute('span', 'xxxlang', 'CDATA'); // current problematic multilang 01500 } 01501 01502 $purifier = new HTMLPurifier($config); 01503 $purifiers[$type] = $purifier; 01504 } else { 01505 $purifier = $purifiers[$type]; 01506 } 01507 01508 $multilang = (strpos($text, 'class="multilang"') !== false); 01509 01510 if ($multilang) { 01511 $text = preg_replace('/<span(\s+lang="([a-zA-Z0-9_-]+)"|\s+class="multilang"){2}\s*>/', '<span xxxlang="${2}">', $text); 01512 } 01513 $text = $purifier->purify($text); 01514 if ($multilang) { 01515 $text = preg_replace('/<span xxxlang="([a-zA-Z0-9_-]+)">/', '<span lang="${1}" class="multilang">', $text); 01516 } 01517 01518 return $text; 01519 } 01520 01535 function text_to_html($text, $smiley_ignored=null, $para=true, $newlines=true) { 01537 $text = preg_replace("~>([[:space:]]+)<~i", "><", $text); 01538 01540 $text = preg_replace("~([\n\r])<~i", " <", $text); 01541 $text = preg_replace("~>([\n\r])~i", "> ", $text); 01542 01544 if ($newlines) { 01545 $text = nl2br($text); 01546 } 01547 01549 if ($para) { 01550 //return '<p>'.$text.'</p>'; //1.9 version 01551 return '<div class="text_to_html">'.$text.'</div>'; 01552 } else { 01553 return $text; 01554 } 01555 } 01556 01564 function markdown_to_html($text) { 01565 global $CFG; 01566 01567 if ($text === '' or $text === NULL) { 01568 return $text; 01569 } 01570 01571 require_once($CFG->libdir .'/markdown.php'); 01572 01573 return Markdown($text); 01574 } 01575 01587 function html_to_text($html, $width = 75, $dolinks = true) { 01588 01589 global $CFG; 01590 01591 require_once($CFG->libdir .'/html2text.php'); 01592 01593 $h2t = new html2text($html, false, $dolinks, $width); 01594 $result = $h2t->get_text(); 01595 01596 return $result; 01597 } 01598 01612 function highlight($needle, $haystack, $matchcase = false, 01613 $prefix = '<span class="highlight">', $suffix = '</span>') { 01614 01616 if (empty($needle) or empty($haystack)) { 01617 return $haystack; 01618 } 01619 01621 $words = preg_split('/ +/', trim($needle)); 01622 foreach ($words as $index => $word) { 01623 if (strpos($word, '-') === 0) { 01624 unset($words[$index]); 01625 } else if (strpos($word, '+') === 0) { 01626 $words[$index] = '\b' . preg_quote(ltrim($word, '+'), '/') . '\b'; // Match only as a complete word. 01627 } else { 01628 $words[$index] = preg_quote($word, '/'); 01629 } 01630 } 01631 $regexp = '/(' . implode('|', $words) . ')/u'; // u is do UTF-8 matching. 01632 if (!$matchcase) { 01633 $regexp .= 'i'; 01634 } 01635 01637 if (empty($words)) { 01638 return $haystack; 01639 } 01640 01642 $placeholders = array(); 01643 $matches = array(); 01644 preg_match_all('/<[^>]*>/', $haystack, $matches); 01645 foreach (array_unique($matches[0]) as $key => $htmltag) { 01646 $placeholders['<|' . $key . '|>'] = $htmltag; 01647 } 01648 01650 $haystack = str_replace($placeholders, array_keys($placeholders), $haystack); 01651 01653 $haystack = preg_replace($regexp, $prefix . '$1' . $suffix, $haystack); 01654 01656 $haystack = str_replace(array_keys($placeholders), $placeholders, $haystack); 01657 01658 return $haystack; 01659 } 01660 01671 function highlightfast($needle, $haystack) { 01672 01673 if (empty($needle) or empty($haystack)) { 01674 return $haystack; 01675 } 01676 01677 $parts = explode(moodle_strtolower($needle), moodle_strtolower($haystack)); 01678 01679 if (count($parts) === 1) { 01680 return $haystack; 01681 } 01682 01683 $pos = 0; 01684 01685 foreach ($parts as $key => $part) { 01686 $parts[$key] = substr($haystack, $pos, strlen($part)); 01687 $pos += strlen($part); 01688 01689 $parts[$key] .= '<span class="highlight">'.substr($haystack, $pos, strlen($needle)).'</span>'; 01690 $pos += strlen($needle); 01691 } 01692 01693 return str_replace('<span class="highlight"></span>', '', join('', $parts)); 01694 } 01695 01703 function get_html_lang($dir = false) { 01704 $direction = ''; 01705 if ($dir) { 01706 if (right_to_left()) { 01707 $direction = ' dir="rtl"'; 01708 } else { 01709 $direction = ' dir="ltr"'; 01710 } 01711 } 01712 //Accessibility: added the 'lang' attribute to $direction, used in theme <html> tag. 01713 $language = str_replace('_', '-', current_language()); 01714 @header('Content-Language: '.$language); 01715 return ($direction.' lang="'.$language.'" xml:lang="'.$language.'"'); 01716 } 01717 01718 01720 01725 function send_headers($contenttype, $cacheable = true) { 01726 global $CFG; 01727 01728 @header('Content-Type: ' . $contenttype); 01729 @header('Content-Script-Type: text/javascript'); 01730 @header('Content-Style-Type: text/css'); 01731 01732 if ($cacheable) { 01733 // Allow caching on "back" (but not on normal clicks) 01734 @header('Cache-Control: private, pre-check=0, post-check=0, max-age=0'); 01735 @header('Pragma: no-cache'); 01736 @header('Expires: '); 01737 } else { 01738 // Do everything we can to always prevent clients and proxies caching 01739 @header('Cache-Control: no-store, no-cache, must-revalidate'); 01740 @header('Cache-Control: post-check=0, pre-check=0', false); 01741 @header('Pragma: no-cache'); 01742 @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT'); 01743 @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); 01744 } 01745 @header('Accept-Ranges: none'); 01746 01747 if (empty($CFG->allowframembedding)) { 01748 @header('X-Frame-Options: sameorigin'); 01749 } 01750 } 01751 01762 function link_arrow_right($text, $url='', $accesshide=false, $addclass='') { 01763 global $OUTPUT; //TODO: move to output renderer 01764 $arrowclass = 'arrow '; 01765 if (! $url) { 01766 $arrowclass .= $addclass; 01767 } 01768 $arrow = '<span class="'.$arrowclass.'">'.$OUTPUT->rarrow().'</span>'; 01769 $htmltext = ''; 01770 if ($text) { 01771 $htmltext = '<span class="arrow_text">'.$text.'</span> '; 01772 if ($accesshide) { 01773 $htmltext = get_accesshide($htmltext); 01774 } 01775 } 01776 if ($url) { 01777 $class = 'arrow_link'; 01778 if ($addclass) { 01779 $class .= ' '.$addclass; 01780 } 01781 return '<a class="'.$class.'" href="'.$url.'" title="'.preg_replace('/<.*?>/','',$text).'">'.$htmltext.$arrow.'</a>'; 01782 } 01783 return $htmltext.$arrow; 01784 } 01785 01796 function link_arrow_left($text, $url='', $accesshide=false, $addclass='') { 01797 global $OUTPUT; // TODO: move to utput renderer 01798 $arrowclass = 'arrow '; 01799 if (! $url) { 01800 $arrowclass .= $addclass; 01801 } 01802 $arrow = '<span class="'.$arrowclass.'">'.$OUTPUT->larrow().'</span>'; 01803 $htmltext = ''; 01804 if ($text) { 01805 $htmltext = ' <span class="arrow_text">'.$text.'</span>'; 01806 if ($accesshide) { 01807 $htmltext = get_accesshide($htmltext); 01808 } 01809 } 01810 if ($url) { 01811 $class = 'arrow_link'; 01812 if ($addclass) { 01813 $class .= ' '.$addclass; 01814 } 01815 return '<a class="'.$class.'" href="'.$url.'" title="'.preg_replace('/<.*?>/','',$text).'">'.$arrow.$htmltext.'</a>'; 01816 } 01817 return $arrow.$htmltext; 01818 } 01819 01830 function get_accesshide($text, $elem='span', $class='', $attrs='') { 01831 return "<$elem class=\"accesshide $class\" $attrs>$text</$elem>"; 01832 } 01833 01839 function get_separator() { 01840 //Accessibility: the 'hidden' slash is preferred for screen readers. 01841 return ' '.link_arrow_right($text='/', $url='', $accesshide=true, 'sep').' '; 01842 } 01843 01860 function print_collapsible_region($contents, $classes, $id, $caption, $userpref = '', $default = false, $return = false) { 01861 $output = print_collapsible_region_start($classes, $id, $caption, $userpref, $default, true); 01862 $output .= $contents; 01863 $output .= print_collapsible_region_end(true); 01864 01865 if ($return) { 01866 return $output; 01867 } else { 01868 echo $output; 01869 } 01870 } 01871 01886 function print_collapsible_region_start($classes, $id, $caption, $userpref = '', $default = false, $return = false) { 01887 global $CFG, $PAGE, $OUTPUT; 01888 01889 // Work out the initial state. 01890 if (!empty($userpref) and is_string($userpref)) { 01891 user_preference_allow_ajax_update($userpref, PARAM_BOOL); 01892 $collapsed = get_user_preferences($userpref, $default); 01893 } else { 01894 $collapsed = $default; 01895 $userpref = false; 01896 } 01897 01898 if ($collapsed) { 01899 $classes .= ' collapsed'; 01900 } 01901 01902 $output = ''; 01903 $output .= '<div id="' . $id . '" class="collapsibleregion ' . $classes . '">'; 01904 $output .= '<div id="' . $id . '_sizer">'; 01905 $output .= '<div id="' . $id . '_caption" class="collapsibleregioncaption">'; 01906 $output .= $caption . ' '; 01907 $output .= '</div><div id="' . $id . '_inner" class="collapsibleregioninner">'; 01908 $PAGE->requires->js_init_call('M.util.init_collapsible_region', array($id, $userpref, get_string('clicktohideshow'))); 01909 01910 if ($return) { 01911 return $output; 01912 } else { 01913 echo $output; 01914 } 01915 } 01916 01923 function print_collapsible_region_end($return = false) { 01924 $output = '</div></div></div>'; 01925 01926 if ($return) { 01927 return $output; 01928 } else { 01929 echo $output; 01930 } 01931 } 01932 01945 function print_group_picture($group, $courseid, $large=false, $return=false, $link=true) { 01946 global $CFG; 01947 01948 if (is_array($group)) { 01949 $output = ''; 01950 foreach($group as $g) { 01951 $output .= print_group_picture($g, $courseid, $large, true, $link); 01952 } 01953 if ($return) { 01954 return $output; 01955 } else { 01956 echo $output; 01957 return; 01958 } 01959 } 01960 01961 $context = get_context_instance(CONTEXT_COURSE, $courseid); 01962 01963 // If there is no picture, do nothing 01964 if (!$group->picture) { 01965 return ''; 01966 } 01967 01968 // If picture is hidden, only show to those with course:managegroups 01969 if ($group->hidepicture and !has_capability('moodle/course:managegroups', $context)) { 01970 return ''; 01971 } 01972 01973 if ($link or has_capability('moodle/site:accessallgroups', $context)) { 01974 $output = '<a href="'. $CFG->wwwroot .'/user/index.php?id='. $courseid .'&group='. $group->id .'">'; 01975 } else { 01976 $output = ''; 01977 } 01978 if ($large) { 01979 $file = 'f1'; 01980 } else { 01981 $file = 'f2'; 01982 } 01983 01984 $grouppictureurl = moodle_url::make_pluginfile_url($context->id, 'group', 'icon', $group->id, '/', $file); 01985 $output .= '<img class="grouppicture" src="'.$grouppictureurl.'"'. 01986 ' alt="'.s(get_string('group').' '.$group->name).'" title="'.s($group->name).'"/>'; 01987 01988 if ($link or has_capability('moodle/site:accessallgroups', $context)) { 01989 $output .= '</a>'; 01990 } 01991 01992 if ($return) { 01993 return $output; 01994 } else { 01995 echo $output; 01996 } 01997 } 01998 01999 02012 function print_recent_activity_note($time, $user, $text, $link, $return=false, $viewfullnames=null) { 02013 static $strftimerecent = null; 02014 $output = ''; 02015 02016 if (is_null($viewfullnames)) { 02017 $context = get_context_instance(CONTEXT_SYSTEM); 02018 $viewfullnames = has_capability('moodle/site:viewfullnames', $context); 02019 } 02020 02021 if (is_null($strftimerecent)) { 02022 $strftimerecent = get_string('strftimerecent'); 02023 } 02024 02025 $output .= '<div class="head">'; 02026 $output .= '<div class="date">'.userdate($time, $strftimerecent).'</div>'; 02027 $output .= '<div class="name">'.fullname($user, $viewfullnames).'</div>'; 02028 $output .= '</div>'; 02029 $output .= '<div class="info"><a href="'.$link.'">'.format_string($text,true).'</a></div>'; 02030 02031 if ($return) { 02032 return $output; 02033 } else { 02034 echo $output; 02035 } 02036 } 02037 02061 function navmenulist($course, $sections, $modinfo, $strsection, $strjumpto, $width=50, $cmid=0) { 02062 02063 global $CFG, $OUTPUT; 02064 02065 $section = -1; 02066 $url = ''; 02067 $menu = array(); 02068 $doneheading = false; 02069 02070 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); 02071 02072 $menu[] = '<ul class="navmenulist"><li class="jumpto section"><span>'.$strjumpto.'</span><ul>'; 02073 foreach ($modinfo->cms as $mod) { 02074 if (!$mod->has_view()) { 02075 // Don't show modules which you can't link to! 02076 continue; 02077 } 02078 02079 if ($mod->sectionnum > $course->numsections) { 02080 break; 02081 } 02082 02083 if (!$mod->uservisible) { // do not icnlude empty sections at all 02084 continue; 02085 } 02086 02087 if ($mod->sectionnum >= 0 and $section != $mod->sectionnum) { 02088 $thissection = $sections[$mod->sectionnum]; 02089 02090 if ($thissection->visible or !$course->hiddensections or 02091 has_capability('moodle/course:viewhiddensections', $coursecontext)) { 02092 $thissection->summary = strip_tags(format_string($thissection->summary,true)); 02093 if (!$doneheading) { 02094 $menu[] = '</ul></li>'; 02095 } 02096 if ($course->format == 'weeks' or empty($thissection->summary)) { 02097 $item = $strsection ." ". $mod->sectionnum; 02098 } else { 02099 if (textlib::strlen($thissection->summary) < ($width-3)) { 02100 $item = $thissection->summary; 02101 } else { 02102 $item = textlib::substr($thissection->summary, 0, $width).'...'; 02103 } 02104 } 02105 $menu[] = '<li class="section"><span>'.$item.'</span>'; 02106 $menu[] = '<ul>'; 02107 $doneheading = true; 02108 02109 $section = $mod->sectionnum; 02110 } else { 02111 // no activities from this hidden section shown 02112 continue; 02113 } 02114 } 02115 02116 $url = $mod->modname .'/view.php?id='. $mod->id; 02117 $mod->name = strip_tags(format_string($mod->name ,true)); 02118 if (textlib::strlen($mod->name) > ($width+5)) { 02119 $mod->name = textlib::substr($mod->name, 0, $width).'...'; 02120 } 02121 if (!$mod->visible) { 02122 $mod->name = '('.$mod->name.')'; 02123 } 02124 $class = 'activity '.$mod->modname; 02125 $class .= ($cmid == $mod->id) ? ' selected' : ''; 02126 $menu[] = '<li class="'.$class.'">'. 02127 '<img src="'.$OUTPUT->pix_url('icon', $mod->modname) . '" alt="" />'. 02128 '<a href="'.$CFG->wwwroot.'/mod/'.$url.'">'.$mod->name.'</a></li>'; 02129 } 02130 02131 if ($doneheading) { 02132 $menu[] = '</ul></li>'; 02133 } 02134 $menu[] = '</ul></li></ul>'; 02135 02136 return implode("\n", $menu); 02137 } 02138 02154 function print_grade_menu($courseid, $name, $current, $includenograde=true, $return=false) { 02155 02156 global $CFG, $OUTPUT; 02157 02158 $output = ''; 02159 $strscale = get_string('scale'); 02160 $strscales = get_string('scales'); 02161 02162 $scales = get_scales_menu($courseid); 02163 foreach ($scales as $i => $scalename) { 02164 $grades[-$i] = $strscale .': '. $scalename; 02165 } 02166 if ($includenograde) { 02167 $grades[0] = get_string('nograde'); 02168 } 02169 for ($i=100; $i>=1; $i--) { 02170 $grades[$i] = $i; 02171 } 02172 $output .= html_writer::select($grades, $name, $current, false); 02173 02174 $linkobject = '<span class="helplink"><img class="iconhelp" alt="'.$strscales.'" src="'.$OUTPUT->pix_url('help') . '" /></span>'; 02175 $link = new moodle_url('/course/scales.php', array('id'=>$courseid, 'list'=>1)); 02176 $action = new popup_action('click', $link, 'ratingscales', array('height' => 400, 'width' => 500)); 02177 $output .= $OUTPUT->action_link($link, $linkobject, $action, array('title'=>$strscales)); 02178 02179 if ($return) { 02180 return $output; 02181 } else { 02182 echo $output; 02183 } 02184 } 02185 02197 function mdie($msg='', $errorcode=1) { 02198 trigger_error($msg); 02199 exit($errorcode); 02200 } 02201 02210 function notice ($message, $link='', $course=NULL) { 02211 global $CFG, $SITE, $COURSE, $PAGE, $OUTPUT; 02212 02213 $message = clean_text($message); // In case nasties are in here 02214 02215 if (CLI_SCRIPT) { 02216 echo("!!$message!!\n"); 02217 exit(1); // no success 02218 } 02219 02220 if (!$PAGE->headerprinted) { 02221 //header not yet printed 02222 $PAGE->set_title(get_string('notice')); 02223 echo $OUTPUT->header(); 02224 } else { 02225 echo $OUTPUT->container_end_all(false); 02226 } 02227 02228 echo $OUTPUT->box($message, 'generalbox', 'notice'); 02229 echo $OUTPUT->continue_button($link); 02230 02231 echo $OUTPUT->footer(); 02232 exit(1); // general error code 02233 } 02234 02249 function redirect($url, $message='', $delay=-1) { 02250 global $OUTPUT, $PAGE, $SESSION, $CFG; 02251 02252 if (CLI_SCRIPT or AJAX_SCRIPT) { 02253 // this is wrong - developers should not use redirect in these scripts, 02254 // but it should not be very likely 02255 throw new moodle_exception('redirecterrordetected', 'error'); 02256 } 02257 02258 // prevent debug errors - make sure context is properly initialised 02259 if ($PAGE) { 02260 $PAGE->set_context(null); 02261 $PAGE->set_pagelayout('redirect'); // No header and footer needed 02262 } 02263 02264 if ($url instanceof moodle_url) { 02265 $url = $url->out(false); 02266 } 02267 02268 $debugdisableredirect = false; 02269 do { 02270 if (defined('DEBUGGING_PRINTED')) { 02271 // some debugging already printed, no need to look more 02272 $debugdisableredirect = true; 02273 break; 02274 } 02275 02276 if (empty($CFG->debugdisplay) or empty($CFG->debug)) { 02277 // no errors should be displayed 02278 break; 02279 } 02280 02281 if (!function_exists('error_get_last') or !$lasterror = error_get_last()) { 02282 break; 02283 } 02284 02285 if (!($lasterror['type'] & $CFG->debug)) { 02286 //last error not interesting 02287 break; 02288 } 02289 02290 // watch out here, @hidden() errors are returned from error_get_last() too 02291 if (headers_sent()) { 02292 //we already started printing something - that means errors likely printed 02293 $debugdisableredirect = true; 02294 break; 02295 } 02296 02297 if (ob_get_level() and ob_get_contents()) { 02298 // there is something waiting to be printed, hopefully it is the errors, 02299 // but it might be some error hidden by @ too - such as the timezone mess from setup.php 02300 $debugdisableredirect = true; 02301 break; 02302 } 02303 } while (false); 02304 02305 // Technically, HTTP/1.1 requires Location: header to contain the absolute path. 02306 // (In practice browsers accept relative paths - but still, might as well do it properly.) 02307 // This code turns relative into absolute. 02308 if (!preg_match('|^[a-z]+:|', $url)) { 02309 // Get host name http://www.wherever.com 02310 $hostpart = preg_replace('|^(.*?[^:/])/.*$|', '$1', $CFG->wwwroot); 02311 if (preg_match('|^/|', $url)) { 02312 // URLs beginning with / are relative to web server root so we just add them in 02313 $url = $hostpart.$url; 02314 } else { 02315 // URLs not beginning with / are relative to path of current script, so add that on. 02316 $url = $hostpart.preg_replace('|\?.*$|','',me()).'/../'.$url; 02317 } 02318 // Replace all ..s 02319 while (true) { 02320 $newurl = preg_replace('|/(?!\.\.)[^/]*/\.\./|', '/', $url); 02321 if ($newurl == $url) { 02322 break; 02323 } 02324 $url = $newurl; 02325 } 02326 } 02327 02328 // Sanitise url - we can not rely on moodle_url or our URL cleaning 02329 // because they do not support all valid external URLs 02330 $url = preg_replace('/[\x00-\x1F\x7F]/', '', $url); 02331 $url = str_replace('"', '%22', $url); 02332 $encodedurl = preg_replace("/\&(?![a-zA-Z0-9#]{1,8};)/", "&", $url); 02333 $encodedurl = preg_replace('/^.*href="([^"]*)".*$/', "\\1", clean_text('<a href="'.$encodedurl.'" />', FORMAT_HTML)); 02334 $url = str_replace('&', '&', $encodedurl); 02335 02336 if (!empty($message)) { 02337 if ($delay === -1 || !is_numeric($delay)) { 02338 $delay = 3; 02339 } 02340 $message = clean_text($message); 02341 } else { 02342 $message = get_string('pageshouldredirect'); 02343 $delay = 0; 02344 } 02345 02346 if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) { 02347 if (defined('MDL_PERFTOLOG') && !function_exists('register_shutdown_function')) { 02348 $perf = get_performance_info(); 02349 error_log("PERF: " . $perf['txt']); 02350 } 02351 } 02352 02353 if ($delay == 0 && !$debugdisableredirect && !headers_sent()) { 02354 // workaround for IIS bug http://support.microsoft.com/kb/q176113/ 02355 if (session_id()) { 02356 session_get_instance()->write_close(); 02357 } 02358 02359 //302 might not work for POST requests, 303 is ignored by obsolete clients. 02360 @header($_SERVER['SERVER_PROTOCOL'] . ' 303 See Other'); 02361 @header('Location: '.$url); 02362 echo bootstrap_renderer::plain_redirect_message($encodedurl); 02363 exit; 02364 } 02365 02366 // Include a redirect message, even with a HTTP redirect, because that is recommended practice. 02367 if ($PAGE) { 02368 $CFG->docroot = false; // to prevent the link to moodle docs from being displayed on redirect page. 02369 echo $OUTPUT->redirect_message($encodedurl, $message, $delay, $debugdisableredirect); 02370 exit; 02371 } else { 02372 echo bootstrap_renderer::early_redirect_message($encodedurl, $message, $delay); 02373 exit; 02374 } 02375 } 02376 02383 function obfuscate_email($email) { 02384 02385 $i = 0; 02386 $length = strlen($email); 02387 $obfuscated = ''; 02388 while ($i < $length) { 02389 if (rand(0,2) && $email{$i}!='@') { //MDL-20619 some browsers have problems unobfuscating @ 02390 $obfuscated.='%'.dechex(ord($email{$i})); 02391 } else { 02392 $obfuscated.=$email{$i}; 02393 } 02394 $i++; 02395 } 02396 return $obfuscated; 02397 } 02398 02406 function obfuscate_text($plaintext) { 02407 02408 $i=0; 02409 $length = strlen($plaintext); 02410 $obfuscated=''; 02411 $prev_obfuscated = false; 02412 while ($i < $length) { 02413 $c = ord($plaintext{$i}); 02414 $numerical = ($c >= ord('0')) && ($c <= ord('9')); 02415 if ($prev_obfuscated and $numerical ) { 02416 $obfuscated.='&#'.ord($plaintext{$i}).';'; 02417 } else if (rand(0,2)) { 02418 $obfuscated.='&#'.ord($plaintext{$i}).';'; 02419 $prev_obfuscated = true; 02420 } else { 02421 $obfuscated.=$plaintext{$i}; 02422 $prev_obfuscated = false; 02423 } 02424 $i++; 02425 } 02426 return $obfuscated; 02427 } 02428 02438 function obfuscate_mailto($email, $label='', $dimmed=false) { 02439 02440 if (empty($label)) { 02441 $label = $email; 02442 } 02443 if ($dimmed) { 02444 $title = get_string('emaildisable'); 02445 $dimmed = ' class="dimmed"'; 02446 } else { 02447 $title = ''; 02448 $dimmed = ''; 02449 } 02450 return sprintf("<a href=\"%s:%s\" $dimmed title=\"$title\">%s</a>", 02451 obfuscate_text('mailto'), obfuscate_email($email), 02452 obfuscate_text($label)); 02453 } 02454 02462 function rebuildnolinktag($text) { 02463 02464 $text = preg_replace('/<(\/*nolink)>/i','<$1>',$text); 02465 02466 return $text; 02467 } 02468 02473 function print_maintenance_message() { 02474 global $CFG, $SITE, $PAGE, $OUTPUT; 02475 02476 $PAGE->set_pagetype('maintenance-message'); 02477 $PAGE->set_pagelayout('maintenance'); 02478 $PAGE->set_title(strip_tags($SITE->fullname)); 02479 $PAGE->set_heading($SITE->fullname); 02480 echo $OUTPUT->header(); 02481 echo $OUTPUT->heading(get_string('sitemaintenance', 'admin')); 02482 if (isset($CFG->maintenance_message) and !html_is_blank($CFG->maintenance_message)) { 02483 echo $OUTPUT->box_start('maintenance_message generalbox boxwidthwide boxaligncenter'); 02484 echo $CFG->maintenance_message; 02485 echo $OUTPUT->box_end(); 02486 } 02487 echo $OUTPUT->footer(); 02488 die; 02489 } 02490 02497 class tabobject { 02501 var $id; 02502 var $link; 02503 var $text; 02507 var $linkedwhenselected; 02508 02518 function tabobject ($id, $link='', $text='', $title='', $linkedwhenselected=false) { 02519 $this->id = $id; 02520 $this->link = $link; 02521 $this->text = $text; 02522 $this->title = $title ? $title : $text; 02523 $this->linkedwhenselected = $linkedwhenselected; 02524 } 02525 } 02526 02527 02528 02539 function print_tabs($tabrows, $selected=NULL, $inactive=NULL, $activated=NULL, $return=false) { 02540 global $CFG; 02541 02543 if (!is_array($inactive)) { 02544 $inactive = array(); 02545 } 02546 02548 if (!is_array($activated)) { 02549 $activated = array(); 02550 } 02551 02553 if (!$tree = convert_tabrows_to_tree($tabrows, $selected, $inactive, $activated)) { 02554 return false; 02555 } 02556 02558 02559 $output = convert_tree_to_html($tree); 02560 02561 $output = "\n\n".'<div class="tabtree">'.$output.'</div><div class="clearer"> </div>'."\n\n"; 02562 02564 02565 if ($return) { 02566 return $output; 02567 } 02568 echo $output; 02569 } 02570 02578 function convert_tree_to_html($tree, $row=0) { 02579 02580 $str = "\n".'<ul class="tabrow'.$row.'">'."\n"; 02581 02582 $first = true; 02583 $count = count($tree); 02584 02585 foreach ($tree as $tab) { 02586 $count--; // countdown to zero 02587 02588 $liclass = ''; 02589 02590 if ($first && ($count == 0)) { // Just one in the row 02591 $liclass = 'first last'; 02592 $first = false; 02593 } else if ($first) { 02594 $liclass = 'first'; 02595 $first = false; 02596 } else if ($count == 0) { 02597 $liclass = 'last'; 02598 } 02599 02600 if ((empty($tab->subtree)) && (!empty($tab->selected))) { 02601 $liclass .= (empty($liclass)) ? 'onerow' : ' onerow'; 02602 } 02603 02604 if ($tab->inactive || $tab->active || $tab->selected) { 02605 if ($tab->selected) { 02606 $liclass .= (empty($liclass)) ? 'here selected' : ' here selected'; 02607 } else if ($tab->active) { 02608 $liclass .= (empty($liclass)) ? 'here active' : ' here active'; 02609 } 02610 } 02611 02612 $str .= (!empty($liclass)) ? '<li class="'.$liclass.'">' : '<li>'; 02613 02614 if ($tab->inactive || $tab->active || ($tab->selected && !$tab->linkedwhenselected)) { 02615 // The a tag is used for styling 02616 $str .= '<a class="nolink"><span>'.$tab->text.'</span></a>'; 02617 } else { 02618 $str .= '<a href="'.$tab->link.'" title="'.$tab->title.'"><span>'.$tab->text.'</span></a>'; 02619 } 02620 02621 if (!empty($tab->subtree)) { 02622 $str .= convert_tree_to_html($tab->subtree, $row+1); 02623 } else if ($tab->selected) { 02624 $str .= '<div class="tabrow'.($row+1).' empty"> </div>'."\n"; 02625 } 02626 02627 $str .= ' </li>'."\n"; 02628 } 02629 $str .= '</ul>'."\n"; 02630 02631 return $str; 02632 } 02633 02643 function convert_tabrows_to_tree($tabrows, $selected, $inactive, $activated) { 02644 02646 02647 $tabrows = array_reverse($tabrows); 02648 02649 $subtree = array(); 02650 02651 foreach ($tabrows as $row) { 02652 $tree = array(); 02653 02654 foreach ($row as $tab) { 02655 $tab->inactive = in_array((string)$tab->id, $inactive); 02656 $tab->active = in_array((string)$tab->id, $activated); 02657 $tab->selected = (string)$tab->id == $selected; 02658 02659 if ($tab->active || $tab->selected) { 02660 if ($subtree) { 02661 $tab->subtree = $subtree; 02662 } 02663 } 02664 $tree[] = $tab; 02665 } 02666 $subtree = $tree; 02667 } 02668 02669 return $subtree; 02670 } 02671 02698 function debugging($message = '', $level = DEBUG_NORMAL, $backtrace = null) { 02699 global $CFG, $USER, $UNITTEST; 02700 02701 $forcedebug = false; 02702 if (!empty($CFG->debugusers)) { 02703 $debugusers = explode(',', $CFG->debugusers); 02704 $forcedebug = in_array($USER->id, $debugusers); 02705 } 02706 02707 if (!$forcedebug and (empty($CFG->debug) || $CFG->debug < $level)) { 02708 return false; 02709 } 02710 02711 if (!isset($CFG->debugdisplay)) { 02712 $CFG->debugdisplay = ini_get_bool('display_errors'); 02713 } 02714 02715 if ($message) { 02716 if (!$backtrace) { 02717 $backtrace = debug_backtrace(); 02718 } 02719 $from = format_backtrace($backtrace, CLI_SCRIPT); 02720 if (!empty($UNITTEST->running)) { 02721 // When the unit tests are running, any call to trigger_error 02722 // is intercepted by the test framework and reported as an exception. 02723 // Therefore, we cannot use trigger_error during unit tests. 02724 // At the same time I do not think we should just discard those messages, 02725 // so displaying them on-screen seems like the only option. (MDL-20398) 02726 echo '<div class="notifytiny">' . $message . $from . '</div>'; 02727 02728 } else if (NO_DEBUG_DISPLAY) { 02729 // script does not want any errors or debugging in output, 02730 // we send the info to error log instead 02731 error_log('Debugging: ' . $message . $from); 02732 02733 } else if ($forcedebug or $CFG->debugdisplay) { 02734 if (!defined('DEBUGGING_PRINTED')) { 02735 define('DEBUGGING_PRINTED', 1); // indicates we have printed something 02736 } 02737 if (CLI_SCRIPT) { 02738 echo "++ $message ++\n$from"; 02739 } else { 02740 echo '<div class="notifytiny">' . $message . $from . '</div>'; 02741 } 02742 02743 } else { 02744 trigger_error($message . $from, E_USER_NOTICE); 02745 } 02746 } 02747 return true; 02748 } 02749 02761 function print_location_comment($file, $line, $return = false) 02762 { 02763 if ($return) { 02764 return "<!-- $file at line $line -->\n"; 02765 } else { 02766 echo "<!-- $file at line $line -->\n"; 02767 } 02768 } 02769 02770 02774 function right_to_left() { 02775 return (get_string('thisdirection', 'langconfig') === 'rtl'); 02776 } 02777 02778 02786 function fix_align_rtl($align) { 02787 if (!right_to_left()) { 02788 return $align; 02789 } 02790 if ($align=='left') { return 'right'; } 02791 if ($align=='right') { return 'left'; } 02792 return $align; 02793 } 02794 02795 02805 function is_in_popup() { 02806 $inpopup = optional_param('inpopup', '', PARAM_BOOL); 02807 02808 return ($inpopup); 02809 } 02810 02820 class progress_bar { 02822 private $html_id; 02824 private $width; 02826 private $percent = 0; 02828 private $lastupdate = 0; 02830 private $time_start = 0; 02831 02840 public function __construct($html_id = '', $width = 500, $autostart = false) { 02841 if (!empty($html_id)) { 02842 $this->html_id = $html_id; 02843 } else { 02844 $this->html_id = 'pbar_'.uniqid(); 02845 } 02846 02847 $this->width = $width; 02848 02849 if ($autostart){ 02850 $this->create(); 02851 } 02852 } 02853 02859 public function create() { 02860 $this->time_start = microtime(true); 02861 if (CLI_SCRIPT) { 02862 return; // temporary solution for cli scripts 02863 } 02864 $htmlcode = <<<EOT 02865 <div style="text-align:center;width:{$this->width}px;clear:both;padding:0;margin:0 auto;"> 02866 <h2 id="status_{$this->html_id}" style="text-align: center;margin:0 auto"></h2> 02867 <p id="time_{$this->html_id}"></p> 02868 <div id="bar_{$this->html_id}" style="border-style:solid;border-width:1px;width:500px;height:50px;"> 02869 <div id="progress_{$this->html_id}" 02870 style="text-align:center;background:#FFCC66;width:4px;border:1px 02871 solid gray;height:38px; padding-top:10px;"> <span id="pt_{$this->html_id}"></span> 02872 </div> 02873 </div> 02874 </div> 02875 EOT; 02876 flush(); 02877 echo $htmlcode; 02878 flush(); 02879 } 02880 02888 private function _update($percent, $msg) { 02889 if (empty($this->time_start)) { 02890 throw new coding_exception('You must call create() (or use the $autostart ' . 02891 'argument to the constructor) before you try updating the progress bar.'); 02892 } 02893 02894 if (CLI_SCRIPT) { 02895 return; // temporary solution for cli scripts 02896 } 02897 02898 $es = $this->estimate($percent); 02899 02900 if ($es === null) { 02901 // always do the first and last updates 02902 $es = "?"; 02903 } else if ($es == 0) { 02904 // always do the last updates 02905 } else if ($this->lastupdate + 20 < time()) { 02906 // we must update otherwise browser would time out 02907 } else if (round($this->percent, 2) === round($percent, 2)) { 02908 // no significant change, no need to update anything 02909 return; 02910 } 02911 02912 $this->percent = $percent; 02913 $this->lastupdate = microtime(true); 02914 02915 $w = ($this->percent/100) * $this->width; 02916 echo html_writer::script(js_writer::function_call('update_progress_bar', array($this->html_id, $w, $this->percent, $msg, $es))); 02917 flush(); 02918 } 02919 02927 private function estimate($pt) { 02928 if ($this->lastupdate == 0) { 02929 return null; 02930 } 02931 if ($pt < 0.00001) { 02932 return null; // we do not know yet how long it will take 02933 } 02934 if ($pt > 99.99999) { 02935 return 0; // nearly done, right? 02936 } 02937 $consumed = microtime(true) - $this->time_start; 02938 if ($consumed < 0.001) { 02939 return null; 02940 } 02941 02942 return (100 - $pt) * ($consumed / $pt); 02943 } 02944 02951 public function update_full($percent, $msg) { 02952 $percent = max(min($percent, 100), 0); 02953 $this->_update($percent, $msg); 02954 } 02955 02963 public function update($cur, $total, $msg) { 02964 $percent = ($cur / $total) * 100; 02965 $this->update_full($percent, $msg); 02966 } 02967 02971 public function restart() { 02972 $this->percent = 0; 02973 $this->lastupdate = 0; 02974 $this->time_start = 0; 02975 } 02976 } 02977 02985 abstract class progress_trace { 02991 abstract public function output($message, $depth = 0); 02992 02996 public function finished() { 02997 } 02998 } 02999 03006 class null_progress_trace extends progress_trace { 03014 public function output($message, $depth = 0) { 03015 } 03016 } 03017 03024 class text_progress_trace extends progress_trace { 03032 public function output($message, $depth = 0) { 03033 echo str_repeat(' ', $depth), $message, "\n"; 03034 flush(); 03035 } 03036 } 03037 03044 class html_progress_trace extends progress_trace { 03052 public function output($message, $depth = 0) { 03053 echo '<p>', str_repeat('  ', $depth), htmlspecialchars($message), "</p>\n"; 03054 flush(); 03055 } 03056 } 03057 03064 class html_list_progress_trace extends progress_trace { 03066 protected $currentdepth = -1; 03067 03075 public function output($message, $depth = 0) { 03076 $samedepth = true; 03077 while ($this->currentdepth > $depth) { 03078 echo "</li>\n</ul>\n"; 03079 $this->currentdepth -= 1; 03080 if ($this->currentdepth == $depth) { 03081 echo '<li>'; 03082 } 03083 $samedepth = false; 03084 } 03085 while ($this->currentdepth < $depth) { 03086 echo "<ul>\n<li>"; 03087 $this->currentdepth += 1; 03088 $samedepth = false; 03089 } 03090 if ($samedepth) { 03091 echo "</li>\n<li>"; 03092 } 03093 echo htmlspecialchars($message); 03094 flush(); 03095 } 03096 03100 public function finished() { 03101 while ($this->currentdepth >= 0) { 03102 echo "</li>\n</ul>\n"; 03103 $this->currentdepth -= 1; 03104 } 03105 } 03106 } 03107 03115 function print_password_policy() { 03116 global $CFG; 03117 03118 $message = ''; 03119 if (!empty($CFG->passwordpolicy)) { 03120 $messages = array(); 03121 $messages[] = get_string('informminpasswordlength', 'auth', $CFG->minpasswordlength); 03122 if (!empty($CFG->minpassworddigits)) { 03123 $messages[] = get_string('informminpassworddigits', 'auth', $CFG->minpassworddigits); 03124 } 03125 if (!empty($CFG->minpasswordlower)) { 03126 $messages[] = get_string('informminpasswordlower', 'auth', $CFG->minpasswordlower); 03127 } 03128 if (!empty($CFG->minpasswordupper)) { 03129 $messages[] = get_string('informminpasswordupper', 'auth', $CFG->minpasswordupper); 03130 } 03131 if (!empty($CFG->minpasswordnonalphanum)) { 03132 $messages[] = get_string('informminpasswordnonalphanum', 'auth', $CFG->minpasswordnonalphanum); 03133 } 03134 03135 $messages = join(', ', $messages); // this is ugly but we do not have anything better yet... 03136 $message = get_string('informpasswordpolicy', 'auth', $messages); 03137 } 03138 return $message; 03139 }