'; /** * Allowed protocols - array of protocols that are safe to use in links and so on * @global string $ALLOWED_PROTOCOLS */ $ALLOWED_PROTOCOLS = array('http', 'https', 'ftp', 'news', 'mailto', 'rtsp', 'teamspeak', 'gopher', 'mms', 'color', 'callto', 'cursor', 'text-align', 'font-size', 'font-weight', 'font-style', 'font-family', 'border', 'margin', 'padding', 'background', 'background-color', 'text-decoration'); // CSS as well to get through kses /// Functions /** * Add quotes to HTML characters * * Returns $var with HTML characters (like "<", ">", etc.) properly quoted. * This function is very similar to {@link p()} * * @param string $var the string potentially containing HTML characters * @param boolean $obsolete no longer used. * @return string */ function s($var, $obsolete = false) { if ($var == '0') { // for integer 0, boolean false, string '0' return '0'; } return preg_replace("/&(#\d+);/i", "&$1;", htmlspecialchars($var)); } /** * Add quotes to HTML characters * * Prints $var with HTML characters (like "<", ">", etc.) properly quoted. * This function is very similar to {@link s()} * * @param string $var the string potentially containing HTML characters * @param boolean $obsolete no longer used. * @return string */ function p($var, $obsolete = false) { echo s($var, $obsolete); } /** * Does proper javascript quoting. * Do not use addslashes anymore, because it does not work when magic_quotes_sybase is enabled. * * @since 1.8 - 22/02/2007 * @param mixed value * @return mixed quoted result */ function addslashes_js($var) { if (is_string($var)) { $var = str_replace('\\', '\\\\', $var); $var = str_replace(array('\'', '"', "\n", "\r", "\0"), array('\\\'', '\\"', '\\n', '\\r', '\\0'), $var); $var = str_replace('', '<\/', $var); // XHTML compliance } else if (is_array($var)) { $var = array_map('addslashes_js', $var); } else if (is_object($var)) { $a = get_object_vars($var); foreach ($a as $key=>$value) { $a[$key] = addslashes_js($value); } $var = (object)$a; } return $var; } /** * Remove query string from url * * Takes in a URL and returns it without the querystring portion * * @param string $url the url which may have a query string attached * @return string */ function strip_querystring($url) { if ($commapos = strpos($url, '?')) { return substr($url, 0, $commapos); } else { return $url; } } /** * Returns the URL of the HTTP_REFERER, less the querystring portion if required * @param boolean $stripquery if true, also removes the query part of the url. * @return string */ function get_referer($stripquery=true) { if (isset($_SERVER['HTTP_REFERER'])) { if ($stripquery) { return strip_querystring($_SERVER['HTTP_REFERER']); } else { return $_SERVER['HTTP_REFERER']; } } else { return ''; } } /** * Returns the name of the current script, WITH the querystring portion. * this function is necessary because PHP_SELF and REQUEST_URI and SCRIPT_NAME * return different things depending on a lot of things like your OS, Web * server, and the way PHP is compiled (ie. as a CGI, module, ISAPI, etc.) * NOTE: This function returns false if the global variables needed are not set. * * @return string */ function me() { global $ME; return $ME; } /** * Like {@link me()} but returns a full URL * @see me() * @return string */ function qualified_me() { global $FULLME; return $FULLME; } /** * Class for creating and manipulating urls. * * See short write up here http://docs.moodle.org/en/Development:lib/weblib.php_moodle_url */ class moodle_url { protected $scheme = ''; // e.g. http protected $host = ''; protected $port = ''; protected $user = ''; protected $pass = ''; protected $path = ''; protected $fragment = ''; protected $params = array(); // Associative array of query string params /** * Pass no arguments to create a url that refers to this page. Use empty string to create empty url. * * @param mixed $url a number of different forms are accespted: * null - create a URL that is the same as the URL used to load this page, but with no query string * '' - and empty URL * string - a URL, will be parsed into it's bits, including query string * array - as returned from the PHP function parse_url * moodle_url - make a copy of another moodle_url * @param array $params these params override anything in the query string * where params have the same name. */ public function __construct($url = null, $params = array()) { if ($url === '') { // Leave URL blank. } else if (is_a($url, 'moodle_url')) { $this->scheme = $url->scheme; $this->host = $url->host; $this->port = $url->port; $this->user = $url->user; $this->pass = $url->pass; $this->path = $url->path; $this->fragment = $url->fragment; $this->params = $url->params; } else { if ($url === null) { global $ME; $url = $ME; } if (is_string($url)) { $url = parse_url($url); } $parts = $url; if ($parts === FALSE) { throw new moodle_exception('invalidurl'); } if (isset($parts['query'])) { parse_str(str_replace('&', '&', $parts['query']), $this->params); } unset($parts['query']); foreach ($parts as $key => $value) { $this->$key = $value; } } $this->params($params); } /** * Add an array of params to the params for this page. The added params override existing ones if they * have the same name. * * @param array $params Defaults to null. If null then return value of param 'name'. * @return array params for url. */ public function params($params = null) { if (!is_null($params)) { return $this->params = $params + $this->params; } else { return $this->params; } } /** * Remove all params if no arguments passed. Remove selected params if * arguments are passed. Can be called as either remove_params('param1', 'param2') * or remove_params(array('param1', 'param2')). * * @param mixed $params either an array of param names, or a string param name, * @param string $params,... any number of additional param names. */ public function remove_params($params = NULL) { if (empty($params)) { $this->params = array(); return; } if (!is_array($params)) { $params = func_get_args(); } foreach ($params as $param) { if (isset($this->params[$param])) { unset($this->params[$param]); } } } /** * Add a param to the params for this page. The added param overrides existing one if they * have the same name. * * @param string $paramname name * @param string $param value. Defaults to null. If null then return value of param 'name' */ public function param($paramname, $param = null) { if (!is_null($param)) { $this->params = array($paramname => $param) + $this->params; } else { return $this->params[$paramname]; } } /** * Get the params as as a query string. * @param array $overrideparams params to add to the output params, these * override existing ones with the same name. * @return string query string that can be added to a url. */ public function get_query_string($overrideparams = array()) { $arr = array(); $params = $overrideparams + $this->params; foreach ($params as $key => $val) { $arr[] = urlencode($key)."=".urlencode($val); } return implode($arr, "&"); } /** * Outputs params as hidden form elements. * * @param array $exclude params to ignore * @param integer $indent indentation * @param array $overrideparams params to add to the output params, these * override existing ones with the same name. * @return string html for form elements. */ public function hidden_params_out($exclude = array(), $indent = 0, $overrideparams=array()) { $tabindent = str_repeat("\t", $indent); $str = ''; $params = $overrideparams + $this->params; foreach ($params as $key => $val) { if (FALSE === array_search($key, $exclude)) { $val = s($val); $str.= "$tabindent\n"; } } return $str; } /** * Output url * * @param boolean $omitquerystring whether to output page params as a query string in the url. * @param array $overrideparams params to add to the output url, these override existing ones with the same name. * @return string url */ public function out($omitquerystring = false, $overrideparams = array()) { $uri = $this->scheme ? $this->scheme.':'.((strtolower($this->scheme) == 'mailto') ? '':'//'): ''; $uri .= $this->user ? $this->user.($this->pass? ':'.$this->pass:'').'@':''; $uri .= $this->host ? $this->host : ''; $uri .= $this->port ? ':'.$this->port : ''; $uri .= $this->path ? $this->path : ''; if (!$omitquerystring) { $querystring = $this->get_query_string($overrideparams); if ($querystring) { $uri .= '?' . $querystring; } } $uri .= $this->fragment ? '#'.$this->fragment : ''; return $uri; } /** * Output action url with sesskey * * @param boolean $noquerystring whether to output page params as a query string in the url. * @return string url */ public function out_action($overrideparams = array()) { $overrideparams = array('sesskey'=> sesskey()) + $overrideparams; return $this->out(false, $overrideparams); } } /** * Determine if there is data waiting to be processed from a form * * Used on most forms in Moodle to check for data * Returns the data as an object, if it's found. * This object can be used in foreach loops without * casting because it's cast to (array) automatically * * Checks that submitted POST data exists and returns it as object. * * @return mixed false or object */ function data_submitted() { if (empty($_POST)) { return false; } else { return (object)$_POST; } } /** * Given some normal text this function will break up any * long words to a given size by inserting the given character * * It's multibyte savvy and doesn't change anything inside html tags. * * @param string $string the string to be modified * @param int $maxsize maximum length of the string to be returned * @param string $cutchar the string used to represent word breaks * @return string */ function break_up_long_words($string, $maxsize=20, $cutchar=' ') { /// Loading the textlib singleton instance. We are going to need it. $textlib = textlib_get_instance(); /// First of all, save all the tags inside the text to skip them $tags = array(); filter_save_tags($string,$tags); /// Process the string adding the cut when necessary $output = ''; $length = $textlib->strlen($string); $wordlength = 0; for ($i=0; $i<$length; $i++) { $char = $textlib->substr($string, $i, 1); if ($char == ' ' or $char == "\t" or $char == "\n" or $char == "\r" or $char == "<" or $char == ">") { $wordlength = 0; } else { $wordlength++; if ($wordlength > $maxsize) { $output .= $cutchar; $wordlength = 0; } } $output .= $char; } /// Finally load the tags back again if (!empty($tags)) { $output = str_replace(array_keys($tags), $tags, $output); } return $output; } /** * This function will print a button/link/etc. form element * that will work on both Javascript and non-javascript browsers. * Relies on the Javascript function openpopup in javascript.php * * All parameters default to null, only $type and $url are mandatory. * * $url must be relative to home page eg /mod/survey/stuff.php * @param string $url Web link. Either relative to $CFG->wwwroot, or a full URL. * @param string $name Name to be assigned to the popup window (this is used by * client-side scripts to "talk" to the popup window) * @param string $linkname Text to be displayed as web link * @param int $height Height to assign to popup window * @param int $width Height to assign to popup window * @param string $title Text to be displayed as popup page title * @param string $options List of additional options for popup window * @param string $return If true, return as a string, otherwise print * @param string $id id added to the element * @param string $class class added to the element * @return string * @uses $CFG */ function element_to_popup_window ($type=null, $url=null, $name=null, $linkname=null, $height=400, $width=500, $title=null, $options=null, $return=false, $id=null, $class=null) { if (is_null($url)) { debugging('You must give the url to display in the popup. URL is missing - can\'t create popup window.', DEBUG_DEVELOPER); } global $CFG; if ($options == 'none') { // 'none' is legacy, should be removed in v2.0 $options = null; } // add some sane default options for popup windows if (!$options) { $options = 'menubar=0,location=0,scrollbars,resizable'; } if ($width) { $options .= ',width='. $width; } if ($height) { $options .= ',height='. $height; } if ($id) { $id = ' id="'.$id.'" '; } if ($class) { $class = ' class="'.$class.'" '; } if ($name) { $_name = $name; if (($name = preg_replace("/\s/", '_', $name)) != $_name) { debugging('The $name of a popup window shouldn\'t contain spaces - string modified. '. $_name .' changed to '. $name, DEBUG_DEVELOPER); } } else { $name = 'popup'; } // get some default string, using the localized version of legacy defaults if (is_null($linkname) || $linkname === '') { $linkname = get_string('clickhere'); } if (!$title) { $title = get_string('popupwindowname'); } $fullscreen = 0; // must be passed to openpopup $element = ''; switch ($type) { case 'button': $element = '\n"; break; case 'link': // Add wwwroot only if the URL does not already start with http:// or https:// if (!preg_match('|https?://|', $url)) { $url = $CFG->wwwroot . $url; } $element = '$linkname"; break; default : print_error('cannotcreatepopupwin'); break; } if ($return) { return $element; } else { echo $element; } } /** * Creates and displays (or returns) a link to a popup window, using element_to_popup_window function. * * @return string html code to display a link to a popup window. * @see element_to_popup_window() */ function link_to_popup_window ($url, $name=null, $linkname=null, $height=400, $width=500, $title=null, $options=null, $return=false) { return element_to_popup_window('link', $url, $name, $linkname, $height, $width, $title, $options, $return, null, null); } /** * Creates and displays (or returns) a buttons to a popup window, using element_to_popup_window function. * * @return string html code to display a button to a popup window. * @see element_to_popup_window() */ function button_to_popup_window ($url, $name=null, $linkname=null, $height=400, $width=500, $title=null, $options=null, $return=false, $id=null, $class=null) { return element_to_popup_window('button', $url, $name, $linkname, $height, $width, $title, $options, $return, $id, $class); } /** * Prints a simple button to close a window * @param string $name name of the window to close * @param boolean $return whether this function should return a string or output it. * @param boolean $reloadopener if true, clicking the button will also reload * the page that opend this popup window. * @return string if $return is true, nothing otherwise */ function close_window_button($name='closewindow', $return=false, $reloadopener = false) { global $CFG; $js = 'self.close();'; if ($reloadopener) { $js = 'window.opener.location.reload(1);' . $js; } $output = ''; $output .= '' . "\n"; $output .= ''; $output .= ''; $output .= ''; $output .= '' . "\n"; if ($return) { return $output; } else { echo $output; } } /* * Try and close the current window using JavaScript, either immediately, or after a delay. * @param integer $delay a delay in seconds before closing the window. Default 0. * @param boolean $reloadopener if true, we will see if this window was a pop-up, and try * to reload the parent window before this one closes. */ function close_window($delay = 0, $reloadopener = false) { global $THEME, $PAGE; if (!$PAGE->headerprinted) { print_header(get_string('closewindow')); } else { print_container_end_all(false, $THEME->open_header_containers); } if ($reloadopener) { $function = 'close_window_reloading_opener'; } else { $function = 'close_window'; } echo '' . get_string('windowclosing') . ''; print_delayed_js_call($delay, $function); print_footer('empty'); exit; } /** * Given an array of values, output the HTML for a select element with those options. * Normally, you only need to use the first few parameters. * * @param array $options The options to offer. An array of the form * $options[{value}] = {text displayed for that option}; * @param string $name the name of this form control, as in <select name="..." ... * @param string $selected the option to select initially, default none. * @param string $nothing The label for the 'nothing is selected' option. Defaults to get_string('choose'). * Set this to '' if you don't want a 'nothing is selected' option. * @param string $script in not '', then this is added to the <select> element as an onchange handler. * @param string $nothingvalue The value corresponding to the $nothing option. Defaults to 0. * @param boolean $return if false (the default) the the output is printed directly, If true, the * generated HTML is returned as a string. * @param boolean $disabled if true, the select is generated in a disabled state. Default, false. * @param int $tabindex if give, sets the tabindex attribute on the <select> element. Default none. * @param string $id value to use for the id attribute of the <select> element. If none is given, * then a suitable one is constructed. * @param mixed $listbox if false, display as a dropdown menu. If true, display as a list box. * By default, the list box will have a number of rows equal to min(10, count($options)), but if * $listbox is an integer, that number is used for size instead. * @param boolean $multiple if true, enable multiple selections, else only 1 item can be selected. Used * when $listbox display is enabled * @param string $class value to use for the class attribute of the <select> element. If none is given, * then a suitable one is constructed. */ function choose_from_menu ($options, $name, $selected='', $nothing='choose', $script='', $nothingvalue='0', $return=false, $disabled=false, $tabindex=0, $id='', $listbox=false, $multiple=false, $class='') { if ($nothing == 'choose') { $nothing = get_string('choose') .'...'; } $attributes = ($script) ? 'onchange="'. $script .'"' : ''; if ($disabled) { $attributes .= ' disabled="disabled"'; } if ($tabindex) { $attributes .= ' tabindex="'.$tabindex.'"'; } if ($id ==='') { $id = 'menu'.$name; // name may contaion [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading $id = str_replace('[', '', $id); $id = str_replace(']', '', $id); } if ($class ==='') { $class = 'menu'.$name; // name may contaion [], which would make an invalid class. e.g. numeric question type editing form, assignment quickgrading $class = str_replace('[', '', $class); $class = str_replace(']', '', $class); } $class = 'select ' . $class; /// Add 'select' selector always if ($listbox) { if (is_integer($listbox)) { $size = $listbox; } else { $numchoices = count($options); if ($nothing) { $numchoices += 1; } $size = min(10, $numchoices); } $attributes .= ' size="' . $size . '"'; if ($multiple) { $attributes .= ' multiple="multiple"'; } } $output = '' . "\n"; if ($nothing) { $output .= ' ' . "\n"; } if (!empty($options)) { foreach ($options as $value => $label) { $output .= ' ' . "\n"; } else { $output .= '>'. $label .'' . "\n"; } } } $output .= '' . "\n"; if ($return) { return $output; } else { echo $output; } } /** * Choose value 0 or 1 from a menu with options 'No' and 'Yes'. * Other options like choose_from_menu. * @param string $name * @param string $selected * @param string $string (defaults to '') * @param boolean $return whether this function should return a string or output it (defaults to false) * @param boolean $disabled (defaults to false) * @param int $tabindex */ function choose_from_menu_yesno($name, $selected, $script = '', $return = false, $disabled = false, $tabindex = 0) { return choose_from_menu(array(get_string('no'), get_string('yes')), $name, $selected, '', $script, '0', $return, $disabled, $tabindex); } /** * Just like choose_from_menu, but takes a nested array (2 levels) and makes a dropdown menu * including option headings with the first level. */ function choose_from_menu_nested($options,$name,$selected='',$nothing='choose',$script = '', $nothingvalue=0,$return=false,$disabled=false,$tabindex=0) { if ($nothing == 'choose') { $nothing = get_string('choose') .'...'; } $attributes = ($script) ? 'onchange="'. $script .'"' : ''; if ($disabled) { $attributes .= ' disabled="disabled"'; } if ($tabindex) { $attributes .= ' tabindex="'.$tabindex.'"'; } $output = '' . "\n"; if ($nothing) { $output .= ' ' . "\n"; } if (!empty($options)) { foreach ($options as $section => $values) { $output .= ' '."\n"; foreach ($values as $value => $label) { $output .= ' ' . "\n"; } else { $output .= '>'. $label .'' . "\n"; } } $output .= ' '."\n"; } } $output .= '' . "\n"; if ($return) { return $output; } else { echo $output; } } /** * Given an array of values, creates a group of radio buttons to be part of a form * * @param array $options An array of value-label pairs for the radio group (values as keys) * @param string $name Name of the radiogroup (unique in the form) * @param string $checked The value that is already checked */ function choose_from_radio ($options, $name, $checked='', $return=false) { static $idcounter = 0; if (!$name) { $name = 'unnamed'; } $output = '\n"; if (!empty($options)) { $currentradio = 0; foreach ($options as $value => $label) { $htmlid = 'auto-rb'.sprintf('%04d', ++$idcounter); $output .= ' "; $output .= ''. $value .'' . "\n"; } else { $output .= ' /> '. $label .'' . "\n"; } $currentradio = ($currentradio + 1) % 2; } } $output .= '' . "\n"; if ($return) { return $output; } else { echo $output; } } /** Display an standard html checkbox with an optional label * * @param string $name The name of the checkbox * @param string $value The valus that the checkbox will pass when checked * @param boolean $checked The flag to tell the checkbox initial state * @param string $label The label to be showed near the checkbox * @param string $alt The info to be inserted in the alt tag */ function print_checkbox ($name, $value, $checked = true, $label = '', $alt = '', $script='',$return=false) { static $idcounter = 0; if (!$name) { $name = 'unnamed'; } if ($alt) { $alt = strip_tags($alt); } else { $alt = 'checkbox'; } if ($checked) { $strchecked = ' checked="checked"'; } else { $strchecked = ''; } $htmlid = 'auto-cb'.sprintf('%04d', ++$idcounter); $output = '"; $output .= ''; if(!empty($label)) { $output .= ' '.$label.''; } $output .= ''."\n"; if (empty($return)) { echo $output; } else { return $output; } } /** Display an standard html text field with an optional label * * @param string $name The name of the text field * @param string $value The value of the text field * @param string $label The label to be showed near the text field * @param string $alt The info to be inserted in the alt tag */ function print_textfield ($name, $value, $alt = '',$size=50,$maxlength=0, $return=false) { static $idcounter = 0; if (empty($name)) { $name = 'unnamed'; } if (empty($alt)) { $alt = 'textfield'; } if (!empty($maxlength)) { $maxlength = ' maxlength="'.$maxlength.'" '; } $htmlid = 'auto-tf'.sprintf('%04d', ++$idcounter); $output = '"; $output .= ''; $output .= ''."\n"; if (empty($return)) { echo $output; } else { return $output; } } /** * Implements a complete little form with a dropdown menu. When JavaScript is on * selecting an option from the dropdown automatically submits the form (while * avoiding the usual acessibility problems with this appoach). With JavaScript * off, a 'Go' button is printed. * * @param string $baseurl The target URL up to the point of the variable that changes * @param array $options A list of value-label pairs for the popup list * @param string $formid id for the control. Must be unique on the page. Used in the HTML. * @param string $selected The option that is initially selected * @param string $nothing The label for the "no choice" option * @param string $help The name of a help page if help is required * @param string $helptext The name of the label for the help button * @param boolean $return Indicates whether the function should return the HTML * as a string or echo it directly to the page being rendered * @param string $targetwindow The name of the target page to open the linked page in. * @param string $selectlabel Text to place in a [label] element - preferred for accessibility. * @param array $optionsextra an array with the same keys as $options. The values are added within the corresponding tag. * @param string $submitvalue Optional label for the 'Go' button. Defaults to get_string('go'). * @param boolean $disabled If true, the menu will be displayed disabled. * @param boolean $showbutton If true, the button will always be shown even if JavaScript is available * @return string If $return is true then the entire form is returned as a string. * @todo Finish documenting this function */ function popup_form($baseurl, $options, $formid, $selected='', $nothing='choose', $help='', $helptext='', $return=false, $targetwindow='self', $selectlabel='', $optionsextra=NULL, $submitvalue='', $disabled=false, $showbutton=false) { global $CFG, $SESSION; static $go, $choose; /// Locally cached, in case there's lots on a page if (empty($options)) { return ''; } if (empty($submitvalue)){ if (!isset($go)) { $go = get_string('go'); $submitvalue=$go; } } if ($nothing == 'choose') { if (!isset($choose)) { $choose = get_string('choose'); } $nothing = $choose.'...'; } if ($disabled) { $disabled = ' disabled="disabled"'; } else { $disabled = ''; } // changed reference to document.getElementById('id_abc') instead of document.abc // MDL-7861 $output = 'frametarget. ' id="'.$formid.'"'. ' class="popupform">'; if ($help) { $button = helpbutton($help, $helptext, 'moodle', true, false, '', true); } else { $button = ''; } if ($selectlabel) { $selectlabel = ''.$selectlabel.''; } if ($showbutton) { // Using the no-JavaScript version $javascript = ''; } else if (check_browser_version('MSIE') || (check_browser_version('Opera') && !check_browser_operating_system("Linux"))) { //IE and Opera fire the onchange when ever you move into a dropdown list with the keyboard. //onfocus will call a function inside dropdown.js. It fixes this IE/Opera behavior. //Note: There is a bug on Opera+Linux with the javascript code (first mouse selection is inactive), //so we do not fix the Opera behavior on Linux $javascript = ' onfocus="initSelect(\''.$formid.'\','.$targetwindow.')"'; } else { //Other browser $javascript = ' onchange="'.$targetwindow. '.location=document.getElementById(\''.$formid. '\').jump.options[document.getElementById(\''. $formid.'\').jump.selectedIndex].value;"'; } $output .= ''.$selectlabel.$button.''."\n"; if ($nothing != '') { $selectlabeloption = ''; if ($selected=='') { $selectlabeloption = ' selected="selected"'; } foreach ($options as $value => $label) { //if one of the options is the empty value, don't make this the default if ($value == '') { $selected = ''; } } $output .= " $nothing\n"; } $inoptgroup = false; foreach ($options as $value => $label) { if ($label == '--') { /// we are ending previous optgroup /// Check to see if we already have a valid open optgroup /// XHTML demands that there be at least 1 option within an optgroup if ($inoptgroup and (count($optgr) > 1) ) { $output .= implode('', $optgr); $output .= ' '; } $optgr = array(); $inoptgroup = false; continue; } else if (substr($label,0,2) == '--') { /// we are starting a new optgroup /// Check to see if we already have a valid open optgroup /// XHTML demands that there be at least 1 option within an optgroup if ($inoptgroup and (count($optgr) > 1) ) { $output .= implode('', $optgr); $output .= ' '; } unset($optgr); $optgr = array(); $optgr[] = ' '; // Plain labels $inoptgroup = true; /// everything following will be in an optgroup continue; } else { if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()])) { $url = $SESSION->sid_process_url( $baseurl . $value ); } else { $url=$baseurl . $value; } $optstr = ' ' . "\n"; } else { $optstr .= '>'. $value .'' . "\n"; } if ($inoptgroup) { $optgr[] = $optstr; } else { $output .= $optstr; } } } /// catch the final group if not closed if ($inoptgroup and count($optgr) > 1) { $output .= implode('', $optgr); $output .= ' '; } $output .= ''; $output .= ''; if (!$showbutton) { $output .= ''; } $output .= ''; if (!$showbutton) { $output .= ''; } $output .= ''; if ($return) { return $output; } else { echo $output; } } /** * Prints some red text * * @param string $error The text to be displayed in red */ function formerr($error) { if (!empty($error)) { echo ''. $error .''; } } /** * Validates an email to make sure it makes sense. * * @param string $address The email address to validate. * @return boolean */ function validate_email($address) { return (ereg('^[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+'. '(\.[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+)*'. '@'. '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'. '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$', $address)); } /** * Extracts file argument either from file parameter or PATH_INFO * Note: $scriptname parameter is not needed anymore * * @return string file path (only safe characters) */ function get_file_argument() { global $SCRIPT; $relativepath = optional_param('file', FALSE, PARAM_PATH); // then try extract file from PATH_INFO (slasharguments method) if ($relativepath === false and isset($_SERVER['PATH_INFO']) and $_SERVER['PATH_INFO'] !== '') { // check that PATH_INFO works == must not contain the script name if (strpos($_SERVER['PATH_INFO'], $SCRIPT) === false) { $relativepath = clean_param(urldecode($_SERVER['PATH_INFO']), PARAM_PATH); } } // note: we are not using any other way because they are not compatible with unicode file names ;-) return $relativepath; } /** * Just returns an array of text formats suitable for a popup menu * * @uses FORMAT_MOODLE * @uses FORMAT_HTML * @uses FORMAT_PLAIN * @uses FORMAT_MARKDOWN * @return array */ function format_text_menu() { return array (FORMAT_MOODLE => get_string('formattext'), FORMAT_HTML => get_string('formathtml'), FORMAT_PLAIN => get_string('formatplain'), FORMAT_MARKDOWN => get_string('formatmarkdown')); } /** * Given text in a variety of format codings, this function returns * the text as safe HTML. * * This function should mainly be used for long strings like posts, * answers, glossary items etc. For short strings @see format_string(). * * @uses $CFG * @uses FORMAT_MOODLE * @uses FORMAT_HTML * @uses FORMAT_PLAIN * @uses FORMAT_WIKI * @uses FORMAT_MARKDOWN * @param string $text The text to be formatted. This is raw text originally from user input. * @param int $format Identifier of the text format to be used * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN) * @param array $options ? * @param int $courseid ? * @return string * @todo Finish documenting this function */ function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) { global $CFG, $COURSE, $DB, $PAGE; static $croncache = array(); $hashstr = ''; if ($text === '') { return ''; // no need to do any filters and cleaning } if (!isset($options->trusted)) { $options->trusted = false; } if (!isset($options->noclean)) { if ($options->trusted and trusttext_active()) { // no cleaning if text trusted and noclean not specified $options->noclean=true; } else { $options->noclean=false; } } if (!isset($options->nocache)) { $options->nocache=false; } if (!isset($options->smiley)) { $options->smiley=true; } if (!isset($options->filter)) { $options->filter=true; } if (!isset($options->para)) { $options->para=true; } if (!isset($options->newlines)) { $options->newlines=true; } if (empty($courseid)) { $courseid = $COURSE->id; } if ($options->filter) { $filtermanager = filter_manager::instance(); } else { $filtermanager = new null_filter_manager(); } $context = $PAGE->context; if (!empty($CFG->cachetext) and empty($options->nocache)) { $hashstr .= $text.'-'.$filtermanager->text_filtering_hash($context, $courseid).'-'.(int)$courseid.'-'.current_language().'-'. (int)$format.(int)$options->trusted.(int)$options->noclean.(int)$options->smiley. (int)$options->filter.(int)$options->para.(int)$options->newlines; $time = time() - $CFG->cachetext; $md5key = md5($hashstr); if (CLI_SCRIPT) { if (isset($croncache[$md5key])) { return $croncache[$md5key]; } } if ($oldcacheitem = $DB->get_record('cache_text', array('md5key'=>$md5key), '*', true)) { if ($oldcacheitem->timemodified >= $time) { if (CLI_SCRIPT) { if (count($croncache) > 150) { reset($croncache); $key = key($croncache); unset($croncache[$key]); } $croncache[$md5key] = $oldcacheitem->formattedtext; } return $oldcacheitem->formattedtext; } } } switch ($format) { case FORMAT_HTML: if ($options->smiley) { replace_smilies($text); } if (!$options->noclean) { $text = clean_text($text, FORMAT_HTML); } $text = $filtermanager->filter_text($text, $context, $courseid); break; case FORMAT_PLAIN: $text = s($text); // cleans dangerous JS $text = rebuildnolinktag($text); $text = str_replace(' ', ' ', $text); $text = nl2br($text); break; case FORMAT_WIKI: // this format is deprecated $text = 'NOTICE: Wiki-like formatting has been removed from Moodle. You should not be seeing this message as all texts should have been converted to Markdown format instead. Please post a bug report to http://moodle.org/bugs with information about where you saw this message.'.s($text); break; case FORMAT_MARKDOWN: $text = markdown_to_html($text); if ($options->smiley) { replace_smilies($text); } if (!$options->noclean) { $text = clean_text($text, FORMAT_HTML); } $text = $filtermanager->filter_text($text, $context, $courseid); break; default: // FORMAT_MOODLE or anything else $text = text_to_html($text, $options->smiley, $options->para, $options->newlines); if (!$options->noclean) { $text = clean_text($text, FORMAT_HTML); } $text = $filtermanager->filter_text($text, $context, $courseid); break; } // Warn people that we have removed this old mechanism, just in case they // were stupid enough to rely on it. if (isset($CFG->currenttextiscacheable)) { debugging('Once upon a time, Moodle had a truly evil use of global variables ' . 'called $CFG->currenttextiscacheable. The good news is that this no ' . 'longer exists. The bad news is that you seem to be using a filter that '. 'relies on it. Please seek out and destroy that filter code.', DEBUG_DEVELOPER); } if (empty($options->nocache) and !empty($CFG->cachetext)) { if (CLI_SCRIPT) { // special static cron cache - no need to store it in db if its not already there if (count($croncache) > 150) { reset($croncache); $key = key($croncache); unset($croncache[$key]); } $croncache[$md5key] = $text; return $text; } $newcacheitem = new object(); $newcacheitem->md5key = $md5key; $newcacheitem->formattedtext = $text; $newcacheitem->timemodified = time(); if ($oldcacheitem) { // See bug 4677 for discussion $newcacheitem->id = $oldcacheitem->id; try { $DB->update_record('cache_text', $newcacheitem); // Update existing record in the cache table } catch (dml_exception $e) { // It's unlikely that the cron cache cleaner could have // deleted this entry in the meantime, as it allows // some extra time to cover these cases. } } else { try { $DB->insert_record('cache_text', $newcacheitem); // Insert a new record in the cache table } catch (dml_exception $e) { // Again, it's possible that another user has caused this // record to be created already in the time that it took // to traverse this function. That's OK too, as the // call above handles duplicate entries, and eventually // the cron cleaner will delete them. } } } return $text; } /** Converts the text format from the value to the 'internal' * name or vice versa. $key can either be the value or the name * and you get the other back. * * @param mixed int 0-4 or string one of 'moodle','html','plain','markdown' * @return mixed as above but the other way around! */ function text_format_name( $key ) { $lookup = array(); $lookup[FORMAT_MOODLE] = 'moodle'; $lookup[FORMAT_HTML] = 'html'; $lookup[FORMAT_PLAIN] = 'plain'; $lookup[FORMAT_MARKDOWN] = 'markdown'; $value = "error"; if (!is_numeric($key)) { $key = strtolower( $key ); $value = array_search( $key, $lookup ); } else { if (isset( $lookup[$key] )) { $value = $lookup[ $key ]; } } return $value; } /** * Resets all data related to filters, called during upgrade or when filter settings change. * @return void */ function reset_text_filters_cache() { global $CFG, $DB; $DB->delete_records('cache_text'); $purifdir = $CFG->dataroot.'/cache/htmlpurifier'; remove_dir($purifdir, true); } /** Given a simple string, this function returns the string * processed by enabled string filters if $CFG->filterall is enabled * * This function should be used to print short strings (non html) that * need filter processing e.g. activity titles, post subjects, * glossary concepts. * * @param string $string The string to be filtered. * @param boolean $striplinks To strip any link in the result text (Moodle 1.8 default changed from false to true! MDL-8713) * @param int $courseid Current course as filters can, potentially, use it * @return string */ function format_string($string, $striplinks=true, $courseid=NULL ) { global $CFG, $COURSE, $PAGE; //We'll use a in-memory cache here to speed up repeated strings static $strcache = false; if ($strcache === false or count($strcache) > 2000 ) { // this number might need some tuning to limit memory usage in cron $strcache = array(); } //init course id if (empty($courseid)) { $courseid = $COURSE->id; } //Calculate md5 $md5 = md5($string.'<+>'.$striplinks.'<+>'.$courseid.'<+>'.current_language()); //Fetch from cache if possible if (isset($strcache[$md5])) { return $strcache[$md5]; } // First replace all ampersands not followed by html entity code $string = preg_replace("/\&(?![a-zA-Z0-9#]{1,8};)/", "&", $string); if (!empty($CFG->filterall) && $CFG->version >= 2009040600) { // Avoid errors during the upgrade to the new system. $context = $PAGE->context; $string = filter_manager::instance()->filter_string($string, $context, $courseid); } // If the site requires it, strip ALL tags from this string if (!empty($CFG->formatstringstriptags)) { $string = strip_tags($string); } else { // Otherwise strip just links if that is required (default) if ($striplinks) { //strip links in string $string = preg_replace('/(]+?>)(.+?)(<\/a>)/is','$2',$string); } $string = clean_text($string); } //Store to cache $strcache[$md5] = $string; return $string; } /** * Given text in a variety of format codings, this function returns * the text as plain text suitable for plain email. * * @uses FORMAT_MOODLE * @uses FORMAT_HTML * @uses FORMAT_PLAIN * @uses FORMAT_WIKI * @uses FORMAT_MARKDOWN * @param string $text The text to be formatted. This is raw text originally from user input. * @param int $format Identifier of the text format to be used * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN) * @return string */ function format_text_email($text, $format) { switch ($format) { case FORMAT_PLAIN: return $text; break; case FORMAT_WIKI: $text = wiki_to_html($text); /// This expression turns links into something nice in a text format. (Russell Jungwirth) /// From: http://php.net/manual/en/function.eregi-replace.php and simplified $text = eregi_replace('(]*>([^<]*))','\\3 [ \\2 ]', $text); return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES))); break; case FORMAT_HTML: return html_to_text($text); break; case FORMAT_MOODLE: case FORMAT_MARKDOWN: default: $text = eregi_replace('(]*>([^<]*))','\\3 [ \\2 ]', $text); return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES))); break; } } /** * Given some text in HTML format, this function will pass it * through any filters that have been configured for this context. * * @param string $text The text to be passed through format filters * @param int $courseid The current course. * @return string the filtered string. */ function filter_text($text, $courseid=NULL) { global $CFG, $COURSE, $PAGE; if (empty($courseid)) { $courseid = $COURSE->id; // (copied from format_text) } $context = $PAGE->context; return filter_manager::instance()->filter_text($text, $context, $courseid); } /** * Formats activity intro text * @param string $module name of module * @param object $activity instance of activity * @param int $cmid course module id * @param bool $filter filter resulting html text * @return text */ function format_module_intro($module, $activity, $cmid, $filter=true) { global $CFG; require_once("$CFG->libdir/filelib.php"); $options = (object)array('noclean'=>true, 'para'=>false, 'filter'=>false); $context = get_context_instance(CONTEXT_MODULE, $cmid); $intro = file_rewrite_pluginfile_urls($activity->intro, 'pluginfile.php', $context->id, $module.'_intro', null); return trim(format_text($intro, $activity->introformat, $options)); } /** * Legacy function, used for cleaning of old forum and glossary text only. * @param string $text text that may contain TRUSTTEXT marker * @return text without any TRUSTTEXT marker */ function trusttext_strip($text) { global $CFG; while (true) { //removing nested TRUSTTEXT $orig = $text; $text = str_replace('#####TRUSTTEXT#####', '', $text); if (strcmp($orig, $text) === 0) { return $text; } } } /** * Must be called before editing of all texts * with trust flag. Removes all XSS nasties * from texts stored in database if needed. * @param object $object data object with xxx, xxxformat and xxxtrust fields * @param string $field name of text field * @param object $context active context * @return object updated $object */ function trusttext_pre_edit($object, $field, $context) { $trustfield = $field.'trust'; $formatfield = $field.'format'; if (!$object->$trustfield or !trusttext_trusted($context)) { $object->$field = clean_text($object->$field, $object->$formatfield); } return $object; } /** * Is urrent user trusted to enter no dangerous XSS in this context? * Please note the user must be in fact trusted everywhere on this server!! * @param $context * @return bool true if user trusted */ function trusttext_trusted($context) { return (trusttext_active() and has_capability('moodle/site:trustcontent', $context)); } /** * Is trusttext feature active? * @param $context * @return bool */ function trusttext_active() { global $CFG; return !empty($CFG->enabletrusttext); } /** * Given raw text (eg typed in by a user), this function cleans it up * and removes any nasty tags that could mess up Moodle pages. * * @uses FORMAT_MOODLE * @uses FORMAT_PLAIN * @uses ALLOWED_TAGS * @param string $text The text to be cleaned * @param int $format Identifier of the text format to be used * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN) * @return string The cleaned up text */ function clean_text($text, $format=FORMAT_MOODLE) { global $ALLOWED_TAGS, $CFG; if (empty($text) or is_numeric($text)) { return (string)$text; } switch ($format) { case FORMAT_PLAIN: case FORMAT_MARKDOWN: return $text; default: if (!empty($CFG->enablehtmlpurifier)) { $text = purify_html($text); } else { /// Fix non standard entity notations $text = preg_replace('/([0-9]+)(;?)/', "\\1;", $text); $text = preg_replace('/([0-9a-fA-F]+)(;?)/', "\\1;", $text); /// Remove tags that are not allowed $text = strip_tags($text, $ALLOWED_TAGS); /// Clean up embedded scripts and , using kses $text = cleanAttributes($text); /// Again remove tags that are not allowed $text = strip_tags($text, $ALLOWED_TAGS); } /// Remove potential script events - some extra protection for undiscovered bugs in our code $text = eregi_replace("([^a-z])language([[:space:]]*)=", "\\1Xlanguage=", $text); $text = eregi_replace("([^a-z])on([a-z]+)([[:space:]]*)=", "\\1Xon\\2=", $text); return $text; } } /** * KSES replacement cleaning function - uses HTML Purifier. */ function purify_html($text) { global $CFG; // this can not be done only once because we sometimes need to reset the cache $cachedir = $CFG->dataroot.'/cache/htmlpurifier'; $status = check_dir_exists($cachedir, true, true); static $purifier = false; static $config; if ($purifier === false) { require_once $CFG->libdir.'/htmlpurifier/HTMLPurifier.safe-includes.php'; $config = HTMLPurifier_Config::createDefault(); $config->set('Core', 'ConvertDocumentToFragment', true); $config->set('Core', 'Encoding', 'UTF-8'); $config->set('HTML', 'Doctype', 'XHTML 1.0 Transitional'); $config->set('Cache', 'SerializerPath', $cachedir); $config->set('URI', 'AllowedSchemes', array('http'=>1, 'https'=>1, 'ftp'=>1, 'irc'=>1, 'nntp'=>1, 'news'=>1, 'rtsp'=>1, 'teamspeak'=>1, 'gopher'=>1, 'mms'=>1)); $config->set('Attr', 'AllowedFrameTargets', array('_blank')); $purifier = new HTMLPurifier($config); } return $purifier->purify($text); } /** * This function takes a string and examines it for HTML tags. * If tags are detected it passes the string to a helper function {@link cleanAttributes2()} * which checks for attributes and filters them for malicious content * 17/08/2004 :: Eamon DOT Costello AT dcu DOT ie * * @param string $str The string to be examined for html tags * @return string */ function cleanAttributes($str){ $result = preg_replace_callback( '%(<[^>]*(>|$)|>)%m', #search for html tags "cleanAttributes2", $str ); return $result; } /** * This function takes a string with an html tag and strips out any unallowed * protocols e.g. javascript: * It calls ancillary functions in kses which are prefixed by kses * 17/08/2004 :: Eamon DOT Costello AT dcu DOT ie * * @param array $htmlArray An array from {@link cleanAttributes()}, containing in its 1st * element the html to be cleared * @return string */ function cleanAttributes2($htmlArray){ global $CFG, $ALLOWED_PROTOCOLS; require_once($CFG->libdir .'/kses.php'); $htmlTag = $htmlArray[1]; if (substr($htmlTag, 0, 1) != '<') { return '>'; //a single character ">" detected } if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $htmlTag, $matches)) { return ''; // It's seriously malformed } $slash = trim($matches[1]); //trailing xhtml slash $elem = $matches[2]; //the element name $attrlist = $matches[3]; // the list of attributes as a string $attrArray = kses_hair($attrlist, $ALLOWED_PROTOCOLS); $attStr = ''; foreach ($attrArray as $arreach) { $arreach['name'] = strtolower($arreach['name']); if ($arreach['name'] == 'style') { $value = $arreach['value']; while (true) { $prevvalue = $value; $value = kses_no_null($value); $value = preg_replace("/\/\*.*\*\//Us", '', $value); $value = kses_decode_entities($value); $value = preg_replace('/([0-9]+)(;?)/', "\\1;", $value); $value = preg_replace('/([0-9a-fA-F]+)(;?)/', "\\1;", $value); if ($value === $prevvalue) { $arreach['value'] = $value; break; } } $arreach['value'] = preg_replace("/j\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t/i", "Xjavascript", $arreach['value']); $arreach['value'] = preg_replace("/e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n/i", "Xexpression", $arreach['value']); $arreach['value'] = preg_replace("/b\s*i\s*n\s*d\s*i\s*n\s*g/i", "Xbinding", $arreach['value']); } else if ($arreach['name'] == 'href') { //Adobe Acrobat Reader XSS protection $arreach['value'] = preg_replace('/(\.(pdf|fdf|xfdf|xdp|xfd)[^#]*)#.*$/i', '$1', $arreach['value']); } $attStr .= ' '.$arreach['name'].'="'.$arreach['value'].'"'; } $xhtml_slash = ''; if (preg_match('%/\s*$%', $attrlist)) { $xhtml_slash = ' /'; } return '<'. $slash . $elem . $attStr . $xhtml_slash .'>'; } /** * Replaces all known smileys in the text with image equivalents * * @uses $CFG * @param string $text Passed by reference. The string to search for smily strings. * @return string */ function replace_smilies(&$text) { global $CFG; if (empty($CFG->emoticons)) { /// No emoticons defined, nothing to process here return; } $lang = current_language(); $emoticonstring = $CFG->emoticons; static $e = array(); static $img = array(); static $emoticons = null; if (is_null($emoticons)) { $emoticons = array(); if ($emoticonstring) { $items = explode('{;}', $CFG->emoticons); foreach ($items as $item) { $item = explode('{:}', $item); $emoticons[$item[0]] = $item[1]; } } } if (empty($img[$lang])) { /// After the first time this is not run again $e[$lang] = array(); $img[$lang] = array(); foreach ($emoticons as $emoticon => $image){ $alttext = get_string($image, 'pix'); $alttext = preg_replace('/^\[\[(.*)\]\]$/', '$1', $alttext); /// Clean alttext in case there isn't lang string for it. $e[$lang][] = $emoticon; $img[$lang][] = ''; } } // Exclude from transformations all the code inside \n"; if ($loadlib == $CFG->wwwroot.'/lib/yui/logger/logger-min.js') { // Special case, we need the CSS too. $output .= 'wwwroot}/lib/yui/logger/assets/logger.css\" />\n"; } } return $output; } /** * Generate the HTML for calling a javascript funtion. You often need to do this * if you have your javascript in an external file, and need to call one function * to initialise it. * * You can pass in an optional list of arguments, which are properly escaped for * you using the json_encode function. * * @param string $function the name of the JavaScript function to call. * @param array $args an optional list of arguments to the function call. * @param boolean $return if true, return the HTML code, otherwise output it. * @return mixed string if $return is true, otherwise nothing. */ function print_js_call($function, $args = array(), $return = false) { $quotedargs = array(); foreach ($args as $arg) { $quotedargs[] = json_encode($arg); } $html = ''; $html .= '\n"; if ($return) { return $html; } else { echo $html; } } /** * Generate the HTML for calling a javascript funtion after a time delay. * In other respects, this function is the same as print_js_call. * * @param integer $delay the desired delay in seconds. * @param string $function the name of the JavaScript function to call. * @param array $args an optional list of arguments to the function call. * @param boolean $return if true, return the HTML code, otherwise output it. * @return mixed string if $return is true, otherwise nothing. */ function print_delayed_js_call($delay, $function, $args = array(), $return = false) { $quotedargs = array(); foreach ($args as $arg) { $quotedargs[] = json_encode($arg); } $html = ''; $html .= '\n"; if ($return) { return $html; } else { echo $html; } } /** * Sometimes you need access to some values in your JavaScript that you can only * get from PHP code. You can handle this by generating your JS in PHP, but a * better idea is to write static javascrip code that reads some configuration * variable, and then just output the configuration variables from PHP using * this function. * * For example, look at the code in question_init_qenginejs_script() in * lib/questionlib.php. It writes out a bunch of $settings like * 'pixpath' => $CFG->pixpath, with $prefix = 'qengine_config'. This gets output * in print_header, then the code in question/qengine.js can access these variables * as qengine_config.pixpath, and so on. * * This method will also work without a prefix, but it is better to avoid that * we don't want to add more things than necessary to the global JavaScript scope. * * This method automatically wrapps the values in quotes, and addslashes_js them. * * @param array $settings the values you want to write out, as variablename => value. * @param string $prefix a namespace prefix to use in the JavaScript. * @param boolean $return if true, return the HTML code, otherwise output it. * @return mixed string if $return is true, otherwise nothing. */ function print_js_config($settings = array(), $prefix='', $return = false) { $html = ''; $html .= '\n"; if ($return) { return $html; } else { echo $html; } } /** * This function generates the code that defines the standard moodle_cfg object. * This object has a number of fields that are values that various pieces of * JavaScript code need access too. For example $CFG->wwwroot and $CFG->pixpath. * * @return string a "; */ function redirect($url, $message='', $delay=-1) { global $CFG, $THEME, $SESSION, $PAGE; if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()])) { $url = $SESSION->sid_process_url($url); } $message = clean_text($message); $encodedurl = preg_replace("/\&(?![a-zA-Z0-9#]{1,8};)/", "&", $url); $encodedurl = preg_replace('/^.*href="([^"]*)".*$/', "\\1", clean_text('')); $url = str_replace('&', '&', $encodedurl); /// At developer debug level. Don't redirect if errors have been printed on screen. /// Currenly only works in PHP 5.2+; we do not want strict PHP5 errors $lasterror = error_get_last(); $error = defined('DEBUGGING_PRINTED') or (!empty($lasterror) && ($lasterror['type'] & DEBUG_DEVELOPER)); $errorprinted = debugging('', DEBUG_ALL) && $CFG->debugdisplay && $error; if ($errorprinted) { $message = "Error output, so disabling automatic redirect." . $message; } $performanceinfo = ''; if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) { if (defined('MDL_PERFTOLOG') && !function_exists('register_shutdown_function')) { $perf = get_performance_info(); error_log("PERF: " . $perf['txt']); } } /// when no message and header printed yet, try to redirect if (empty($message) and !$PAGE->headerprinted) { // Technically, HTTP/1.1 requires Location: header to contain // the absolute path. (In practice browsers accept relative // paths - but still, might as well do it properly.) // This code turns relative into absolute. if (!preg_match('|^[a-z]+:|', $url)) { // Get host name http://www.wherever.com $hostpart = preg_replace('|^(.*?[^:/])/.*$|', '$1', $CFG->wwwroot); if (preg_match('|^/|', $url)) { // URLs beginning with / are relative to web server root so we just add them in $url = $hostpart.$url; } else { // URLs not beginning with / are relative to path of current script, so add that on. $url = $hostpart.preg_replace('|\?.*$|','',me()).'/../'.$url; } // Replace all ..s while (true) { $newurl = preg_replace('|/(?!\.\.)[^/]*/\.\./|', '/', $url); if ($newurl == $url) { break; } $url = $newurl; } } $delay = 0; //try header redirection first @header($_SERVER['SERVER_PROTOCOL'] . ' 303 See Other'); //302 might not work for POST requests, 303 is ignored by obsolete clients @header('Location: '.$url); //another way for older browsers and already sent headers (eg trailing whitespace in config.php) echo ''; print_js_call('document.location.replace', array($url)); die; } if ($delay == -1) { $delay = 3; // if no delay specified wait 3 seconds } if (!$PAGE->headerprinted) { // this type of redirect might not be working in some browsers - such as lynx :-( print_header('', '', '', '', $errorprinted ? '' : ('')); $delay += 3; // double redirect prevention, it was sometimes breaking upgrades before 1.7 } else { print_container_end_all(false, $THEME->open_header_containers); } echo ''; echo '' . $message . ''; echo '( '. get_string('continue') .' )'; echo ''; if (!$errorprinted) { print_delayed_js_call($delay, 'document.location.replace', array($url)); } $CFG->docroot = false; // to prevent the link to moodle docs from being displayed on redirect page. print_footer('none'); die; } /** * Print a bold message in an optional color. * * @param string $message The message to print out * @param string $style Optional style to display message text in * @param string $align Alignment option * @param bool $return whether to return an output string or echo now */ function notify($message, $style='notifyproblem', $align='center', $return=false) { global $DB; if ($style == 'green') { $style = 'notifysuccess'; // backward compatible with old color system } $message = clean_text($message); if (!CLI_SCRIPT) { $output = ''. $message .''."\n"; } else { if ($style === 'notifysuccess') { $output = '++'.$message.'++'; } else { $output = '!!'.$message.'!!'; } } if ($return) { return $output; } if (!CLI_SCRIPT) { echo $output; } else { console_write($output."\n", '', false); } } /** * Given an email address, this function will return an obfuscated version of it * * @param string $email The email address to obfuscate * @return string */ function obfuscate_email($email) { $i = 0; $length = strlen($email); $obfuscated = ''; while ($i < $length) { if (rand(0,2)) { $obfuscated.='%'.dechex(ord($email{$i})); } else { $obfuscated.=$email{$i}; } $i++; } return $obfuscated; } /** * This function takes some text and replaces about half of the characters * with HTML entity equivalents. Return string is obviously longer. * * @param string $plaintext The text to be obfuscated * @return string */ function obfuscate_text($plaintext) { $i=0; $length = strlen($plaintext); $obfuscated=''; $prev_obfuscated = false; while ($i < $length) { $c = ord($plaintext{$i}); $numerical = ($c >= ord('0')) && ($c <= ord('9')); if ($prev_obfuscated and $numerical ) { $obfuscated.=''.ord($plaintext{$i}).';'; } else if (rand(0,2)) { $obfuscated.=''.ord($plaintext{$i}).';'; $prev_obfuscated = true; } else { $obfuscated.=$plaintext{$i}; $prev_obfuscated = false; } $i++; } return $obfuscated; } /** * This function uses the {@link obfuscate_email()} and {@link obfuscate_text()} * to generate a fully obfuscated email link, ready to use. * * @param string $email The email address to display * @param string $label The text to dispalyed as hyperlink to $email * @param boolean $dimmed If true then use css class 'dimmed' for hyperlink * @return string */ function obfuscate_mailto($email, $label='', $dimmed=false) { if (empty($label)) { $label = $email; } if ($dimmed) { $title = get_string('emaildisable'); $dimmed = ' class="dimmed"'; } else { $title = ''; $dimmed = ''; } return sprintf("%s", obfuscate_text('mailto'), obfuscate_email($email), obfuscate_text($label)); } /** * Prints a single paging bar to provide access to other pages (usually in a search) * * @param int $totalcount Thetotal number of entries available to be paged through * @param int $page The page you are currently viewing * @param int $perpage The number of entries that should be shown per page * @param mixed $baseurl If this is a string then it is the url which will be appended with $pagevar, an equals sign and the page number. * If this is a moodle_url object then the pagevar param will be replaced by the page no, for each page. * @param string $pagevar This is the variable name that you use for the page number in your code (ie. 'tablepage', 'blogpage', etc) * @param bool $nocurr do not display the current page as a link * @param bool $return whether to return an output string or echo now * @return bool or string */ function print_paging_bar($totalcount, $page, $perpage, $baseurl, $pagevar='page',$nocurr=false, $return=false) { $maxdisplay = 18; $output = ''; if ($totalcount > $perpage) { $output .= ''; $output .= get_string('page') .':'; if ($page > 0) { $pagenum = $page - 1; if (!is_a($baseurl, 'moodle_url')){ $output .= ' ('. get_string('previous') .') '; } else { $output .= ' ('. get_string('previous') .') '; } } if ($perpage > 0) { $lastpage = ceil($totalcount / $perpage); } else { $lastpage = 1; } if ($page > 15) { $startpage = $page - 10; if (!is_a($baseurl, 'moodle_url')){ $output .= ' 1 ...'; } else { $output .= ' 1 ...'; } } else { $startpage = 0; } $currpage = $startpage; $displaycount = $displaypage = 0; while ($displaycount < $maxdisplay and $currpage < $lastpage) { $displaypage = $currpage+1; if ($page == $currpage && empty($nocurr)) { $output .= ' '. $displaypage; } else { if (!is_a($baseurl, 'moodle_url')){ $output .= ' '. $displaypage .''; } else { $output .= ' '. $displaypage .''; } } $displaycount++; $currpage++; } if ($currpage < $lastpage) { $lastpageactual = $lastpage - 1; if (!is_a($baseurl, 'moodle_url')){ $output .= ' ...'. $lastpage .' '; } else { $output .= ' ...'. $lastpage .' '; } } $pagenum = $page + 1; if ($pagenum != $displaypage) { if (!is_a($baseurl, 'moodle_url')){ $output .= ' ('. get_string('next') .')'; } else { $output .= ' ('. get_string('next') .')'; } } $output .= ''; } if ($return) { return $output; } echo $output; return true; } /** * This function is used to rebuild the tag because some formats (PLAIN and WIKI) * will transform it to html entities * * @param string $text Text to search for nolink tag in * @return string */ function rebuildnolinktag($text) { $text = preg_replace('/<(\/*nolink)>/i','<$1>',$text); return $text; } /** * Prints a nice side block with an optional header. The content can either * be a block of HTML or a list of text with optional icons. * * @param string $heading HTML for the heading. Can include full HTML or just * plain text - plain text will automatically be enclosed in the appropriate * heading tags. * @param string $content HTML for the content * @param array $list an alternative to $content, it you want a list of things with optional icons. * @param array $icons optional icons for the things in $list. * @param string $footer Extra HTML content that gets output at the end, inside a <div class="footer"> * @param array $attributes an array of attribute => value pairs that are put on the * outer div of this block. If there is a class attribute ' sideblock' gets appended to it. If there isn't * already a class, class='sideblock' is used. * @param string $title Plain text title, as embedded in the $heading. * @todo Finish documenting this function. Show example of various attributes, etc. */ function print_side_block($heading='', $content='', $list=NULL, $icons=NULL, $footer='', $attributes = array(), $title='') { //Accessibility: skip block link, with title-text (or $block_id) to differentiate links. static $block_id = 0; $block_id++; if (empty($heading)) { $skip_text = get_string('skipblock', 'access').' '.$block_id; } else { $skip_text = get_string('skipa', 'access', strip_tags($title)); } $skip_link = ''.$skip_text.''; $skip_dest = ''; if (! empty($heading)) { echo $skip_link; } //ELSE: a single link on a page "Skip block 4" is too confusing - ignore. print_side_block_start($heading, $attributes); // The content. if ($content) { echo $content; } else { if ($list) { $row = 0; //Accessibility: replaced unnecessary table with list, see themes/standard/styles_layout.css echo "\n\n"; foreach ($list as $key => $string) { echo ''; if ($icons) { echo ''. $icons[$key] .''; } echo ''. $string .''; echo "\n"; $row = $row ? 0:1; } echo "\n"; } } // Footer, if any. if ($footer) { echo ''; } print_side_block_end($attributes, $title); echo $skip_dest; } /** * Starts a nice side block with an optional header. * * @param string $heading HTML for the heading. Can include full HTML or just * plain text - plain text will automatically be enclosed in the appropriate * heading tags. * @param array $attributes ? * @todo Finish documenting this function */ function print_side_block_start($heading='', $attributes = array()) { global $CFG, $THEME; // If there are no special attributes, give a default CSS class if (empty($attributes) || !is_array($attributes)) { $attributes = array('class' => 'sideblock'); } else if(!isset($attributes['class'])) { $attributes['class'] = 'sideblock'; } else if(!strpos($attributes['class'], 'sideblock')) { $attributes['class'] .= ' sideblock'; } // OK, the class is surely there and in addition to anything // else, it's tagged as a sideblock /* // IE misery: if I do it this way, blocks which start hidden cannot be "unhidden" // If there is a cookie to hide this thing, start it hidden if (!empty($attributes['id']) && isset($_COOKIE['hide:'.$attributes['id']])) { $attributes['class'] = 'hidden '.$attributes['class']; } */ $attrtext = ''; foreach ($attributes as $attr => $val) { $attrtext .= ' '.$attr.'="'.$val.'"'; } echo ''; if (!empty($THEME->customcorners)) { echo ''."\n"; } if ($heading) { // Some callers pass in complete html for the heading, which may include // complicated things such as the 'hide block' button; some just pass in // text. If they only pass in plain text i.e. it doesn't include a // , then we add in standard tags that make it look like a normal // page block including the h2 for accessibility if(strpos($heading,'')===false) { $heading=''.$heading.''; } echo ''; if (!empty($THEME->customcorners)) { echo ' '; echo ''; echo ''; } echo $heading; if (!empty($THEME->customcorners)) { echo ''; } echo ''; } else { if (!empty($THEME->customcorners)) { echo ' '; } } if (!empty($THEME->customcorners)) { echo ''; echo ''; } echo ''; } /** * Print table ending tags for a side block box. */ function print_side_block_end($attributes = array(), $title='') { global $CFG, $THEME; echo ''; if (!empty($THEME->customcorners)) { echo ' '; } echo ''; $strshow = addslashes_js(get_string('showblocka', 'access', strip_tags($title))); $strhide = addslashes_js(get_string('hideblocka', 'access', strip_tags($title))); // IE workaround: if I do it THIS way, it works! WTF? if (!empty($CFG->allowuserblockhiding) && isset($attributes['id'])) { echo ''; } } /** * @deprecated since Moodle 2.0 - use $PAGE->pagetype instead of the . * @param string $getid used to return $PAGE->pagetype. * @param string $getclass used to return $PAGE->legacyclass. */ function page_id_and_class(&$getid, &$getclass) { global $PAGE; debugging('Call to deprecated function page_id_and_class. Please use $PAGE->pagetype instead.', DEBUG_DEVELOPER); $getid = $PAGE->pagetype; $getclass = $PAGE->legacyclass; } /** * Prints a maintenance message from /maintenance.html */ function print_maintenance_message () { global $CFG, $SITE; $PAGE->set_pagetype('maintenance-message'); print_header(strip_tags($SITE->fullname), $SITE->fullname, 'home'); print_box_start(); print_heading(get_string('sitemaintenance', 'admin')); @include($CFG->dataroot.'/1/maintenance.html'); print_box_end(); print_footer(); } /** * Adjust the list of allowed tags based on $CFG->allowobjectembed and user roles (admin) */ function adjust_allowed_tags() { global $CFG, $ALLOWED_TAGS; if (!empty($CFG->allowobjectembed)) { $ALLOWED_TAGS .= ''; } } /// Some code to print tabs /// A class for tabs class tabobject { var $id; var $link; var $text; var $linkedwhenselected; /// A constructor just because I like constructors function tabobject ($id, $link='', $text='', $title='', $linkedwhenselected=false) { $this->id = $id; $this->link = $link; $this->text = $text; $this->title = $title ? $title : $text; $this->linkedwhenselected = $linkedwhenselected; } } /** * Returns a string containing a nested list, suitable for formatting into tabs with CSS. * * @param array $tabrows An array of rows where each row is an array of tab objects * @param string $selected The id of the selected tab (whatever row it's on) * @param array $inactive An array of ids of inactive tabs that are not selectable. * @param array $activated An array of ids of other tabs that are currently activated **/ function print_tabs($tabrows, $selected=NULL, $inactive=NULL, $activated=NULL, $return=false) { global $CFG; /// $inactive must be an array if (!is_array($inactive)) { $inactive = array(); } /// $activated must be an array if (!is_array($activated)) { $activated = array(); } /// Convert the tab rows into a tree that's easier to process if (!$tree = convert_tabrows_to_tree($tabrows, $selected, $inactive, $activated)) { return false; } /// Print out the current tree of tabs (this function is recursive) $output = convert_tree_to_html($tree); $output = "\n\n".''.$output.' '."\n\n"; /// We're done! if ($return) { return $output; } echo $output; } function convert_tree_to_html($tree, $row=0) { $str = "\n".''."\n"; $first = true; $count = count($tree); foreach ($tree as $tab) { $count--; // countdown to zero $liclass = ''; if ($first && ($count == 0)) { // Just one in the row $liclass = 'first last'; $first = false; } else if ($first) { $liclass = 'first'; $first = false; } else if ($count == 0) { $liclass = 'last'; } if ((empty($tab->subtree)) && (!empty($tab->selected))) { $liclass .= (empty($liclass)) ? 'onerow' : ' onerow'; } if ($tab->inactive || $tab->active || $tab->selected) { if ($tab->selected) { $liclass .= (empty($liclass)) ? 'here selected' : ' here selected'; } else if ($tab->active) { $liclass .= (empty($liclass)) ? 'here active' : ' here active'; } } $str .= (!empty($liclass)) ? '' : ''; if ($tab->inactive || $tab->active || ($tab->selected && !$tab->linkedwhenselected)) { // The a tag is used for styling $str .= ''.$tab->text.''; } else { $str .= ''.$tab->text.''; } if (!empty($tab->subtree)) { $str .= convert_tree_to_html($tab->subtree, $row+1); } else if ($tab->selected) { $str .= ' '."\n"; } $str .= ' '."\n"; } $str .= ''."\n"; return $str; } function convert_tabrows_to_tree($tabrows, $selected, $inactive, $activated) { /// Work backwards through the rows (bottom to top) collecting the tree as we go. $tabrows = array_reverse($tabrows); $subtree = array(); foreach ($tabrows as $row) { $tree = array(); foreach ($row as $tab) { $tab->inactive = in_array((string)$tab->id, $inactive); $tab->active = in_array((string)$tab->id, $activated); $tab->selected = (string)$tab->id == $selected; if ($tab->active || $tab->selected) { if ($subtree) { $tab->subtree = $subtree; } } $tree[] = $tab; } $subtree = $tree; } return $subtree; } /** * Returns a string containing a link to the user documentation for the current * page. Also contains an icon by default. Shown to teachers and admin only. * * @param string $text The text to be displayed for the link * @param string $iconpath The path to the icon to be displayed */ function page_doc_link($text='', $iconpath='') { global $CFG, $PAGE; if (empty($CFG->docroot) || empty($CFG->rolesactive)) { return ''; } if (!has_capability('moodle/site:doclinks', $PAGE->context)) { return ''; } $path = $PAGE->docspath; if (!$path) { return ''; } return doc_link($path, $text, $iconpath); } /** * @param string $path the end of the URL. * @return The MoodleDocs URL in the user's language. for example http://docs.moodle.org/en/$path */ function get_docs_url($path) { global $CFG; return $CFG->docroot . '/' . str_replace('_utf8', '', current_language()) . '/' . $path; } /** * Returns a string containing a link to the user documentation. * Also contains an icon by default. Shown to teachers and admin only. * * @param string $path The page link after doc root and language, no leading slash. * @param string $text The text to be displayed for the link * @param string $iconpath The path to the icon to be displayed */ function doc_link($path='', $text='', $iconpath='') { global $CFG; if (empty($CFG->docroot)) { return ''; } $url = get_docs_url($path); $target = ''; if (!empty($CFG->doctonewwindow)) { $target = " onclick=\"window.open('$url'); return false;\""; } $str = ""; if (empty($iconpath)) { $iconpath = $CFG->httpswwwroot . '/pix/docs.gif'; } // alt left blank intentionally to prevent repetition in screenreaders $str .= '' .$text. ''; return $str; } /** * Returns true if the current site debugging settings are equal or above specified level. * If passed a parameter it will emit a debugging notice similar to trigger_error(). The * routing of notices is controlled by $CFG->debugdisplay * eg use like this: * * 1) debugging('a normal debug notice'); * 2) debugging('something really picky', DEBUG_ALL); * 3) debugging('annoying debug message only for develpers', DEBUG_DEVELOPER); * 4) if (debugging()) { perform extra debugging operations (do not use print or echo) } * * In code blocks controlled by debugging() (such as example 4) * any output should be routed via debugging() itself, or the lower-level * trigger_error() or error_log(). Using echo or print will break XHTML * JS and HTTP headers. * * * @param string $message a message to print * @param int $level the level at which this debugging statement should show * @param array $backtrace use different backtrace * @return bool */ function debugging($message='', $level=DEBUG_NORMAL, $backtrace=null) { global $CFG; if (empty($CFG->debug)) { return false; } if ($CFG->debug >= $level) { if ($message) { if (!$backtrace) { $backtrace = debug_backtrace(); } $from = print_backtrace($backtrace, true); if (!isset($CFG->debugdisplay)) { $CFG->debugdisplay = ini_get_bool('display_errors'); } if ($CFG->debugdisplay) { if (!defined('DEBUGGING_PRINTED')) { define('DEBUGGING_PRINTED', 1); // indicates we have printed something } notify($message . $from, 'notifytiny'); } else { trigger_error($message . $from, E_USER_NOTICE); } } return true; } return false; } /** * Prints formatted backtrace * @param backtrace array * @param return return as string or print * @return mixed */ function print_backtrace($callers, $return=false) { global $CFG; if (empty($callers)) { if ($return) { return ''; } else { return; } } $from = ''; foreach ($callers as $caller) { if (!isset($caller['line'])) { $caller['line'] = '?'; // probably call_user_func() } if (!isset($caller['file'])) { $caller['file'] = $CFG->dirroot.'/unknownfile'; // probably call_user_func() } $from .= 'line ' . $caller['line'] . ' of ' . substr($caller['file'], strlen($CFG->dirroot) + 1); if (isset($caller['function'])) { $from .= ': call to '; if (isset($caller['class'])) { $from .= $caller['class'] . $caller['type']; } $from .= $caller['function'] . '()'; } else if (isset($caller['exception'])) { $from .= ': '.$caller['exception'].' thrown'; } $from .= ''; } $from .= ''; if ($return) { return $from; } else { echo $from; } } /** * Disable debug messages from debugging(), while keeping PHP error reporting level as is. */ function disable_debugging() { global $CFG; $CFG->debug = $CFG->debug | 0x80000000; // switch the sign bit in integer number ;-) } /** * Returns string to add a frame attribute, if required */ function frametarget() { global $CFG; if (empty($CFG->framename) or ($CFG->framename == '_top')) { return ''; } else { return ' target="'.$CFG->framename.'" '; } } /** * Outputs a HTML comment to the browser. This is used for those hard-to-debug * pages that use bits from many different files in very confusing ways (e.g. blocks). * @usage print_location_comment(__FILE__, __LINE__); * @param string $file * @param integer $line * @param boolean $return Whether to return or print the comment * @return mixed Void unless true given as third parameter */ function print_location_comment($file, $line, $return = false) { if ($return) { return "\n"; } else { echo "\n"; } } /** * Returns an image of an up or down arrow, used for column sorting. To avoid unnecessary DB accesses, please * provide this function with the language strings for sortasc and sortdesc. * If no sort string is associated with the direction, an arrow with no alt text will be printed/returned. * @param string $direction 'up' or 'down' * @param string $strsort The language string used for the alt attribute of this image * @param bool $return Whether to print directly or return the html string * @return string HTML for the image * * TODO See if this isn't already defined somewhere. If not, move this to weblib */ function print_arrow($direction='up', $strsort=null, $return=false) { global $CFG; if (!in_array($direction, array('up', 'down', 'right', 'left', 'move'))) { return null; } $return = null; switch ($direction) { case 'up': $sortdir = 'asc'; break; case 'down': $sortdir = 'desc'; break; case 'move': $sortdir = 'asc'; break; default: $sortdir = null; break; } // Prepare language string $strsort = ''; if (empty($strsort) && !empty($sortdir)) { $strsort = get_string('sort' . $sortdir, 'grades'); } $return = ' '; if ($return) { return $return; } else { echo $return; } } /** * Returns boolean true if the current language is right-to-left (Hebrew, Arabic etc) * */ function right_to_left() { static $result; if (isset($result)) { return $result; } return $result = (get_string('thisdirection') == 'rtl'); } /** * Returns swapped left<=>right if in RTL environment. * part of RTL support * * @param string $align align to check * @return string */ function fix_align_rtl($align) { if (!right_to_left()) { return $align; } if ($align=='left') { return 'right'; } if ($align=='right') { return 'left'; } return $align; } /** * Returns true if the page is displayed in a popup window. * Gets the information from the URL parameter inpopup. * * @return boolean * * TODO Use a central function to create the popup calls allover Moodle and * TODO In the moment only works with resources and probably questions. */ function is_in_popup() { $inpopup = optional_param('inpopup', '', PARAM_BOOL); return ($inpopup); } //=========================================================================// /** * Write to standard out and error with exit in error. * * @param standard out/err $stream * @param string $identifier * @param name of module $module */ function console_write($identifier, $module='install', $use_string_lib=true) { if (!isset($_SERVER['REMOTE_ADDR'])) { // real CLI script if ($use_string_lib) { fwrite(STDOUT, get_string($identifier, $module)); } else { fwrite(STDOUT, $identifier); } } else { // emulated cli script - something like cron if ($use_string_lib) { echo get_string($identifier, $module); } else { echo $identifier; } } } //=========================================================================// /** * Write to standard out and error with exit in error. * * @param standard out/err $stream * @param string $identifier * @param name of module $module */ function console_write_error($identifier, $module='install', $use_string_lib=true) { if (!isset($_SERVER['REMOTE_ADDR'])) { // real CLI script if ($use_string_lib) { fwrite(STDERR, get_string($identifier, $module)); } else { fwrite(STDERR, $identifier); } fwrite(STDERR, "\n\n".get_string('aborting', $module)."\n\n"); } else { // emulated cli script - something like cron if ($use_string_lib) { echo get_string($identifier, $module); } else { echo $identifier; } echo "\n\n".get_string('aborting', $module)."\n\n"; } die; die; die; } /** * To use this class. * - construct * - call create (or use the 3rd param to the constructor) * - call update or update_full repeatedly * - */ class progress_bar { private $html_id; private $percent; private $width; private $clr; private $lastcall; private $time_start; private $minimum_time = 2; //min time between updates. function __construct($html_id = 'pid', $width = 500, $autostart = false){ $this->html_id = $html_id; $this->clr = new stdClass; $this->clr->done = 'green'; $this->clr->process = '#FFCC66'; $this->width = $width; $this->restart(); if($autostart){ $this->create(); } } /** * set progress bar color, call before $this->create * Usage: * $clr->done = 'red'; * $clr->process = 'blue'; * $pb->setclr($clr); * $pb->create(); * ...... * * @param $clr object */ function setclr($clr){ foreach($clr as $n=>$v) { $this->clr->$n = $v; } } /** * Create a new progress bar, this function will output * html. * */ function create(){ flush(); $this->lastcall->pt = 0; $this->lastcall->time = microtime(true); $htmlcode = << Number.prototype.fixed=function(n){ with(Math) return round(Number(this)*pow(10,n))/pow(10,n); } function up_{$this->html_id} (id, width, pt, msg, es){ percent = pt*100; document.getElementById("status_"+id).innerHTML = msg; document.getElementById("pt_"+id).innerHTML = percent.fixed(2) + '%'; if(percent == 100) { document.getElementById("progress_"+id).style.background = "{$this->clr->done}"; document.getElementById("time_"+id).style.display = "none"; } else { document.getElementById("progress_"+id).style.background = "{$this->clr->process}"; if (es == Infinity){ document.getElementById("time_"+id).innerHTML = "Initializing..."; }else { document.getElementById("time_"+id).innerHTML = es.fixed(2)+" sec"; document.getElementById("time_"+id).style.display = "block"; } } document.getElementById("progress_"+id).style.width = width + "px"; } EOT; echo $htmlcode; flush(); } function _update($percent, $msg, $es){ if(empty($this->time_start)){ $this->time_start = microtime(true); } $this->percent = $percent; $this->lastcall->time = microtime(true); $this->lastcall->pt = $percent; $w = $this->percent * $this->width; if ($es === null){ $es = "Infinity"; } echo ""; flush(); } /** * estimate time * * @param $curtime int the time call this function * @param $percent int */ function estimate($curtime, $pt){ $consume = $curtime - $this->time_start; $one = $curtime - $this->lastcall->time; $this->percent = $pt; $percent = $pt - $this->lastcall->pt; if ($percent != 0) { $left = ($one / $percent) - $consume; } else { return null; } if($left < 0) { return 0; } else { return $left; } } /** * Update progress bar according percent * * @param $percent int from 1-100 * @param $msg string the message needed to be shown */ function update_full($percent, $msg){ $percent = max(min($percent, 100), 0); if ($percent != 100 && ($this->lastcall->time + $this->minimum_time) > microtime(true)){ return; } $this->_update($percent/100, $msg); } /** * Update progress bar according the nubmer of tasks * * @param $cur int current task number * @param $total int total task number * @param $msg string message */ function update($cur, $total, $msg){ $cur = max($cur, 0); if ($cur >= $total){ $percent = 1; } else { $percent = $cur / $total; } /** if ($percent != 1 && ($this->lastcall->time + $this->minimum_time) > microtime(true)){ return; } */ $es = $this->estimate(microtime(true), $percent); $this->_update($percent, $msg, $es); } /** * Restart the progress bar. */ function restart(){ $this->percent = 0; $this->lastcall = new stdClass; $this->lastcall->pt = 0; $this->lastcall->time = microtime(true); $this->time_start = 0; } } /** * Use this class from long operations where you want to output occasional information about * what is going on, but don't know if, or in what format, the output should be. */ abstract class moodle_progress_trace { /** * Ouput an progress message in whatever format. * @param string $message the message to output. * @param integer $depth indent depth for this message. */ abstract public function output($message, $depth = 0); /** * Called when the processing is finished. */ public function finished() { } } /** * This subclass of moodle_progress_trace does not ouput anything. */ class null_progress_trace extends moodle_progress_trace { public function output($message, $depth = 0) { } } /** * This subclass of moodle_progress_trace outputs to plain text. */ class text_progress_trace extends moodle_progress_trace { public function output($message, $depth = 0) { echo str_repeat(' ', $depth), $message, "\n"; flush(); } } /** * This subclass of moodle_progress_trace outputs as HTML. */ class html_progress_trace extends moodle_progress_trace { public function output($message, $depth = 0) { echo '', str_repeat(' ', $depth), htmlspecialchars($message), "\n"; flush(); } } class html_list_progress_trace extends moodle_progress_trace { protected $currentdepth = -1; public function output($message, $depth = 0) { $samedepth = true; while ($this->currentdepth > $depth) { echo "
' . get_string('windowclosing') . '
NOTICE: Wiki-like formatting has been removed from Moodle. You should not be seeing this message as all texts should have been converted to Markdown format instead. Please post a bug report to http://moodle.org/bugs with information about where you saw this message.
" . $message; } $performanceinfo = ''; if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) { if (defined('MDL_PERFTOLOG') && !function_exists('register_shutdown_function')) { $perf = get_performance_info(); error_log("PERF: " . $perf['txt']); } } /// when no message and header printed yet, try to redirect if (empty($message) and !$PAGE->headerprinted) { // Technically, HTTP/1.1 requires Location: header to contain // the absolute path. (In practice browsers accept relative // paths - but still, might as well do it properly.) // This code turns relative into absolute. if (!preg_match('|^[a-z]+:|', $url)) { // Get host name http://www.wherever.com $hostpart = preg_replace('|^(.*?[^:/])/.*$|', '$1', $CFG->wwwroot); if (preg_match('|^/|', $url)) { // URLs beginning with / are relative to web server root so we just add them in $url = $hostpart.$url; } else { // URLs not beginning with / are relative to path of current script, so add that on. $url = $hostpart.preg_replace('|\?.*$|','',me()).'/../'.$url; } // Replace all ..s while (true) { $newurl = preg_replace('|/(?!\.\.)[^/]*/\.\./|', '/', $url); if ($newurl == $url) { break; } $url = $newurl; } } $delay = 0; //try header redirection first @header($_SERVER['SERVER_PROTOCOL'] . ' 303 See Other'); //302 might not work for POST requests, 303 is ignored by obsolete clients @header('Location: '.$url); //another way for older browsers and already sent headers (eg trailing whitespace in config.php) echo ''; print_js_call('document.location.replace', array($url)); die; } if ($delay == -1) { $delay = 3; // if no delay specified wait 3 seconds } if (!$PAGE->headerprinted) { // this type of redirect might not be working in some browsers - such as lynx :-( print_header('', '', '', '', $errorprinted ? '' : ('')); $delay += 3; // double redirect prevention, it was sometimes breaking upgrades before 1.7 } else { print_container_end_all(false, $THEME->open_header_containers); } echo '
', str_repeat(' ', $depth), htmlspecialchars($message), "