Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/weblib.php
Go to the documentation of this file.
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("/&amp;#(\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('&amp;', '&', $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('&amp;', $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('  ', '&nbsp; ', $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('&lt;', '&gt;'), 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};)/", "&amp;", $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>&nbsp;';
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 = '&nbsp;<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 .'&amp;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};)/", "&amp;", $url);
02333     $encodedurl = preg_replace('/^.*href="([^"]*)".*$/', "\\1", clean_text('<a href="'.$encodedurl.'" />', FORMAT_HTML));
02334     $url = str_replace('&amp;', '&', $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('/&lt;(\/*nolink)&gt;/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">&nbsp;</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;">&nbsp;<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('&#160;&#160;', $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 }
 All Data Structures Namespaces Files Functions Variables Enumerations