Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

5474 lines (4718 sloc) 184.175 kb
<?php // $Id$
///////////////////////////////////////////////////////////////////////////
// //
// NOTICE OF COPYRIGHT //
// //
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
// http://moodle.com //
// //
// Copyright (C) 2001-2003 Martin Dougiamas http://dougiamas.com //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation; either version 2 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License for more details: //
// //
// http://www.gnu.org/copyleft/gpl.html //
// //
///////////////////////////////////////////////////////////////////////////
/**
* Library of functions for web output
*
* Library of all general-purpose Moodle PHP functions and constants
* that produce HTML output
*
* Other main libraries:
* - datalib.php - functions that access the database.
* - moodlelib.php - general-purpose Moodle functions.
* @author Martin Dougiamas
* @version $Id$
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package moodlecore
*/
/// We are going to uses filterlib functions here
require_once("$CFG->libdir/filterlib.php");
/// Constants
/// Define text formatting types ... eventually we can add Wiki, BBcode etc
/**
* Does all sorts of transformations and filtering
*/
define('FORMAT_MOODLE', '0'); // Does all sorts of transformations and filtering
/**
* Plain HTML (with some tags stripped)
*/
define('FORMAT_HTML', '1'); // Plain HTML (with some tags stripped)
/**
* Plain text (even tags are printed in full)
*/
define('FORMAT_PLAIN', '2'); // Plain text (even tags are printed in full)
/**
* Wiki-formatted text
* Deprecated: left here just to note that '3' is not used (at the moment)
* and to catch any latent wiki-like text (which generates an error)
*/
define('FORMAT_WIKI', '3'); // Wiki-formatted text
/**
* Markdown-formatted text http://daringfireball.net/projects/markdown/
*/
define('FORMAT_MARKDOWN', '4'); // Markdown-formatted text http://daringfireball.net/projects/markdown/
/**
* TRUSTTEXT marker - if present in text, text cleaning should be bypassed
*/
define('TRUSTTEXT', '#####TRUSTTEXT#####');
/**
* Allowed tags - string of html tags that can be tested against for safe html tags
* @global string $ALLOWED_TAGS
*/
$ALLOWED_TAGS =
'<p><br><b><i><u><font><table><tbody><span><div><tr><td><th><ol><ul><dl><li><dt><dd><h1><h2><h3><h4><h5><h6><hr><img><a><strong><emphasis><em><sup><sub><address><cite><blockquote><pre><strike><param><acronym><nolink><lang><tex><algebra><math><mi><mn><mo><mtext><mspace><ms><mrow><mfrac><msqrt><mroot><mstyle><merror><mpadded><mphantom><mfenced><msub><msup><msubsup><munder><mover><munderover><mmultiscripts><mtable><mtr><mtd><maligngroup><malignmark><maction><cn><ci><apply><reln><fn><interval><inverse><sep><condition><declare><lambda><compose><ident><quotient><exp><factorial><divide><max><min><minus><plus><power><rem><times><root><gcd><and><or><xor><not><implies><forall><exists><abs><conjugate><eq><neq><gt><lt><geq><leq><ln><log><int><diff><partialdiff><lowlimit><uplimit><bvar><degree><set><list><union><intersect><in><notin><subset><prsubset><notsubset><notprsubset><setdiff><sum><product><limit><tendsto><mean><sdev><variance><median><mode><moment><vector><matrix><matrixrow><determinant><transpose><selector><annotation><semantics><annotation-xml><tt><code>';
/**
* 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',
'border', 'margin', 'padding', 'background'); // 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 $strip to decide if we want to strip slashes or no. Default to false.
* true should be used to print data from forms and false for data from DB.
* @return string
*/
function s($var, $strip=false) {
if ($var == '0') { // for integer 0, boolean false, string '0'
return '0';
}
if ($strip) {
return preg_replace("/&amp;(#\d+);/i", "&$1;", htmlspecialchars(stripslashes_safe($var)));
} else {
return preg_replace("/&amp;(#\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 $strip to decide if we want to strip slashes or no. Default to false.
* true should be used to print data from forms and false for data from DB.
* @return string
*/
function p($var, $strip=false) {
echo s($var, $strip);
}
/**
* Ensure that a variable is set
*
* Return $var if it is defined, otherwise return $default,
* This function is very similar to {@link optional_variable()}
*
* @param mixed $var the variable which may be unset
* @param mixed $default the value to return if $var is unset
* @return mixed
*/
function nvl(&$var, $default='') {
global $CFG;
if (!empty($CFG->disableglobalshack)) {
error( "The nvl() function is deprecated ($var, $default)." );
}
return isset($var) ? $var : $default;
}
/**
* 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
* @return string
*/
function get_referer() {
return strip_querystring(nvl($_SERVER['HTTP_REFERER']));
}
/**
* 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.)
* <b>NOTE:</b> This function returns false if the global variables needed are not set.
*
* @return string
*/
function me() {
if (!empty($_SERVER['REQUEST_URI'])) {
return $_SERVER['REQUEST_URI'];
} else if (!empty($_SERVER['PHP_SELF'])) {
if (!empty($_SERVER['QUERY_STRING'])) {
return $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING'];
}
return $_SERVER['PHP_SELF'];
} else if (!empty($_SERVER['SCRIPT_NAME'])) {
if (!empty($_SERVER['QUERY_STRING'])) {
return $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
}
return $_SERVER['SCRIPT_NAME'];
} else if (!empty($_SERVER['URL'])) { // May help IIS (not well tested)
if (!empty($_SERVER['QUERY_STRING'])) {
return $_SERVER['URL'] .'?'. $_SERVER['QUERY_STRING'];
}
return $_SERVER['URL'];
} else {
notify('Warning: Could not find any of these web server variables: $REQUEST_URI, $PHP_SELF, $SCRIPT_NAME or $URL');
return false;
}
}
/**
* Like {@link me()} but returns a full URL
* @see me()
* @return string
*/
function qualified_me() {
global $CFG;
if (!empty($CFG->wwwroot)) {
$url = parse_url($CFG->wwwroot);
}
if (!empty($url['host'])) {
$hostname = $url['host'];
} else if (!empty($_SERVER['SERVER_NAME'])) {
$hostname = $_SERVER['SERVER_NAME'];
} else if (!empty($_ENV['SERVER_NAME'])) {
$hostname = $_ENV['SERVER_NAME'];
} else if (!empty($_SERVER['HTTP_HOST'])) {
$hostname = $_SERVER['HTTP_HOST'];
} else if (!empty($_ENV['HTTP_HOST'])) {
$hostname = $_ENV['HTTP_HOST'];
} else {
notify('Warning: could not find the name of this server!');
return false;
}
if (!empty($url['port'])) {
$hostname .= ':'.$url['port'];
} else if (!empty($_SERVER['SERVER_PORT'])) {
if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
$hostname .= ':'.$_SERVER['SERVER_PORT'];
}
}
if (isset($_SERVER['HTTPS'])) {
$protocol = ($_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
} else if (isset($_SERVER['SERVER_PORT'])) { # Apache2 does not export $_SERVER['HTTPS']
$protocol = ($_SERVER['SERVER_PORT'] == '443') ? 'https://' : 'http://';
} else {
$protocol = 'http://';
}
$url_prefix = $protocol.$hostname;
return $url_prefix . me();
}
/**
* Determine if a web referer is valid
*
* Returns true if the referer is the same as the goodreferer. If
* the referer to test is not specified, use {@link qualified_me()}.
* If the admin has not set secure forms ($CFG->secureforms) then
* this function returns true regardless of a match.
*
* @uses $CFG
* @param string $goodreferer the url to compare to referer
* @return boolean
*/
function match_referer($goodreferer = '') {
global $CFG;
if (empty($CFG->secureforms)) { // Don't bother checking referer
return true;
}
if ($goodreferer == 'nomatch') { // Don't bother checking referer
return true;
}
if (empty($goodreferer)) {
$goodreferer = qualified_me();
// try to remove everything after ? because POST url may contain GET parameters (SID rewrite, etc.)
$pos = strpos($goodreferer, '?');
if ($pos !== FALSE) {
$goodreferer = substr($goodreferer, 0, $pos);
}
}
$referer = get_referer();
return (($referer == $goodreferer) or ($referer == $CFG->wwwroot .'/') or ($referer == $CFG->wwwroot .'/index.php'));
}
/**
* 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 also
* checks the referer against the given url (it uses
* the current page if none was specified.
*
* @uses $CFG
* @param string $url the url to compare to referer for secure forms
* @return boolean
*/
function data_submitted($url='') {
global $CFG;
if (empty($_POST)) {
return false;
} else {
if (match_referer($url)) {
return (object)$_POST;
} else {
debugging('The form did not come from this page! (referer = '. get_referer() .')');
return false;
}
}
}
/**
* Moodle replacement for php stripslashes() function,
* works also for objects and arrays.
*
* The standard php stripslashes() removes ALL backslashes
* even from strings - so C:\temp becomes C:temp - this isn't good.
* This function should work as a fairly safe replacement
* to be called on quoted AND unquoted strings (to be sure)
*
* @param mixed something to remove unsafe slashes from
* @return mixed
*/
function stripslashes_safe($mixed) {
// there is no need to remove slashes from int, float and bool types
if (empty($mixed)) {
//nothing to do...
} else if (is_string($mixed)) {
if (ini_get_bool('magic_quotes_sybase')) { //only unescape single quotes
$mixed = str_replace("''", "'", $mixed);
} else { //the rest, simple and double quotes and backslashes
$mixed = str_replace("\\'", "'", $mixed);
$mixed = str_replace('\\"', '"', $mixed);
$mixed = str_replace('\\\\', '\\', $mixed);
}
} else if (is_array($mixed)) {
foreach ($mixed as $key => $value) {
$mixed[$key] = stripslashes_safe($value);
}
} else if (is_object($mixed)) {
$vars = get_object_vars($mixed);
foreach ($vars as $key => $value) {
$mixed->$key = stripslashes_safe($value);
}
}
return $mixed;
}
/**
* Recursive implementation of stripslashes()
*
* This function will allow you to strip the slashes from a variable.
* If the variable is an array or object, slashes will be stripped
* from the items (or properties) it contains, even if they are arrays
* or objects themselves.
*
* @param mixed the variable to remove slashes from
* @return mixed
*/
function stripslashes_recursive($var) {
if(is_object($var)) {
$properties = get_object_vars($var);
foreach($properties as $property => $value) {
$var->$property = stripslashes_recursive($value);
}
}
else if(is_array($var)) {
foreach($var as $property => $value) {
$var[$property] = stripslashes_recursive($value);
}
}
else if(is_string($var)) {
$var = stripslashes($var);
}
return $var;
}
/**
* 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, current_charset());
$wordlength = 0;
for ($i=0; $i<$length; $i++) {
$char = $textlib->substr($string, $i, 1, current_charset());
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 does a search and replace, ignoring case
* This function is only used for versions of PHP older than version 5
* which do not have a native version of this function.
* Taken from the PHP manual, by bradhuizenga @ softhome.net
*
* @param string $find the string to search for
* @param string $replace the string to replace $find with
* @param string $string the string to search through
* return string
*/
if (!function_exists('str_ireplace')) { /// Only exists in PHP 5
function str_ireplace($find, $replace, $string) {
if (!is_array($find)) {
$find = array($find);
}
if(!is_array($replace)) {
if (!is_array($find)) {
$replace = array($replace);
} else {
// this will duplicate the string into an array the size of $find
$c = count($find);
$rString = $replace;
unset($replace);
for ($i = 0; $i < $c; $i++) {
$replace[$i] = $rString;
}
}
}
foreach ($find as $fKey => $fItem) {
$between = explode(strtolower($fItem),strtolower($string));
$pos = 0;
foreach($between as $bKey => $bItem) {
$between[$bKey] = substr($string,$pos,strlen($bItem));
$pos += strlen($bItem) + strlen($fItem);
}
$string = implode($replace[$fKey],$between);
}
return ($string);
}
}
/**
* Locate the position of a string in another string
*
* This function is only used for versions of PHP older than version 5
* which do not have a native version of this function.
* Taken from the PHP manual, by dmarsh @ spscc.ctc.edu
*
* @param string $haystack The string to be searched
* @param string $needle The string to search for
* @param int $offset The position in $haystack where the search should begin.
*/
if (!function_exists('stripos')) { /// Only exists in PHP 5
function stripos($haystack, $needle, $offset=0) {
return strpos(strtoupper($haystack), strtoupper($needle), $offset);
}
}
/**
* Load a template from file
*
* Returns a (big) string containing the contents of a template file with all
* the variables interpolated. all the variables must be in the $var[] array or
* object (whatever you decide to use).
*
* <b>WARNING: do not use this on big files!!</b>
*
* @param string $filename Location on the server's filesystem where template can be found.
* @param mixed $var Passed in by reference. An array or object which will be loaded with data from the template file.
*/
function read_template($filename, &$var) {
$temp = str_replace("\\", "\\\\", implode(file($filename), ''));
$temp = str_replace('"', '\"', $temp);
eval("\$template = \"$temp\";");
return $template;
}
/**
* Set a variable's value depending on whether or not it already has a value.
*
* If variable is set, set it to the set_value otherwise set it to the
* unset_value. used to handle checkboxes when you are expecting them from
* a form
*
* @param mixed $var Passed in by reference. The variable to check.
* @param mixed $set_value The value to set $var to if $var already has a value.
* @param mixed $unset_value The value to set $var to if $var does not already have a value.
*/
function checked(&$var, $set_value = 1, $unset_value = 0) {
if (empty($var)) {
$var = $unset_value;
} else {
$var = $set_value;
}
}
/**
* Prints the word "checked" if a variable is true, otherwise prints nothing,
* used for printing the word "checked" in a checkbox form element.
*
* @param boolean $var Variable to be checked for true value
* @param string $true_value Value to be printed if $var is true
* @param string $false_value Value to be printed if $var is false
*/
function frmchecked(&$var, $true_value = 'checked', $false_value = '') {
if ($var) {
echo $true_value;
} else {
echo $false_value;
}
}
/**
* This function will create a HTML link that will work on both
* Javascript and non-javascript browsers.
* Relies on the Javascript function openpopup in javascript.php
*
* $url must be relative to home page eg /mod/survey/stuff.php
* @param string $url Web link relative to home page
* @param string $name Name to be assigned 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
* @todo Add code examples and list of some options that might be used.
* @param boolean $return Should the link to the popup window be returned as a string (true) or printed immediately (false)?
* @return string
* @uses $CFG
*/
function link_to_popup_window ($url, $name='popup', $linkname='click here',
$height=400, $width=500, $title='Popup window',
$options='none', $return=false) {
global $CFG;
if ($options == 'none') {
$options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
}
$fullscreen = 0;
if (!(strpos($url,$CFG->wwwroot) === false)) { // some log url entries contain _SERVER[HTTP_REFERRER] in which case wwwroot is already there.
$url = substr($url, strlen($CFG->wwwroot));
}
$link = '<a target="'. $name .'" title="'. $title .'" href="'. $CFG->wwwroot . $url .'" '.
"onclick=\"return openpopup('$url', '$name', '$options', $fullscreen);\">$linkname</a>";
if ($return) {
return $link;
} else {
echo $link;
}
}
/**
* This function will print a button submit form element
* that will work on both Javascript and non-javascript browsers.
* Relies on the Javascript function openpopup in javascript.php
*
* $url must be relative to home page eg /mod/survey/stuff.php
* @param string $url Web link relative to home page
* @param string $name Name to be assigned 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
* @return string
* @uses $CFG
*/
function button_to_popup_window ($url, $name='popup', $linkname='click here',
$height=400, $width=500, $title='Popup window', $options='none', $return=false,
$id='', $class='') {
global $CFG;
if ($options == 'none') {
$options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
}
if ($id) {
$id = ' id="'.$id.'" ';
}
if ($class) {
$class = ' class="'.$class.'" ';
}
$fullscreen = 0;
$button = '<input type="button" name="'.$name.'" title="'. $title .'" value="'. $linkname .' ..." '.$id.$class.
"onclick=\"return openpopup('$url', '$name', '$options', $fullscreen);\" />\n";
if ($return) {
return $button;
} else {
echo $button;
}
}
/**
* Prints a simple button to close a window
*/
function close_window_button($name='closewindow', $return=false) {
$output = '';
$output .= '<center>' . "\n";
$output .= '<script type="text/javascript">' . "\n";
$output .= '<!--' . "\n";
$output .= "document.write('<form>');\n";
$output .= "document.write('<input type=\"button\" onclick=\"self.close();\" value=\"".get_string("closewindow")."\" />');\n";
$output .= "document.write('<\/form>');\n";
$output .= '-->' . "\n";
$output .= '</script>' . "\n";
$output .= '<noscript>' . "\n";
$output .= get_string($name);
$output .= '</noscript>' . "\n";
$output .= '</center>' . "\n";
if ($return) {
return $output;
} else {
echo $output;
}
}
/*
* Try and close the current window immediately using Javascript
*/
function close_window($delay=0) {
?>
<script type="text/javascript">
<!--
function close_this_window() {
self.close();
}
setTimeout("close_this_window()", <?php echo $delay * 1000 ?>);
-->
</script>
<noscript><center>
<?php print_string('pleaseclose') ?>
</center></noscript>
<?php
die;
}
/**
* Given an array of value, creates a popup menu to be part of a form
* $options["value"]["label"]
*
* @param type description
* @todo Finish documenting this function
*/
function choose_from_menu ($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.'"';
}
$id = str_replace('[]', '', $name); // name may end in [], which would make an invalid id. e.g. numeric question type editing form.
$output = '<select id="menu'.$id.'" name="'. $name .'" '. $attributes .'>' . "\n";
if ($nothing) {
$output .= ' <option value="'. $nothingvalue .'"'. "\n";
if ($nothingvalue === $selected) {
$output .= ' selected="selected"';
}
$output .= '>'. $nothing .'</option>' . "\n";
}
if (!empty($options)) {
foreach ($options as $value => $label) {
$output .= ' <option value="'. $value .'"';
if ((string)$value == (string)$selected) {
$output .= ' selected="selected"';
}
if ($label === '') {
$output .= '>'. $value .'</option>' . "\n";
} else {
$output .= '>'. $label .'</option>' . "\n";
}
}
}
$output .= '</select>' . "\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.
*/
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 = '<select id="menu'.$name.'" name="'. $name .'" '. $attributes .'>' . "\n";
if ($nothing) {
$output .= ' <option value="'. $nothingvalue .'"'. "\n";
if ($nothingvalue === $selected) {
$output .= ' selected="selected"';
}
$output .= '>'. $nothing .'</option>' . "\n";
}
if (!empty($options)) {
foreach ($options as $section => $values) {
$output .= ' <optgroup label="'.$section.'">'."\n";
foreach ($values as $value => $label) {
$output .= ' <option value="'. $value .'"';
if ((string)$value == (string)$selected) {
$output .= ' selected="selected"';
}
if ($label === '') {
$output .= '>'. $value .'</option>' . "\n";
} else {
$output .= '>'. $label .'</option>' . "\n";
}
}
$output .= ' </optgroup>'."\n";
}
}
$output .= '</select>' . "\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 = '<span class="radiogroup '.$name."\">\n";
if (!empty($options)) {
$currentradio = 0;
foreach ($options as $value => $label) {
$htmlid = 'auto-rb'.sprintf('%04d', ++$idcounter);
$output .= ' <span class="radioelement '.$name.' rb'.$currentradio."\">";
$output .= '<input name="'.$name.'" id="'.$htmlid.'" type="radio" value="'.$value.'"';
if ($value == $checked) {
$output .= ' checked="checked"';
}
if ($label === '') {
$output .= ' /> <label for="'.$htmlid.'">'. $value .'</label></span>' . "\n";
} else {
$output .= ' /> <label for="'.$htmlid.'">'. $label .'</label></span>' . "\n";
}
$currentradio = ($currentradio + 1) % 2;
}
}
$output .= '</span>' . "\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 = 'checkbox';
}
if ($checked) {
$strchecked = ' checked="checked"';
} else {
$strchecked = '';
}
$htmlid = 'auto-cb'.sprintf('%04d', ++$idcounter);
$output = '<span class="checkbox '.$name."\">";
$output .= '<input name="'.$name.'" id="'.$htmlid.'" type="checkbox" value="'.$value.'" alt="'.$alt.'"'.$strchecked.' '.((!empty($script)) ? ' onClick="'.$script.'" ' : '').' />';
if(!empty($label)) {
$output .= ' <label for="'.$htmlid.'">'.$label.'</label>';
}
$output .= '</span>'."\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 = '<span class="textfield '.$name."\">";
$output .= '<input name="'.$name.'" id="'.$htmlid.'" type="text" value="'.$value.'" size="'.$size.'" '.$maxlength.' alt="'.$alt.'" />';
$output .= '</span>'."\n";
if (empty($return)) {
echo $output;
} else {
return $output;
}
}
/**
* Implements a complete little popup form
*
* @uses $CFG
* @param string $common The URL up to the point of the variable that changes
* @param array $options Alist of value-label pairs for the popup list
* @param string $formname Name must be unique on the page
* @param string $selected The option that is already 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 text
* 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 TODO, an array?
* @return string If $return is true then the entire form is returned as a string.
* @todo Finish documenting this function<br>
*/
function popup_form($common, $options, $formname, $selected='', $nothing='choose', $help='', $helptext='', $return=false, $targetwindow='self') {
global $CFG;
static $go, $choose; /// Locally cached, in case there's lots on a page
if (empty($options)) {
return '';
}
if (!isset($go)) {
$go = get_string('go');
}
if ($nothing == 'choose') {
if (!isset($choose)) {
$choose = get_string('choose');
}
$nothing = $choose.'...';
}
$startoutput = '<form action="'.$CFG->wwwroot.'/course/jumpto.php"'.
' method="get"'.
' target="'.$CFG->framename.'"'.
' name="'.$formname.'"'.
' class="popupform">';
$output = '<select name="jump" onchange="'.$targetwindow.'.location=document.'.$formname.
'.jump.options[document.'.$formname.'.jump.selectedIndex].value;">'."\n";
if ($nothing != '') {
$output .= " <option value=\"javascript:void(0)\">$nothing</option>\n";
}
$inoptgroup = false;
foreach ($options as $value => $label) {
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 .= ' </optgroup>';
}
unset($optgr);
$optgr = array();
$optgr[] = ' <optgroup label="'. substr($label,2) .'">'; // Plain labels
$inoptgroup = true; /// everything following will be in an optgroup
continue;
} else {
if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()]))
{
$url=sid_process_url( $common . $value );
} else
{
$url=$common . $value;
}
$optstr = ' <option value="' . $url . '"';
if ($value == $selected) {
$optstr .= ' selected="selected"';
}
if ($label) {
$optstr .= '>'. $label .'</option>' . "\n";
} else {
$optstr .= '>'. $value .'</option>' . "\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 .= ' </optgroup>';
}
$output .= '</select>';
$output .= '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
$output .= '<div id="noscript'.$formname.'" style="display: inline;">';
$output .= '<input type="submit" value="'.$go.'" /></div>';
$output .= '<script type="text/javascript">'.
"\n<!--\n".
'document.getElementById("noscript'.$formname.'").style.display = "none";'.
"\n-->\n".'</script>';
$output .= '</form>' . "\n";
if ($help) {
$button = helpbutton($help, $helptext, 'moodle', true, false, '', true);
} else {
$button = '';
}
if ($return) {
return $startoutput.$button.$output;
} else {
echo $startoutput.$button.$output;
}
}
/**
* Prints some red text
*
* @param string $error The text to be displayed in red
*/
function formerr($error) {
if (!empty($error)) {
echo '<font color="#ff0000">'. $error .'</font>';
}
}
/**
* 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
*
* @param string $scriptname name of the calling script
* @return string file path (only safe characters)
*/
function get_file_argument($scriptname) {
global $_SERVER;
$relativepath = FALSE;
// first try normal parameter (compatible method == no relative links!)
$relativepath = optional_param('file', FALSE, PARAM_PATH);
if ($relativepath === '/testslasharguments') {
echo 'test -1 : Incorrect use - try "file.php/testslasharguments" instead'; //indicate fopen/fread works for health center
die;
}
// then try extract file from PATH_INFO (slasharguments method)
if (!$relativepath and !empty($_SERVER['PATH_INFO'])) {
$path_info = $_SERVER['PATH_INFO'];
// check that PATH_INFO works == must not contain the script name
if (!strpos($path_info, $scriptname)) {
$relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
if ($relativepath === '/testslasharguments') {
echo 'test 1 : Slasharguments test passed. Server confguration is compatible with file.php/1/pic.jpg slashargument setting.'; //indicate ok for health center
die;
}
}
}
// now if both fail try the old way
// (for compatibility with misconfigured or older buggy php implementations)
if (!$relativepath) {
$arr = explode($scriptname, me());
if (!empty($arr[1])) {
$path_info = strip_querystring($arr[1]);
$relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
if ($relativepath === '/testslasharguments') {
echo 'test 2 : Slasharguments test passed (compatibility hack). Server confguration may be compatible with file.php/1/pic.jpg slashargument setting'; //indicate ok for health center
die;
}
}
}
return $relativepath;
}
/**
* Check for bad characters ?
*
* @param string $string ?
* @param int $allowdots ?
* @todo Finish documenting this function - more detail needed in description as well as details on arguments
*/
function detect_munged_arguments($string, $allowdots=1) {
if (substr_count($string, '..') > $allowdots) { // Sometimes we allow dots in references
return true;
}
if (ereg('[\|\`]', $string)) { // check for other bad characters
return true;
}
if (empty($string) or $string == '/') {
return true;
}
return false;
}
/**
* Searches the current environment variables for some slash arguments
*
* @param string $file ?
* @todo Finish documenting this function
*/
function get_slash_arguments($file='file.php') {
if (!$string = me()) {
return false;
}
$pathinfo = explode($file, $string);
if (!empty($pathinfo[1])) {
return addslashes($pathinfo[1]);
} else {
return false;
}
}
/**
* Extracts arguments from "/foo/bar/something"
* eg http://mysite.com/script.php/foo/bar/something
*
* @param string $string ?
* @param int $i ?
* @return array|string
* @todo Finish documenting this function
*/
function parse_slash_arguments($string, $i=0) {
if (detect_munged_arguments($string)) {
return false;
}
$args = explode('/', $string);
if ($i) { // return just the required argument
return $args[$i];
} else { // return the whole array
array_shift($args); // get rid of the empty first one
return $args;
}
}
/**
* 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.
*
* @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;
if ($text === '') {
return ''; // no need to do any filters and cleaning
}
if (!isset($options->trusttext)) {
$options->trusttext = false;
}
if (!isset($options->noclean)) {
$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)) {
if (!empty($course->id)) { // An ugly hack for better compatibility
$courseid = $course->id;
}
}
if (!empty($CFG->cachetext) and empty($options->nocache)) {
$time = time() - $CFG->cachetext;
$md5key = md5($text.'-'.(int)$courseid.'-'.current_language().'-'.(int)$format.(int)$options->trusttext.(int)$options->noclean.(int)$options->smiley.(int)$options->filter.(int)$options->para.(int)$options->newlines);
if ($oldcacheitem = get_record_sql('SELECT * FROM '.$CFG->prefix.'cache_text WHERE md5key = \''.$md5key.'\'', true)) {
if ($oldcacheitem->timemodified >= $time) {
return $oldcacheitem->formattedtext;
}
}
}
// trusttext overrides the noclean option!
if ($options->trusttext) {
if (trusttext_present($text)) {
$text = trusttext_strip($text);
if (!empty($CFG->enabletrusttext)) {
$options->noclean = true;
} else {
$options->noclean = false;
}
} else {
$options->noclean = false;
}
} else if (!debugging('', DEBUG_DEVELOPER)) {
// strip any forgotten trusttext in non-developer mode
// do not forget to disable text cache when debugging trusttext!!
$text = trusttext_strip($text);
}
$CFG->currenttextiscacheable = true; // Default status - can be changed by any filter
switch ($format) {
case FORMAT_HTML:
if ($options->smiley) {
replace_smilies($text);
}
if (!$options->noclean) {
$text = clean_text($text, $format);
}
if ($options->filter) {
$text = filter_text($text, $courseid);
}
break;
case FORMAT_PLAIN:
$text = s($text);
$text = rebuildnolinktag($text);
$text = str_replace(' ', '&nbsp; ', $text);
$text = nl2br($text);
break;
case FORMAT_WIKI:
// this format is deprecated
$text = '<p>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.</p>'.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);
}
if ($options->filter) {
$text = filter_text($text, $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);
}
if ($options->filter) {
$text = filter_text($text, $courseid);
}
break;
}
if (empty($options->nocache) and !empty($CFG->cachetext) and $CFG->currenttextiscacheable) {
$newcacheitem = new object();
$newcacheitem->md5key = $md5key;
$newcacheitem->formattedtext = addslashes($text);
$newcacheitem->timemodified = time();
if ($oldcacheitem) { // See bug 4677 for discussion
$newcacheitem->id = $oldcacheitem->id;
@update_record('cache_text', $newcacheitem); // Update existing record in the cache table
// 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 {
@insert_record('cache_text', $newcacheitem); // Insert a new record in the cache table
// 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;
}
/** Given a simple string, this function returns the string
* processed by enabled filters if $CFG->filterall is enabled
*
* @param string $string The string to be filtered.
* @param boolean $striplinks To strip any link in the result text.
* @param int $courseid Current course as filters can, potentially, use it
* @return string
*/
function format_string ($string, $striplinks = false, $courseid=NULL ) {
global $CFG, $COURSE;
//We'll use a in-memory cache here to speed up repeated strings
static $strcache;
//Calculate md5
$md5 = md5($string.'<+>'.$striplinks);
//Fetch from cache if possible
if(isset($strcache[$md5])) {
return $strcache[$md5];
}
if (empty($courseid)) {
if (!empty($COURSE->id)) { // An ugly hack for better compatibility
$courseid = $COURSE->id; // (copied from format_text)
}
}
if (!empty($CFG->filterall)) {
$string = filter_text($string, $courseid);
}
if ($striplinks) { //strip links in string
$string = preg_replace('/(<a[^>]+?>)(.+?)(<\/a>)/is','$2',$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('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\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('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\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 defined in $CFG->textfilterx
* The variable defines a filepath to a file containing the
* filter function. The file must contain a variable called
* $textfilter_function which contains the name of the function
* with $courseid and $text parameters
*
* @param string $text The text to be passed through format filters
* @param int $courseid ?
* @return string
* @todo Finish documenting this function
*/
function filter_text($text, $courseid=NULL) {
global $CFG;
require_once($CFG->libdir.'/filterlib.php');
if (!empty($CFG->textfilters)) {
$textfilters = explode(',', $CFG->textfilters);
foreach ($textfilters as $textfilter) {
if (is_readable($CFG->dirroot .'/'. $textfilter .'/filter.php')) {
include_once($CFG->dirroot .'/'. $textfilter .'/filter.php');
$functionname = basename($textfilter).'_filter';
if (function_exists($functionname)) {
$text = $functionname($courseid, $text);
}
}
}
}
/// <nolink> tags removed for XHTML compatibility
$text = str_replace('<nolink>', '', $text);
$text = str_replace('</nolink>', '', $text);
return $text;
}
/**
* Is the text marked as trusted?
*
* @param string $text text to be searched for TRUSTTEXT marker
* @return boolean
*/
function trusttext_present($text) {
if (strpos($text, TRUSTTEXT) !== FALSE) {
return true;
} else {
return false;
}
}
/**
* This funtion MUST be called before the cleaning or any other
* function that modifies the data! We do not know the origin of trusttext
* in database, if it gets there in tweaked form we must not convert it
* to supported form!!!
*
* Please be carefull not to use stripslashes on data from database
* or twice stripslashes when processing data recieved from user.
*
* @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;
}
}
}
/**
* Mark text as trusted, such text may contain any HTML tags because the
* normal text cleaning will be bypassed.
* Please make sure that the text comes from trusted user before storing
* it into database!
*/
function trusttext_mark($text) {
global $CFG;
if (!empty($CFG->enabletrusttext) and (strpos($text, TRUSTTEXT) === FALSE)) {
return TRUSTTEXT.$text;
} else {
return $text;
}
}
function trusttext_after_edit(&$text, $context) {
if (has_capability('moodle/site:trustcontent', $context)) {
$text = trusttext_strip($text);
$text = trusttext_mark($text);
} else {
$text = trusttext_strip($text);
}
}
function trusttext_prepare_edit(&$text, &$format, $usehtmleditor, $context) {
global $CFG;
$options = new object();
$options->smiley = false;
$options->filter = false;
if (!empty($CFG->enabletrusttext)
and has_capability('moodle/site:trustcontent', $context)
and trusttext_present($text)) {
$options->noclean = true;
} else {
$options->noclean = false;
}
$text = trusttext_strip($text);
if ($usehtmleditor) {
$text = format_text($text, $format, $options);
$format = FORMAT_HTML;
} else if (!$options->noclean){
$text = clean_text($text, $format);
}
}
/**
* 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;
switch ($format) {
case FORMAT_PLAIN:
return $text;
default:
/// Fix non standard entity notations
$text = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $text);
$text = preg_replace('/(&#x[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 script events
$text = eregi_replace("([^a-z])language([[:space:]]*)=", "\\1Xlanguage=", $text);
$text = eregi_replace("([^a-z])on([a-z]+)([[:space:]]*)=", "\\1Xon\\2=", $text);
return $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 '&gt;'; //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('/(&#x[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))[^a-z0-9_\.\-].*$/i', '$1', $arreach['value']);
}
$attStr .= ' '.$arreach['name'].'="'.$arreach['value'].'" ';
}
// Remove last space from attribute list
$attStr = rtrim($attStr);
$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;
/// this builds the mapping array only once
static $runonce = false;
static $e = array();
static $img = array();
static $emoticons = array(
':-)' => 'smiley',
':)' => 'smiley',
':-D' => 'biggrin',
';-)' => 'wink',
':-/' => 'mixed',
'V-.' => 'thoughtful',
':-P' => 'tongueout',
'B-)' => 'cool',
'^-)' => 'approve',
'8-)' => 'wideeyes',
':o)' => 'clown',
':-(' => 'sad',
':(' => 'sad',
'8-.' => 'shy',
':-I' => 'blush',
':-X' => 'kiss',
'8-o' => 'surprise',
'P-|' => 'blackeye',
'8-[' => 'angry',
'xx-P' => 'dead',
'|-.' => 'sleepy',
'}-]' => 'evil',
'(h)' => 'heart',
'(heart)' => 'heart',
'(y)' => 'yes',
'(n)' => 'no',
'(martin)' => 'martin',
'( )' => 'egg'
);
if ($runonce == false) { /// After the first time this is not run again
foreach ($emoticons as $emoticon => $image){
$alttext = get_string($image, 'pix');
$e[] = $emoticon;
$img[] = '<img alt="'. $alttext .'" width="15" height="15" src="'. $CFG->pixpath .'/s/'. $image .'.gif" />';
}
$runonce = true;
}
// Exclude from transformations all the code inside <script> tags
// Needed to solve Bug 1185. Thanks to jouse 2001 detecting it. :-)
// Based on code from glossary fiter by Williams Castillo.
// - Eloy
// Detect all the <script> zones to take out
$excludes = array();
preg_match_all('/<script language(.+?)<\/script>/is',$text,$list_of_excludes);
// Take out all the <script> zones from text
foreach (array_unique($list_of_excludes[0]) as $key=>$value) {
$excludes['<+'.$key.'+>'] = $value;
}
if ($excludes) {
$text = str_replace($excludes,array_keys($excludes),$text);
}
/// this is the meat of the code - this is run every time
$text = str_replace($e, $img, $text);
// Recover all the <script> zones to text
if ($excludes) {
$text = str_replace(array_keys($excludes),$excludes,$text);
}
}
/**
* Given plain text, makes it into HTML as nicely as possible.
* May contain HTML tags already
*
* @uses $CFG
* @param string $text The string to convert.
* @param boolean $smiley Convert any smiley characters to smiley images?
* @param boolean $para If true then the returned string will be wrapped in paragraph tags
* @param boolean $newlines If true then lines newline breaks will be converted to HTML newline breaks.
* @return string
*/
function text_to_html($text, $smiley=true, $para=true, $newlines=true) {
///
global $CFG;
/// Remove any whitespace that may be between HTML tags
$text = eregi_replace(">([[:space:]]+)<", "><", $text);
/// Remove any returns that precede or follow HTML tags
$text = eregi_replace("([\n\r])<", " <", $text);
$text = eregi_replace(">([\n\r])", "> ", $text);
convert_urls_into_links($text);
/// Make returns into HTML newlines.
if ($newlines) {
$text = nl2br($text);
}
/// Turn smileys into images.
if ($smiley) {
replace_smilies($text);
}
/// Wrap the whole thing in a paragraph tag if required
if ($para) {
return '<p>'.$text.'</p>';
} else {
return $text;
}
}
/**
* Given Markdown formatted text, make it into XHTML using external function
*
* @uses $CFG
* @param string $text The markdown formatted text to be converted.
* @return string Converted text
*/
function markdown_to_html($text) {
global $CFG;
require_once($CFG->libdir .'/markdown.php');
return Markdown($text);
}
/**
* Given HTML text, make it into plain text using external function
*
* @uses $CFG
* @param string $html The text to be converted.
* @return string
*/
function html_to_text($html) {
global $CFG;
require_once($CFG->libdir .'/html2text.php');
return html2text($html);
}
/**
* Given some text this function converts any URLs it finds into HTML links
*
* @param string $text Passed in by reference. The string to be searched for urls.
*/
function convert_urls_into_links(&$text) {
/// Make lone URLs into links. eg http://moodle.com/
$text = eregi_replace("([[:space:]]|^|\(|\[)([[:alnum:]]+)://([^[:space:]]*)([[:alnum:]#?/&=])",
"\\1<a href=\"\\2://\\3\\4\" target=\"_blank\">\\2://\\3\\4</a>", $text);
/// eg www.moodle.com
$text = eregi_replace("([[:space:]]|^|\(|\[)www\.([^[:space:]]*)([[:alnum:]#?/&=])",
"\\1<a href=\"http://www.\\2\\3\" target=\"_blank\">www.\\2\\3</a>", $text);
}
/**
* This function will highlight search words in a given string
* It cares about HTML and will not ruin links. It's best to use
* this function after performing any conversions to HTML.
* Function found here: http://forums.devshed.com/t67822/scdaa2d1c3d4bacb4671d075ad41f0854.html
*
* @param string $needle The string to search for
* @param string $haystack The string to search for $needle in
* @param int $case ?
* @return string
* @todo Finish documenting this function
*/
function highlight($needle, $haystack, $case=0,
$left_string='<span class="highlight">', $right_string='</span>') {
if (empty($needle)) {
return $haystack;
}
//$list_of_words = eregi_replace("[^-a-zA-Z0-9&.']", " ", $needle); // bug 3101
$list_of_words = $needle;
$list_array = explode(' ', $list_of_words);
for ($i=0; $i<sizeof($list_array); $i++) {
if (strlen($list_array[$i]) == 1) {
$list_array[$i] = '';
}
}
$list_of_words = implode(' ', $list_array);
$list_of_words_cp = $list_of_words;
$final = array();
preg_match_all('/<(.+?)>/is',$haystack,$list_of_words);
foreach (array_unique($list_of_words[0]) as $key=>$value) {
$final['<|'.$key.'|>'] = $value;
}
$haystack = str_replace($final,array_keys($final),$haystack);
$list_of_words_cp = eregi_replace(' +', '|', $list_of_words_cp);
if ($list_of_words_cp{0}=='|') {
$list_of_words_cp{0} = '';
}
if ($list_of_words_cp{strlen($list_of_words_cp)-1}=='|') {
$list_of_words_cp{strlen($list_of_words_cp)-1}='';
}
$list_of_words_cp = trim($list_of_words_cp);
if ($list_of_words_cp) {
$list_of_words_cp = "(". $list_of_words_cp .")";
if (!$case){
$haystack = eregi_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
} else {
$haystack = ereg_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
}
}
$haystack = str_replace(array_keys($final),$final,$haystack);
return $haystack;
}
/**
* This function will highlight instances of $needle in $haystack
* It's faster that the above function and doesn't care about
* HTML or anything.
*
* @param string $needle The string to search for
* @param string $haystack The string to search for $needle in
* @return string
*/
function highlightfast($needle, $haystack) {
$parts = explode(moodle_strtolower($needle), moodle_strtolower($haystack));
$pos = 0;
foreach ($parts as $key => $part) {
$parts[$key] = substr($haystack, $pos, strlen($part));
$pos += strlen($part);
$parts[$key] .= '<span class="highlight">'.substr($haystack, $pos, strlen($needle)).'</span>';
$pos += strlen($needle);
}
return (join('', $parts));
}
/// STANDARD WEB PAGE PARTS ///////////////////////////////////////////////////
/**
* Print a standard header
*
* @uses $USER
* @uses $CFG
* @uses $SESSION
* @param string $title Appears at the top of the window
* @param string $heading Appears at the top of the page
* @param string $navigation Premade navigation string (for use as breadcrumbs links)
* @param string $focus Indicates form element to get cursor focus on load eg inputform.password
* @param string $meta Meta tags to be added to the header
* @param boolean $cache Should this page be cacheable?
* @param string $button HTML code for a button (usually for module editing)
* @param string $menu HTML code for a popup menu
* @param boolean $usexml use XML for this page
* @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
* @param bool $return If true, return the visible elements of the header instead of echoing them.
*/
function print_header ($title='', $heading='', $navigation='', $focus='',
$meta='', $cache=true, $button='&nbsp;', $menu='',
$usexml=false, $bodytags='', $return=false) {
global $USER, $CFG, $THEME, $SESSION, $ME, $SITE, $HTTPSPAGEREQUIRED;
/// This makes sure that the header is never repeated twice on a page
if (defined('HEADER_PRINTED')) {
debugging('print_header() was called more than once - this should not happen. Please check the code for this page closely. Note: error() and redirect() are now safe to call after print_header().');
return;
}
define('HEADER_PRINTED', 'true');
global $course, $COURSE;
if (!empty($COURSE->lang)) {
$CFG->courselang = $COURSE->lang;
moodle_setlocale();
} else if (!empty($course->lang)) { // ugly backwards compatibility hack
$CFG->courselang = $course->lang;
moodle_setlocale();
}
if (!empty($COURSE->theme)) {
if (!empty($CFG->allowcoursethemes)) {
$CFG->coursetheme = $COURSE->theme;
theme_setup();
}
} else if (!empty($course->theme)) { // ugly backwards compatibility hack
if (!empty($CFG->allowcoursethemes)) {
$CFG->coursetheme = $course->theme;
theme_setup();
}
}
/// We have to change some URLs in styles if we are in a $HTTPSPAGEREQUIRED page
if (!empty($HTTPSPAGEREQUIRED)) {
$CFG->themewww = str_replace('http:', 'https:', $CFG->themewww);
$CFG->pixpath = str_replace('http:', 'https:', $CFG->pixpath);
$CFG->modpixpath = str_replace('http:', 'https:', $CFG->modpixpath);
foreach ($CFG->stylesheets as $key => $stylesheet) {
$CFG->stylesheets[$key] = str_replace('http:', 'https:', $stylesheet);
}
}
/// Add the required stylesheets
$stylesheetshtml = '';
foreach ($CFG->stylesheets as $stylesheet) {
$stylesheetshtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
}
$meta = $stylesheetshtml.$meta;
if ($navigation == 'home') {
$home = true;
$navigation = '';
} else {
$home = false;
}
/// This is another ugly hack to make navigation elements available to print_footer later
$THEME->title = $title;
$THEME->heading = $heading;
$THEME->navigation = $navigation;
$THEME->button = $button;
$THEME->menu = $menu;
$navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
if ($button == '') {
$button = '&nbsp;';
}
if (!$menu and $navigation) {
if (empty($CFG->loginhttps)) {
$wwwroot = $CFG->wwwroot;
} else {
$wwwroot = str_replace('http:','https:',$CFG->wwwroot);
}
if (isset($course->id)) {
$menu = user_login_string($course);
} else {
$menu = user_login_string($SITE);
}
}
if (isset($SESSION->justloggedin)) {
unset($SESSION->justloggedin);
if (!empty($CFG->displayloginfailures)) {
if (!empty($USER->username) and $USER->username != 'guest') {
if ($count = count_login_failures($CFG->displayloginfailures, $USER->username, $USER->lastlogin)) {
$menu .= '&nbsp;<font size="1">';
if (empty($count->accounts)) {
$menu .= get_string('failedloginattempts', '', $count);
} else {
$menu .= get_string('failedloginattemptsall', '', $count);
}
if (has_capability('moodle/site:viewreports', get_context_instance(CONTEXT_SYSTEM, SITEID))) {
$menu .= ' (<a href="'.$CFG->wwwroot.'/course/report/log/index.php'.
'?chooselog=1&amp;id=1&amp;modid=site_errors">'.get_string('logs').'</a>)';
}
$menu .= '</font>';
}
}
}
}
$encoding = current_charset();
$meta = '<meta http-equiv="content-type" content="text/html; charset='. $encoding .'" />'. "\n". $meta ."\n";
if (!$usexml) {
@header('Content-type: text/html; charset='.$encoding);
}
if ( get_string('thisdirection') == 'rtl' ) {
$direction = ' dir="rtl"';
} else {
$direction = ' dir="ltr"';
}
//Accessibility: added the 'lang' attribute to $direction, used in theme <html> tag.
$language = str_replace('_', '-', str_replace('_utf8', '', current_language()));
$direction .= ' lang="'.$language.'" xml:lang="'.$language.'"';
if ($cache) { // Allow caching on "back" (but not on normal clicks)
@header('Cache-Control: private, pre-check=0, post-check=0, max-age=0');
@header('Pragma: no-cache');
@header('Expires: ');
} else { // Do everything we can to always prevent clients and proxies caching
@header('Cache-Control: no-store, no-cache, must-revalidate');
@header('Cache-Control: post-check=0, pre-check=0', false);
@header('Pragma: no-cache');
@header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
@header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
$meta .= "\n<meta http-equiv=\"pragma\" content=\"no-cache\" />";
$meta .= "\n<meta http-equiv=\"expires\" content=\"0\" />";
}
@header('Accept-Ranges: none');
$currentlanguage = current_language();
if ($usexml) { // Added by Gustav Delius / Mad Alex for MathML output
// Modified by Julian Sedding
$mathplayer = preg_match("/MathPlayer/i", $_SERVER['HTTP_USER_AGENT']);
if(!$mathplayer) {
header('Content-Type: application/xhtml+xml');
}
echo '<?xml version="1.0" ?>'."\n";
if (!empty($CFG->xml_stylesheets)) {
$stylesheets = explode(';', $CFG->xml_stylesheets);
foreach ($stylesheets as $stylesheet) {
echo '<?xml-stylesheet type="text/xsl" href="'. $CFG->wwwroot .'/'. $stylesheet .'" ?>' . "\n";
}
}
echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1';
if (!empty($CFG->xml_doctype_extra)) {
echo ' plus '. $CFG->xml_doctype_extra;
}
echo '//' . strtoupper($currentlanguage) . '" "'. $CFG->xml_dtd .'">'."\n";
$direction = " xmlns=\"http://www.w3.org/1999/xhtml\"
xmlns:math=\"http://www.w3.org/1998/Math/MathML\"
xmlns:xlink=\"http://www.w3.org/1999/xlink\"
$direction";
if($mathplayer) {
$meta .= '<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n";
$meta .= '<!--comment required to prevent this becoming an empty tag-->'."\n";
$meta .= '</object>'."\n";
$meta .= '<?import namespace="math" implementation="#mathplayer" ?>' . "\n";
}
}
// Clean up the title
$title = str_replace('"', '&quot;', $title);
$title = strip_tags($title);
// Create class and id for this page
page_id_and_class($pageid, $pageclass);
if (isset($course->id)) {
$pageclass .= ' course-'.$course->id;
} else {
$pageclass .= ' course-'.SITEID;
}
if (!empty($USER->editing)) {
$pageclass .= ' editing';
}
if (!empty($CFG->blocksdrag)) {
$pageclass .= ' drag';
}
$pageclass .= ' lang-'.$currentlanguage;
$bodytags .= ' class="'.$pageclass.'" id="'.$pageid.'"';
ob_start();
include($CFG->header);
$output = ob_get_contents();
ob_end_clean();
if (!empty($CFG->messaging)) {
$output .= message_popup_window();
}
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* This version of print_header is simpler because the course name does not have to be
* provided explicitly in the strings. It can be used on the site page as in courses
* Eventually all print_header could be replaced by print_header_simple
*
* @param string $title Appears at the top of the window
* @param string $heading Appears at the top of the page
* @param string $navigation Premade navigation string (for use as breadcrumbs links)
* @param string $focus Indicates form element to get cursor focus on load eg inputform.password
* @param string $meta Meta tags to be added to the header
* @param boolean $cache Should this page be cacheable?
* @param string $button HTML code for a button (usually for module editing)
* @param string $menu HTML code for a popup menu
* @param boolean $usexml use XML for this page
* @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
* @param bool $return If true, return the visible elements of the header instead of echoing them.
*/
function print_header_simple($title='', $heading='', $navigation='', $focus='', $meta='',
$cache=true, $button='&nbsp;', $menu='', $usexml=false, $bodytags='', $return=false) {
global $COURSE, $CFG;
$shortname ='';
if ($COURSE->category) {
$shortname = '<a href="'.$CFG->wwwroot.'/course/view.php?id='. $COURSE->id .'">'. $COURSE->shortname .'</a> ->';
}
$output = print_header($COURSE->shortname .': '. $title, $COURSE->fullname .' '. $heading, $shortname .' '. $navigation, $focus, $meta,
$cache, $button, $menu, $usexml, $bodytags, true);
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Can provide a course object to make the footer contain a link to
* to the course home page, otherwise the link will go to the site home
*
* @uses $CFG
* @uses $USER
* @param course $course {@link $COURSE} object containing course information
* @param ? $usercourse ?
* @todo Finish documenting this function
*/
function print_footer($course=NULL, $usercourse=NULL, $return=false) {
global $USER, $CFG, $THEME;
/// Course links
if ($course) {
if (is_string($course) && $course == 'none') { // Don't print any links etc
$homelink = '';
$loggedinas = '';
$home = false;
} else if (is_string($course) && $course == 'home') { // special case for site home page - please do not remove
$course = get_site();
$homelink = '<div class="sitelink">'.
'<a title="moodle '. $CFG->release .' ('. $CFG->version .')" href="http://moodle.org/" target="_blank">'.
'<br /><img width="100" height="30" src="pix/moodlelogo.gif" border="0" alt="moodlelogo" /></a></div>';
$home = true;
} else {
$homelink = '<div class="homelink"><a target="'.$CFG->framename.'" href="'.$CFG->wwwroot.
'/course/view.php?id='.$course->id.'">'.$course->shortname.'</a></div>';
$home = false;
}
} else {
$course = get_site(); // Set course as site course by default
$homelink = '<div class="homelink"><a target="'.$CFG->framename.'" href="'.$CFG->wwwroot.'/">'.get_string('home').'</a></div>';
$home = false;
}
/// Set up some other navigation links (passed from print_header by ugly hack)
$menu = isset($THEME->menu) ? str_replace('navmenu', 'navmenufooter', $THEME->menu) : '';
$title = isset($THEME->title) ? $THEME->title : '';
$button = isset($THEME->button) ? $THEME->button : '';
$heading = isset($THEME->heading) ? $THEME->heading : '';
$navigation = isset($THEME->navigation) ? $THEME->navigation : '';
$navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
/// Set the user link if necessary
if (!$usercourse and is_object($course)) {
$usercourse = $course;
}
if (!isset($loggedinas)) {
$loggedinas = user_login_string($usercourse, $USER);
}
if ($loggedinas == $menu) {
$menu = '';
}
/// Provide some performance info if required
$performanceinfo = '';
if (defined('MDL_PERF') || $CFG->perfdebug > 7) {
$perf = get_performance_info();
if (defined('MDL_PERFTOLOG')) {
error_log("PERF: " . $perf['txt']);
}
if (defined('MDL_PERFTOFOOT') || debugging() || $CFG->perfdebug > 7) {
$performanceinfo = $perf['html'];
}
}
/// Include the actual footer file
ob_start();
include($CFG->footer);
$output = ob_get_contents();
ob_end_clean();
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Returns the name of the current theme
*
* @uses $CFG
* @param $USER
* @param $SESSION
* @return string
*/
function current_theme() {
global $CFG, $USER, $SESSION, $course;
if (!empty($CFG->pagetheme)) { // Page theme is for special page-only themes set by code
return $CFG->pagetheme;
} else if (!empty($CFG->coursetheme) and !empty($CFG->allowcoursethemes)) { // Course themes override others
return $CFG->coursetheme;
} else if (!empty($SESSION->theme)) { // Session theme can override other settings
return $SESSION->theme;
} else if (!empty($USER->theme) and !empty($CFG->allowuserthemes)) { // User theme can override site theme
return $USER->theme;
} else {
return $CFG->theme;
}
}
/**
* This function is called by stylesheets to set up the header
* approriately as well as the current path
*
* @uses $CFG
* @param int $lastmodified ?
* @param int $lifetime ?
* @param string $thename ?
*/
function style_sheet_setup($lastmodified=0, $lifetime=300, $themename='', $forceconfig='', $lang='') {
global $CFG, $THEME;
// Fix for IE6 caching - we don't want the filemtime('styles.php'), instead use now.
$lastmodified = time();
header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastmodified) . ' GMT');
header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $lifetime) . ' GMT');
header('Cache-Control: max-age='. $lifetime);
header('Pragma: ');
header('Content-type: text/css'); // Correct MIME type
$DEFAULT_SHEET_LIST = array('styles_layout', 'styles_fonts', 'styles_color');
if (empty($themename)) {
$themename = current_theme(); // So we have something. Normally not needed.
} else {
$themename = clean_param($themename, PARAM_SAFEDIR);
}
if (!empty($forceconfig)) { // Page wants to use the config from this theme instead
unset($THEME);
include($CFG->themedir.'/'.$forceconfig.'/'.'config.php');
}
/// If this is the standard theme calling us, then find out what sheets we need
if ($themename == 'standard') {
if (!isset($THEME->standardsheets) or $THEME->standardsheets === true) { // Use all the sheets we have
$THEME->sheets = $DEFAULT_SHEET_LIST;
} else if (empty($THEME->standardsheets)) { // We can stop right now!
echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
exit;
} else { // Use the provided subset only
$THEME->sheets = $THEME->standardsheets;
}
/// If we are a parent theme, then check for parent definitions
} else if (!empty($THEME->parent) && $themename == $THEME->parent) {
if (!isset($THEME->parentsheets) or $THEME->parentsheets === true) { // Use all the sheets we have
$THEME->sheets = $DEFAULT_SHEET_LIST;
} else if (empty($THEME->parentsheets)) { // We can stop right now!
echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
exit;
} else { // Use the provided subset only
$THEME->sheets = $THEME->parentsheets;
}
}
/// Work out the last modified date for this theme
foreach ($THEME->sheets as $sheet) {
if (file_exists($CFG->themedir.'/'.$themename.'/'.$sheet.'.css')) {
$sheetmodified = filemtime($CFG->themedir.'/'.$themename.'/'.$sheet.'.css');
if ($sheetmodified > $lastmodified) {
$lastmodified = $sheetmodified;
}
}
}
/// Get a list of all the files we want to include
$files = array();
foreach ($THEME->sheets as $sheet) {
$files[] = array($CFG->themedir, $themename.'/'.$sheet.'.css');
}
if ($themename == 'standard') { // Add any standard styles included in any modules
if (!empty($THEME->modsheets)) { // Search for styles.php within activity modules
if ($mods = get_list_of_plugins('mod')) {
foreach ($mods as $mod) {
if (file_exists($CFG->dirroot.'/mod/'.$mod.'/styles.php')) {
$files[] = array($CFG->dirroot, '/mod/'.$mod.'/styles.php');
}
}
}
}
if (!empty($THEME->blocksheets)) { // Search for styles.php within block modules
if ($mods = get_list_of_plugins('blocks')) {
foreach ($mods as $mod) {
if (file_exists($CFG->dirroot.'/blocks/'.$mod.'/styles.php')) {
$files[] = array($CFG->dirroot, '/blocks/'.$mod.'/styles.php');
}
}
}
}
if (!empty($THEME->langsheets)) { // Search for styles.php within the current language
if (file_exists($CFG->dirroot.'/lang/'.$lang.'/styles.php')) {
$files[] = array($CFG->dirroot, '/lang/'.$lang.'/styles.php');
}
}
}
if ($files) {
/// Produce a list of all the files first
echo '/**************************************'."\n";
echo ' * THEME NAME: '.$themename."\n *\n";
echo ' * Files included in this sheet:'."\n *\n";
foreach ($files as $file) {
echo ' * '.$file[1]."\n";
}
echo ' **************************************/'."\n\n";
/// Actually output all the files in order.
foreach ($files as $file) {
echo '/***** '.$file[1].' start *****/'."\n\n";
@include_once($file[0].'/'.$file[1]);
echo '/***** '.$file[1].' end *****/'."\n\n";
}
}
return $CFG->themewww.'/'.$themename; // Only to help old themes (1.4 and earlier)
}
function theme_setup($theme = '', $params=NULL) {
/// Sets up global variables related to themes
global $CFG, $THEME, $SESSION, $USER;
if (empty($theme)) {
$theme = current_theme();
}
/// If the theme doesn't exist for some reason then revert to standardwhite
if (!file_exists($CFG->themedir .'/'. $theme .'/config.php')) {
$CFG->theme = $theme = 'standardwhite';
}
/// Load up the theme config
$THEME = NULL; // Just to be sure
include($CFG->themedir .'/'. $theme .'/config.php'); // Main config for current theme
/// Put together the parameters
if (!$params) {
$params = array();
}
if ($theme != $CFG->theme) {
$params[] = 'forceconfig='.$theme;
}
/// Force language too if required
if (!empty($THEME->langsheets)) {
$params[] = 'lang='.current_language();
}
/// Convert params to string
if ($params) {
$paramstring = '?'.implode('&', $params);
} else {
$paramstring = '';
}
/// Set up image paths
if (empty($THEME->custompix)) { // Could be set in the above file
$CFG->pixpath = $CFG->wwwroot .'/pix';
$CFG->modpixpath = $CFG->wwwroot .'/mod';
} else {
$CFG->pixpath = $CFG->themewww .'/'. $theme .'/pix';
$CFG->modpixpath = $CFG->themewww .'/'. $theme .'/pix/mod';
}
/// Header and footer paths
$CFG->header = $CFG->themedir .'/'. $theme .'/header.html';
$CFG->footer = $CFG->themedir .'/'. $theme .'/footer.html';
/// Define stylesheet loading order
$CFG->stylesheets = array();
if ($theme != 'standard') { /// The standard sheet is always loaded first
$CFG->stylesheets[] = $CFG->themewww.'/standard/styles.php'.$paramstring;
}
if (!empty($THEME->parent)) { /// Parent stylesheets are loaded next
$CFG->stylesheets[] = $CFG->themewww.'/'.$THEME->parent.'/styles.php'.$paramstring;
}
$CFG->stylesheets[] = $CFG->themewww.'/'.$theme.'/styles.php'.$paramstring;
}
/**
* Returns text to be displayed to the user which reflects their login status
*
* @uses $CFG
* @uses $USER
* @param course $course {@link $COURSE} object containing course information
* @param user $user {@link $USER} object containing user information
* @return string
*/
function user_login_string($course=NULL, $user=NULL) {
global $USER, $CFG, $SITE;
if (empty($user) and !empty($USER->id)) {
$user = $USER;
}
if (empty($course)) {
$course = $SITE;
}
if (isset($user->realuser)) {
if ($realuser = get_record('user', 'id', $user->realuser)) {
$fullname = fullname($realuser, true);
$realuserinfo = " [<a target=\"{$CFG->framename}\"
href=\"$CFG->wwwroot/course/loginas.php?id=$course->id&amp;return=1\">$fullname</a>] ";
}
} else {
$realuserinfo = '';
}
if (empty($CFG->loginhttps)) {
$wwwroot = $CFG->wwwroot;
} else {
$wwwroot = str_replace('http:','https:',$CFG->wwwroot);
}
if (empty($course->id)) {
// $course->id is not defined during installation
return '';
} else if (isset($user->id) and $user->id) {
$context = get_context_instance(CONTEXT_COURSE, $course->id);
$fullname = fullname($user, true);
$username = "<a target=\"{$CFG->framename}\" href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a>";
if (isset($user->username) && $user->username == 'guest') {
$loggedinas = $realuserinfo.get_string('loggedinasguest').
" (<a target=\"{$CFG->framename}\" href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
} else if (!empty($user->switchrole[$context->id])) {
$rolename = '';
if ($role = get_record('role', 'id', $user->switchrole[$context->id])) {
$rolename = ': '.format_string($role->name);
}
$loggedinas = get_string('loggedinas', 'moodle', $username).$rolename.
" (<a target=\"{$CFG->framename}\"
href=\"$CFG->wwwroot/course/view.php?id=$course->id&amp;switchrole=0&amp;sesskey=".sesskey()."\">".get_string('switchrolereturn').'</a>)';
} else {
$loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username).' '.
" (<a target=\"{$CFG->framename}\" href=\"$CFG->wwwroot/login/logout.php?sesskey=".sesskey()."\">".get_string('logout').'</a>)';
}
} else {
$loggedinas = get_string('loggedinnot', 'moodle').
" (<a target=\"{$CFG->framename}\" href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
}
return '<div class="logininfo">'.$loggedinas.'</div>';
}
/**
* Tests whether $THEME->rarrow, $THEME->larrow have been set (theme/-/config.php).
* If not it applies sensible defaults.
*
* Accessibility: right and left arrow Unicode characters for breadcrumb, calendar,
* search forum block, etc. Important: these are 'silent' in a screen-reader
* (unlike &gt; &raquo;), and must be accompanied by text.
* @uses $THEME
*/
function check_theme_arrows() {
global $THEME;
if (!isset($THEME->rarrow) and !isset($THEME->larrow)) {
// Default, looks good in Win XP/IE 6, Win/Firefox 1.5, Win/Netscape 8...
// Also OK in Win 9x/2K/IE 5.x
$THEME->rarrow = '&#x25BA;';
$THEME->larrow = '&#x25C4;';
$uagent = $_SERVER['HTTP_USER_AGENT'];
if (false !== strpos($uagent, 'Opera')
|| false !== strpos($uagent, 'Mac')) {
// Looks good in Win XP/Mac/Opera 8/9, Mac/Firefox 2, Camino, Safari.
// Not broken in Mac/IE 5, Mac/Netscape 7 (?).
$THEME->rarrow = '&#x25B6;';
$THEME->larrow = '&#x25C0;';
}
elseif (false !== strpos($uagent, 'Konqueror')) {
$THEME->rarrow = '&rarr;';
$THEME->larrow = '&larr;';
}
elseif (isset($_SERVER['HTTP_ACCEPT_CHARSET'])
&& false === stripos($_SERVER['HTTP_ACCEPT_CHARSET'], 'utf-8')) {
// (Win/IE 5 doesn't set ACCEPT_CHARSET, but handles Unicode.)
// To be safe, non-Unicode browsers!
$THEME->rarrow = '&gt;';
$THEME->larrow = '&lt;';
}
}
}
/**
* Return a HTML element with the class "accesshide", for accessibility.
* Please use cautiously - where possible, text should be visible!
* @param string $text Plain text.
* @param string $elem Lowercase element name, default "span".
* @param string $class Additional classes for the element.
* @param string $attrs Additional attributes string in the form, "name='value' name2='value2'"
* @return string HTML string.
*/
function get_accesshide($text, $elem='span', $class='', $attrs='') {
return "<$elem class=\"accesshide $class\" $attrs>$text</$elem>";
}
/**
* Prints breadcrumb trail of links, called in theme/-/header.html
*
* @uses $CFG
* @param string $navigation The breadcrumb navigation string to be printed
* @param string $separator The breadcrumb trail separator. The default 0 leads to the use
* of $THEME->rarrow, themes could use '&rarr;', '/', or '' for a style-sheet solution.
* @param boolean $return False to echo the breadcrumb string (default), true to return it.
*/
function print_navigation ($navigation, $separator=0, $return=false) {
global $CFG, $THEME;
$output = '';
check_theme_arrows();
if (0 === $separator) {
$separator = $THEME->rarrow;
}
if (!empty($separator)) {
$separator = '<span class="sep">'. $separator .'</span>';
}
if ($navigation) {
//Accessibility: breadcrumb links now in a list, &raquo; replaced with a 'silent' character.
$output .= get_accesshide(get_string('youarehere','access'), 'h2')."<ul>\n";
if (! $site = get_site()) {
$site->shortname = get_string('home');
}
$navigation = "<li>$separator ". str_replace('->', "</li>\n<li>$separator", $navigation) ."</li>\n";
$output .= '<li class="first"><a target="'. $CFG->framename .'" href="'. $CFG->wwwroot.((!has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM, SITEID)) && !empty($USER->id) && !empty($CFG->mymoodleredirect) && !isguest())
? '/my' : '') .'/">'. $site->shortname ."</a></li>\n". $navigation;
$output .= "</ul>\n";
}
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Prints a string in a specified size (retained for backward compatibility)
*
* @param string $text The text to be displayed
* @param int $size The size to set the font for text display.
*/
function print_headline($text, $size=2, $return=false) {
$output = print_heading($text, 'left', $size, true);
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Prints text in a format for use in headings.
*
* @param string $text The text to be displayed
* @param string $align The alignment of the printed paragraph of text
* @param int $size The size to set the font for text display.
*/
function print_heading($text, $align='', $size=2, $class='main', $return=false) {
if ($align) {
$align = ' align="'.$align.'"';
}
if ($class) {
$class = ' class="'.$class.'"';
}
$output = "<h$size $align $class>".stripslashes_safe($text)."</h$size>";
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Centered heading with attached help button (same title text)
* and optional icon attached
*
* @param string $text The text to be displayed
* @param string $helppage The help page to link to
* @param string $module The module whose help should be linked to
* @param string $icon Image to display if needed
*/
function print_heading_with_help($text, $helppage, $module='moodle', $icon='', $return=false) {
$output = '';
$output .= '<h2 class="main help">'.$icon.stripslashes_safe($text);
$output .= helpbutton($helppage, $text, $module, true, false, '', true);
$output .= '</h2>';
if ($return) {
return $output;
} else {
echo $output;
}
}
function print_heading_block($heading, $class='', $return=false) {
//Accessibility: 'headingblock' is now H1, see theme/standard/styles_*.css: ??
$output = '<h2 class="headingblock header '.$class.'">'.stripslashes($heading).'</h2>';
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Print a link to continue on to another page.
*
* @uses $CFG
* @param string $link The url to create a link to.
*/
function print_continue($link, $return=false) {
global $CFG;
// in case we are logging upgrade in admin/index.php stop it
if (function_exists('upgrade_log_finish')) {
upgrade_log_finish();
}
$output = '';
if (!$link) {
$link = $_SERVER['HTTP_REFERER'];
}
$output .= '<div class="continuebutton">';
$output .= print_single_button($link, NULL, get_string('continue'), 'post'