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 =
'
';
return implode("\n", $menu);
}
/**
* Prints form items with the names $day, $month and $year
*
* @param string $day fieldname
* @param string $month fieldname
* @param string $year fieldname
* @param int $currenttime A default timestamp in GMT
* @param boolean $return
*/
function print_date_selector($day, $month, $year, $currenttime=0, $return=false) {
if (!$currenttime) {
$currenttime = time();
}
$currentdate = usergetdate($currenttime);
for ($i=1; $i<=31; $i++) {
$days[$i] = $i;
}
for ($i=1; $i<=12; $i++) {
$months[$i] = userdate(gmmktime(12,0,0,$i,1,2000), "%B");
}
for ($i=1970; $i<=2020; $i++) {
$years[$i] = $i;
}
return choose_from_menu($days, $day, $currentdate['mday'], '', '', '0', $return)
.choose_from_menu($months, $month, $currentdate['mon'], '', '', '0', $return)
.choose_from_menu($years, $year, $currentdate['year'], '', '', '0', $return);
}
/**
*Prints form items with the names $hour and $minute
*
* @param string $hour fieldname
* @param string ? $minute fieldname
* @param $currenttime A default timestamp in GMT
* @param int $step minute spacing
* @param boolean $return
*/
function print_time_selector($hour, $minute, $currenttime=0, $step=5, $return=false) {
if (!$currenttime) {
$currenttime = time();
}
$currentdate = usergetdate($currenttime);
if ($step != 1) {
$currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
}
for ($i=0; $i<=23; $i++) {
$hours[$i] = sprintf("%02d",$i);
}
for ($i=0; $i<=59; $i+=$step) {
$minutes[$i] = sprintf("%02d",$i);
}
return choose_from_menu($hours, $hour, $currentdate['hours'], '','','0',$return)
.choose_from_menu($minutes, $minute, $currentdate['minutes'], '','','0',$return);
}
/**
* Prints time limit value selector
*
* @uses $CFG
* @param int $timelimit default
* @param string $unit
* @param string $name
* @param boolean $return
*/
function print_timer_selector($timelimit = 0, $unit = '', $name = 'timelimit', $return=false) {
global $CFG;
if ($unit) {
$unit = ' '.$unit;
}
// Max timelimit is sessiontimeout - 10 minutes.
$maxvalue = ($CFG->sessiontimeout / 60) - 10;
for ($i=1; $i<=$maxvalue; $i++) {
$minutes[$i] = $i.$unit;
}
return choose_from_menu($minutes, $name, $timelimit, get_string('none'), '','','0',$return);
}
/**
* Prints a grade menu (as part of an existing form) with help
* Showing all possible numerical grades and scales
*
* @uses $CFG
* @param int $courseid ?
* @param string $name ?
* @param string $current ?
* @param boolean $includenograde ?
* @todo Finish documenting this function
*/
function print_grade_menu($courseid, $name, $current, $includenograde=true, $return=false) {
global $CFG;
$output = '';
$strscale = get_string('scale');
$strscales = get_string('scales');
$scales = get_scales_menu($courseid);
foreach ($scales as $i => $scalename) {
$grades[-$i] = $strscale .': '. $scalename;
}
if ($includenograde) {
$grades[0] = get_string('nograde');
}
for ($i=100; $i>=1; $i--) {
$grades[$i] = $i;
}
$output .= choose_from_menu($grades, $name, $current, '', '', 0, true);
$linkobject = '';
$output .= link_to_popup_window ('/course/scales.php?id='. $courseid .'&list=true', 'ratingscales',
$linkobject, 400, 500, $strscales, 'none', true);
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Prints a scale menu (as part of an existing form) including help button
* Just like {@link print_grade_menu()} but without the numeric grades
*
* @param int $courseid ?
* @param string $name ?
* @param string $current ?
* @todo Finish documenting this function
*/
function print_scale_menu($courseid, $name, $current, $return=false) {
global $CFG;
$output = '';
$strscales = get_string('scales');
$output .= choose_from_menu(get_scales_menu($courseid), $name, $current, '', '', 0, true);
$linkobject = '';
$output .= link_to_popup_window ('/course/scales.php?id='. $courseid .'&list=true', 'ratingscales',
$linkobject, 400, 500, $strscales, 'none', true);
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Prints a help button about a scale
*
* @uses $CFG
* @param id $courseid ?
* @param object $scale ?
* @todo Finish documenting this function
*/
function print_scale_menu_helpbutton($courseid, $scale, $return=false) {
global $CFG;
$output = '';
$strscales = get_string('scales');
$linkobject = '';
$output .= link_to_popup_window ('/course/scales.php?id='. $courseid .'&list=true&scaleid='. $scale->id, 'ratingscale',
$linkobject, 400, 500, $scale->name, 'none', true);
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Print an error page displaying an error message.
* Old method, don't call directly in new code - use print_error instead.
*
*
* @uses $SESSION
* @uses $CFG
* @param string $message The message to display to the user about the error.
* @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page.
*/
function error ($message, $link='') {
global $CFG, $SESSION;
$message = clean_text($message); // In case nasties are in here
if (defined('FULLME') && FULLME == 'cron') {
// Errors in cron should be mtrace'd.
mtrace($message);
die;
}
if (! defined('HEADER_PRINTED')) {
//header not yet printed
@header('HTTP/1.0 404 Not Found');
print_header(get_string('error'));
}
echo ' ';
print_simple_box($message, '', '', '', '', 'errorbox');
// in case we are logging upgrade in admin/index.php stop it
if (function_exists('upgrade_log_finish')) {
upgrade_log_finish();
}
if (!$link) {
if ( !empty($SESSION->fromurl) ) {
$link = $SESSION->fromurl;
unset($SESSION->fromurl);
} else {
$link = $CFG->wwwroot .'/';
}
}
print_continue($link);
print_footer();
for ($i=0;$i<512;$i++) { // Padding to help IE work with 404
echo ' ';
}
die;
}
/**
* Print an error page displaying an error message. New method - use this for new code.
*
* @uses $SESSION
* @uses $CFG
* @param string $errorcode The name of the string from error.php to print
* @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page.
* @param object $a Extra words and phrases that might be required in the error string
*/
function print_error ($errorcode, $module='', $link='', $a=NULL) {
global $CFG;
if (empty($module) || $module == 'moodle' || $module == 'core') {
$module = 'error';
$modulelink = 'moodle';
} else {
$modulelink = $module;
}
if (!empty($CFG->errordocroot)) {
$errordocroot = $CFG->errordocroot;
} else if (!empty($CFG->docroot)) {
$errordocroot = $CFG->docroot;
} else {
$errordocroot = 'http://docs.moodle.org';
}
$message = '
';
error($message, $link);
}
/**
* Print a help button.
*
* @uses $CFG
* @param string $page The keyword that defines a help page
* @param string $title The title of links, rollover tips, alt tags etc
* 'Help with' (or the language equivalent) will be prefixed and '...' will be stripped.
* @param string $module Which module is the page defined in
* @param mixed $image Use a help image for the link? (true/false/"both")
* @param boolean $linktext If true, display the title next to the help icon.
* @param string $text If defined then this text is used in the page, and
* the $page variable is ignored.
* @param boolean $return If true then the output is returned as a string, if false it is printed to the current page.
* @param string $imagetext The full text for the helpbutton icon. If empty use default help.gif
* @return string
* @todo Finish documenting this function
*/
function helpbutton ($page, $title='', $module='moodle', $image=true, $linktext=false, $text='', $return=false,
$imagetext='') {
global $CFG;
if ($module == '') {
$module = 'moodle';
}
//Accessibility: prefix the alt text/title with 'Help with', strip distracting dots '...'
// PLEASE DO NOT CHANGE. ('...' is VERY distracting for non-visual users)
$tooltip = get_string('helpprefix', '', trim($title, ". \t"));
$linkobject = '';
if ($image) {
if ($linktext) {
$linkobject .= $title.' ';
}
if ($imagetext) {
$linkobject .= $imagetext;
} else {
$linkobject .= '';
}
} else {
$linkobject .= $tooltip;
}
if ($text) {
$url = '/help.php?module='. $module .'&text='. s(urlencode($text));
} else {
$url = '/help.php?module='. $module .'&file='. $page .'.html';
}
$link = ''.
link_to_popup_window ($url, 'popup', $linkobject, 400, 500, $tooltip, 'none', true).
'';
if ($return) {
return $link;
} else {
echo $link;
}
}
/**
* Print a help button.
*
* Prints a special help button that is a link to the "live" emoticon popup
* @uses $CFG
* @uses $SESSION
* @param string $form ?
* @param string $field ?
* @todo Finish documenting this function
*/
function emoticonhelpbutton($form, $field) {
global $CFG, $SESSION;
$SESSION->inserttextform = $form;
$SESSION->inserttextfield = $field;
$imagetext = '';
helpbutton('emoticons', get_string('helpemoticons'), 'moodle', true, true, '', false, $imagetext);
}
/**
* Print a message and exit.
*
* @uses $CFG
* @param string $message ?
* @param string $link ?
* @todo Finish documenting this function
*/
function notice ($message, $link='', $course=NULL) {
global $CFG, $SITE;
$message = clean_text($message);
$link = clean_text($link);
if (!$link) {
if (!empty($_SERVER['HTTP_REFERER'])) {
$link = $_SERVER['HTTP_REFERER'];
} else {
$link = $CFG->wwwroot .'/';
}
}
echo ' ';
print_simple_box($message, 'center', '50%', '', '20', 'generalbox', 'notice');
print_continue($link);
if (empty($course)) {
print_footer($SITE);
} else {
print_footer($course);
}
exit;
}
/**
* Print a message along with "Yes" and "No" links for the user to continue.
*
* @param string $message The text to display
* @param string $linkyes The link to take the user to if they choose "Yes"
* @param string $linkno The link to take the user to if they choose "No"
*/
function notice_yesno ($message, $linkyes, $linkno, $optionsyes=NULL, $optionsno=NULL, $methodyes='post', $methodno='post') {
global $CFG;
$message = clean_text($message);
$linkyes = clean_text($linkyes);
$linkno = clean_text($linkno);
print_simple_box_start('center', '60%', '', 5, 'generalbox', 'notice');
echo '
';
print_simple_box_end();
}
/**
* Redirects the user to another page, after printing a notice
*
* @param string $url The url to take the user to
* @param string $message The text message to display to the user about the redirect, if any
* @param string $delay How long before refreshing to the new page at $url?
* @todo '&' needs to be encoded into '&' for XHTML compliance,
* however, this is not true for javascript. Therefore we
* first decode all entities in $url (since we cannot rely on)
* the correct input) and then encode for where it's needed
* echo "";
*/
function redirect($url, $message='', $delay=-1) {
global $CFG;
if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()])) {
$url = sid_process_url($url);
}
$message = clean_text($message);
$url = html_entity_decode($url);
$url = str_replace(array("\n", "\r"), '', $url); // some more cleaning
$encodedurl = htmlentities($url);
$tmpstr = clean_text(''); //clean encoded URL
$encodedurl = substr($tmpstr, 9, strlen($tmpstr)-13);
$url = html_entity_decode($encodedurl);
$surl = addslashes($url);
/// when no message and header printed yet, try to redirect
if (empty($message) and !defined('HEADER_PRINTED')) {
// 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('HTTP/1.x 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 '';
echo ''; // To cope with Mozilla bug
die;
}
if ($delay == -1) {
$delay = 3; // if no delay specified wait 3 seconds
}
if (! defined('HEADER_PRINTED')) {
// this type of redirect might not be working in some browsers - such as lynx :-(
print_header('', '', '', '', '');
$delay += 3; // double redirect prevention, it was sometimes breaking upgrades before 1.7
}
echo '
';
// it might be better not to set timeout the same for both types of redirect, so that we can be sure which one wins
?>
'. $message .''." \n";
}
/**
* 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 string $baseurl The url which will be used to create page numbered links. Each page will consist of the base url appended by the page
var an equal sign, then the page number.
* @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 .= '
';
}
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 Block $title embedded in HTML tags, for example
.
* @param string $content ?
* @param array $list ?
* @param array $icons ?
* @param string $footer ?
* @param array $attributes ?
* @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)) {
$heading = $skip_link . $heading;
}
/*else { //ELSE: I think a single link on a page, "Skip block 4" is too confusing - don't print.
echo $skip_link;
}*/
print_side_block_start($heading, $attributes);
if ($content) {
echo $content;
if ($footer) {
echo '';
}
} else {
if ($list) {
$row = 0;
//Accessibility: replaced unnecessary table with list, see themes/standard/styles_layout.css
echo "\n
\n";
}
if ($footer) {
echo '';
}
}
print_side_block_end($attributes);
echo $skip_dest;
}
/**
* Starts a nice side block with an optional header.
*
* @param string $heading ?
* @param array $attributes ?
* @todo Finish documenting this function
*/
function print_side_block_start($heading='', $attributes = array()) {
global $CFG;
// 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 ($heading) {
//Accessibility: replaced
with H2; no, H2 more appropriate in moodleblock.class.php: _title_html.
echo '
'.$heading.'
';
}
echo '
';
}
/**
* Print table ending tags for a side block box.
*/
function print_side_block_end($attributes = array()) {
global $CFG;
echo '
';
// IE workaround: if I do it THIS way, it works! WTF?
if (!empty($CFG->allowuserblockhiding) && isset($attributes['id'])) {
echo '';
}
}
/**
* Prints out code needed for spellchecking.
* Original idea by Ludo (Marc Alier).
*
* @uses $CFG
* @param boolean $usehtmleditor ?
* @todo Finish documenting this function
*/
function print_speller_code ($usehtmleditor=false, $return=false) {
global $CFG;
$str = '';
if(!$usehtmleditor) {
$str .= "\n".''."\n";
} else {
$str .= "\nfunction spellClickHandler(editor, buttonId) {\n";
$str .= "\teditor._textArea.value = editor.getHTML();\n";
$str .= "\tvar speller = new spellChecker( editor._textArea );\n";
$str .= "\tspeller.popUpUrl = \"" . $CFG->wwwroot ."/lib/speller/spellchecker.html\";\n";
$str .= "\tspeller.spellCheckScript = \"". $CFG->wwwroot ."/lib/speller/server-scripts/spellchecker.php\";\n";
$str .= "\tspeller._moogle_edit=1;\n";
$str .= "\tspeller._editor=editor;\n";
$str .= "\tspeller.openChecker();\n";
$str .= '}'."\n";
}
if ($return) {
return $str;
}
echo $str;
}
/**
* Print button for spellchecking when editor is disabled
*/
function print_speller_button () {
echo ''."\n";
}
function page_id_and_class(&$getid, &$getclass) {
// Create class and id for this page
global $CFG, $ME;
static $class = NULL;
static $id = NULL;
if (empty($CFG->pagepath)) {
$CFG->pagepath = $ME;
}
if (empty($class) || empty($id)) {
$path = str_replace($CFG->httpswwwroot.'/', '', $CFG->pagepath); //Because the page could be HTTPSPAGEREQUIRED
$path = str_replace('.php', '', $path);
if (substr($path, -1) == '/') {
$path .= 'index';
}
if (empty($path) || $path == 'index') {
$id = 'site-index';
$class = 'course';
} else if (substr($path, 0, 5) == 'admin') {
$id = str_replace('/', '-', $path);
$class = 'admin';
} else {
$id = str_replace('/', '-', $path);
$class = explode('-', $id);
array_pop($class);
$class = implode('-', $class);
}
}
$getid = $id;
$getclass = $class;
}
/**
* Prints a maintenance message from /maintenance.html
*/
function print_maintenance_message () {
global $CFG, $SITE;
print_header(strip_tags($SITE->fullname), $SITE->fullname, 'home');
print_simple_box_start('center');
print_heading(get_string('sitemaintenance', 'admin'));
@include($CFG->dataroot.'/1/maintenance.html');
print_simple_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 .= '