libdir/filterlib.php");
require_once("$CFG->libdir/ajax/ajaxlib.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#####');
/**
* Javascript related defines
*/
define('REQUIREJS_BEFOREHEADER', 0);
define('REQUIREJS_INHEADER', 1);
define('REQUIREJS_AFTERHEADER', 2);
/**
* Allowed tags - string of html tags that can be tested against for safe html tags
* @global string $ALLOWED_TAGS
*/
global $ALLOWED_TAGS;
$ALLOWED_TAGS =
'
') === false) {
$navigation[] = array('title' => $a, 'url' => '');
} else {
if (preg_match('/(.*)<\/a>/', $a, $matches)) {
$navigation[] = array('title' => $matches[2], 'url' => $matches[1]);
}
}
}
}
if (! $site = get_site()) {
$site = new object();
$site->shortname = get_string('home');
}
//Accessibility: breadcrumb links now in a list, » replaced with a 'silent' character.
$output .= get_accesshide(get_string('youarehere','access'), 'h2')."
\n";
}
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* This function will build the navigation string to be used by print_header
* and others.
*
* It automatically generates the site and course level (if appropriate) links.
*
* If you pass in a $cm object, the method will also generate the activity (e.g. 'Forums')
* and activityinstances (e.g. 'General Developer Forum') navigation levels.
*
* If you want to add any further navigation links after the ones this function generates,
* the pass an array of extra link arrays like this:
* array(
* array('name' => $linktext1, 'link' => $url1, 'type' => $linktype1),
* array('name' => $linktext2, 'link' => $url2, 'type' => $linktype2)
* )
* The normal case is to just add one further link, for example 'Editing forum' after
* 'General Developer Forum', with no link.
* To do that, you need to pass
* array(array('name' => $linktext, 'link' => '', 'type' => 'title'))
* However, becuase this is a very common case, you can use a shortcut syntax, and just
* pass the string 'Editing forum', instead of an array as $extranavlinks.
*
* At the moment, the link types only have limited significance. Type 'activity' is
* recognised in order to implement the $CFG->hideactivitytypenavlink feature. Types
* that are known to appear are 'home', 'course', 'activity', 'activityinstance' and 'title'.
* This really needs to be documented better. In the mean time, try to be consistent, it will
* enable people to customise the navigation more in future.
*
* When passing a $cm object, the fields used are $cm->modname, $cm->name and $cm->course.
* If you get the $cm object using the function get_coursemodule_from_instance or
* get_coursemodule_from_id (as recommended) then this will be done for you automatically.
* If you don't have $cm->modname or $cm->name, this fuction will attempt to find them using
* the $cm->module and $cm->instance fields, but this takes extra database queries, so a
* warning is printed in developer debug mode.
*
* @uses $CFG
* @uses $THEME
*
* @param mixed $extranavlinks - Normally an array of arrays, keys: name, link, type. If you
* only want one extra item with no link, you can pass a string instead. If you don't want
* any extra links, pass an empty string.
* @param mixed $cm - optionally the $cm object, if you want this function to generate the
* activity and activityinstance levels of navigation too.
*
* @return $navigation as an object so it can be differentiated from old style
* navigation strings.
*/
function build_navigation($extranavlinks, $cm = null) {
global $CFG, $COURSE;
if (is_string($extranavlinks)) {
if ($extranavlinks == '') {
$extranavlinks = array();
} else {
$extranavlinks = array(array('name' => $extranavlinks, 'link' => '', 'type' => 'title'));
}
}
$navlinks = array();
//Site name
if ($site = get_site()) {
$navlinks[] = array(
'name' => format_string($site->shortname),
'link' => "$CFG->wwwroot/",
'type' => 'home');
}
// Course name, if appropriate.
if (isset($COURSE) && $COURSE->id != SITEID) {
$navlinks[] = array(
'name' => format_string($COURSE->shortname),
'link' => "$CFG->wwwroot/course/view.php?id=$COURSE->id",
'type' => 'course');
}
// Activity type and instance, if appropriate.
if (is_object($cm)) {
if (!isset($cm->modname)) {
debugging('The field $cm->modname should be set if you call build_navigation with '.
'a $cm parameter. If you get $cm using get_coursemodule_from_instance or '.
'get_coursemodule_from_id, this will be done automatically.', DEBUG_DEVELOPER);
if (!$cm->modname = get_field('modules', 'name', 'id', $cm->module)) {
error('Cannot get the module type in build navigation.');
}
}
if (!isset($cm->name)) {
debugging('The field $cm->name should be set if you call build_navigation with '.
'a $cm parameter. If you get $cm using get_coursemodule_from_instance or '.
'get_coursemodule_from_id, this will be done automatically.', DEBUG_DEVELOPER);
if (!$cm->name = get_field($cm->modname, 'name', 'id', $cm->instance)) {
error('Cannot get the module name in build navigation.');
}
}
$navlinks[] = array(
'name' => get_string('modulenameplural', $cm->modname),
'link' => $CFG->wwwroot . '/mod/' . $cm->modname . '/index.php?id=' . $cm->course,
'type' => 'activity');
$navlinks[] = array(
'name' => format_string($cm->name),
'link' => $CFG->wwwroot . '/mod/' . $cm->modname . '/view.php?id=' . $cm->id,
'type' => 'activityinstance');
}
//Merge in extra navigation links
$navlinks = array_merge($navlinks, $extranavlinks);
// Work out whether we should be showing the activity (e.g. Forums) link.
// Note: build_navigation() is called from many places --
// install & upgrade for example -- where we cannot count on the
// roles infrastructure to be defined. Hence the $CFG->rolesactive check.
if (!isset($CFG->hideactivitytypenavlink)) {
$CFG->hideactivitytypenavlink = 0;
}
if ($CFG->hideactivitytypenavlink == 2) {
$hideactivitylink = true;
} else if ($CFG->hideactivitytypenavlink == 1 && $CFG->rolesactive &&
!empty($COURSE->id) && $COURSE->id != SITEID) {
if (!isset($COURSE->context)) {
$COURSE->context = get_context_instance(CONTEXT_COURSE, $COURSE->id);
}
$hideactivitylink = !has_capability('moodle/course:manageactivities', $COURSE->context);
} else {
$hideactivitylink = false;
}
//Construct an unordered list from $navlinks
//Accessibility: heading hidden from visual browsers by default.
$navigation = get_accesshide(get_string('youarehere','access'), 'h2')."
\n";
$lastindex = count($navlinks) - 1;
$i = -1; // Used to count the times, so we know when we get to the last item.
$first = true;
foreach ($navlinks as $navlink) {
$i++;
$last = ($i == $lastindex);
if (!is_array($navlink)) {
continue;
}
if (!empty($navlink['type']) && $navlink['type'] == 'activity' && !$last && $hideactivitylink) {
continue;
}
if ($first) {
$navigation .= '
";
return(array('newnav' => true, 'navlinks' => $navigation));
}
/**
* 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, '', $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 = ' style="text-align:'.$align.';"';
}
if ($class) {
$class = ' class="'.$class.'"';
}
$output = "".stripslashes_safe($text)."";
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 .= '
';
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 = '
'.stripslashes($heading).'
';
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 == '') {
if (!empty($_SERVER['HTTP_REFERER'])) {
$link = $_SERVER['HTTP_REFERER'];
$link = str_replace('&', '&', $link); // make it valid XHTML
} else {
$link = $CFG->wwwroot .'/';
}
}
$options = array();
$linkparts = parse_url(str_replace('&', '&', $link));
if (isset($linkparts['query'])) {
parse_str($linkparts['query'], $options);
}
$output .= '
'."\n";
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Print a message in a standard themed box.
* Replaces print_simple_box (see deprecatedlib.php)
*
* @param string $message, the content of the box
* @param string $classes, space-separated class names.
* @param string $idbase
* @param boolean $return, return as string or just print it
* @return mixed string or void
*/
function print_box($message, $classes='generalbox', $ids='', $return=false) {
$output = print_box_start($classes, $ids, true);
$output .= stripslashes_safe($message);
$output .= print_box_end(true);
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Starts a box using divs
* Replaces print_simple_box_start (see deprecatedlib.php)
*
* @param string $classes, space-separated class names.
* @param string $idbase
* @param boolean $return, return as string or just print it
* @return mixed string or void
*/
function print_box_start($classes='generalbox', $ids='', $return=false) {
global $THEME;
if (strpos($classes, 'clearfix') !== false) {
$clearfix = true;
$classes = trim(str_replace('clearfix', '', $classes));
} else {
$clearfix = false;
}
if (!empty($THEME->customcorners)) {
$classes .= ' ccbox box';
} else {
$classes .= ' box';
}
return print_container_start($clearfix, $classes, $ids, $return);
}
/**
* Simple function to end a box (see above)
* Replaces print_simple_box_end (see deprecatedlib.php)
*
* @param boolean $return, return as string or just print it
*/
function print_box_end($return=false) {
return print_container_end($return);
}
/**
* Print a message in a standard themed container.
*
* @param string $message, the content of the container
* @param boolean $clearfix clear both sides
* @param string $classes, space-separated class names.
* @param string $idbase
* @param boolean $return, return as string or just print it
* @return string or void
*/
function print_container($message, $clearfix=false, $classes='', $idbase='', $return=false) {
$output = print_container_start($clearfix, $classes, $idbase, true);
$output .= stripslashes_safe($message);
$output .= print_container_end(true);
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Starts a container using divs
*
* @param boolean $clearfix clear both sides
* @param string $classes, space-separated class names.
* @param string $idbase
* @param boolean $return, return as string or just print it
* @return mixed string or void
*/
function print_container_start($clearfix=false, $classes='', $idbase='', $return=false) {
global $THEME;
if (!isset($THEME->open_containers)) {
$THEME->open_containers = array();
}
$THEME->open_containers[] = $idbase;
if (!empty($THEME->customcorners)) {
$output = _print_custom_corners_start($clearfix, $classes, $idbase);
} else {
if ($idbase) {
$id = ' id="'.$idbase.'"';
} else {
$id = '';
}
if ($clearfix) {
$clearfix = ' clearfix';
} else {
$clearfix = '';
}
if ($classes or $clearfix) {
$class = ' class="'.$classes.$clearfix.'"';
} else {
$class = '';
}
$output = '
';
}
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Simple function to end a container (see above)
* @param boolean $return, return as string or just print it
* @return mixed string or void
*/
function print_container_end($return=false) {
global $THEME;
if (empty($THEME->open_containers)) {
debugging('Incorrect request to end container - no more open containers.', DEBUG_DEVELOPER);
$idbase = '';
} else {
$idbase = array_pop($THEME->open_containers);
}
if (!empty($THEME->customcorners)) {
$output = _print_custom_corners_end($idbase);
} else {
$output = '
';
}
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Returns number of currently open containers
* @return int number of open containers
*/
function open_containers() {
global $THEME;
if (!isset($THEME->open_containers)) {
$THEME->open_containers = array();
}
return count($THEME->open_containers);
}
/**
* Force closing of open containers
* @param boolean $return, return as string or just print it
* @param int $keep number of containers to be kept open - usually theme or page containers
* @return mixed string or void
*/
function print_container_end_all($return=false, $keep=0) {
$output = '';
while (open_containers() > $keep) {
$output .= print_container_end($return);
}
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Internal function - do not use directly!
* Starting part of the surrounding divs for custom corners
*
* @param boolean $clearfix, add CLASS "clearfix" to the inner div against collapsing
* @param string $classes
* @param mixed $idbase, optionally, define one idbase to be added to all the elements in the corners
* @return string
*/
function _print_custom_corners_start($clearfix=false, $classes='', $idbase='') {
/// Analise if we want ids for the custom corner elements
$id = '';
$idbt = '';
$idi1 = '';
$idi2 = '';
$idi3 = '';
if ($idbase) {
$id = 'id="'.$idbase.'" ';
$idbt = 'id="'.$idbase.'-bt" ';
$idi1 = 'id="'.$idbase.'-i1" ';
$idi2 = 'id="'.$idbase.'-i2" ';
$idi3 = 'id="'.$idbase.'-i3" ';
}
/// Calculate current level
$level = open_containers();
/// Output begins
$output = '
'."\n";
$output .= '
';
$output .= "\n";
$output .= '
';
$output .= (!empty($clearfix)) ? '
' : '
';
return $output;
}
/**
* Internal function - do not use directly!
* Ending part of the surrounding divs for custom corners
* @param string $idbase
* @return string
*/
function _print_custom_corners_end($idbase) {
/// Analise if we want ids for the custom corner elements
$idbb = '';
if ($idbase) {
$idbb = 'id="' . $idbase . '-bb" ';
}
/// Output begins
$output = '
';
$output .= "\n";
$output .= '
'."\n";
$output .= '
';
return $output;
}
/**
* Print a self contained form with a single submit button.
*
* @param string $link used as the action attribute on the form, so the URL that will be hit if the button is clicked.
* @param array $options these become hidden form fields, so these options get passed to the script at $link.
* @param string $label the caption that appears on the button.
* @param string $method HTTP method used on the request of the button is clicked. 'get' or 'post'.
* @param string $target no longer used.
* @param boolean $return if false, output the form directly, otherwise return the HTML as a string.
* @param string $tooltip a tooltip to add to the button as a title attribute.
* @param boolean $disabled if true, the button will be disabled.
* @param string $jsconfirmmessage if not empty then display a confirm dialogue with this string as the question.
* @return string / nothing depending on the $return paramter.
*/
function print_single_button($link, $options, $label='OK', $method='get', $target='_self', $return=false, $tooltip='', $disabled = false, $jsconfirmmessage='') {
$output = '';
$link = str_replace('"', '"', $link); //basic XSS protection
$output .= '
';
// taking target out, will need to add later target="'.$target.'"
$output .= '
';
if ($return) {
return $output;
} else {
echo $output;
}
}
/**
* Prints a basic textarea field.
*
* @uses $CFG
* @param boolean $usehtmleditor ?
* @param int $rows ?
* @param int $cols ?
* @param null $width Legacy field no longer used! Set to zero to get control over mincols
* @param null $height Legacy field no longer used! Set to zero to get control over minrows
* @param string $name ?
* @param string $value ?
* @param int $courseid ?
* @todo Finish documenting this function
*/
function print_textarea($usehtmleditor, $rows, $cols, $width, $height, $name, $value='', $courseid=0, $return=false, $id='') {
/// $width and height are legacy fields and no longer used as pixels like they used to be.
/// However, you can set them to zero to override the mincols and minrows values below.
global $CFG, $COURSE, $HTTPSPAGEREQUIRED;
static $scriptcount = 0; // For loading the htmlarea script only once.
$mincols = 65;
$minrows = 10;
$str = '';
if ($id === '') {
$id = 'edit-'.$name;
}
if ( empty($CFG->editorsrc) ) { // for backward compatibility.
if (empty($courseid)) {
$courseid = $COURSE->id;
}
if ($usehtmleditor) {
if (!empty($courseid) and has_capability('moodle/course:managefiles', get_context_instance(CONTEXT_COURSE, $courseid))) {
$httpsrequired = empty($HTTPSPAGEREQUIRED) ? '' : '&httpsrequired=1';
// needed for course file area browsing in image insert plugin
$str .= ($scriptcount < 1) ? ''."\n" : '';
} else {
$httpsrequired = empty($HTTPSPAGEREQUIRED) ? '' : '?httpsrequired=1';
$str .= ($scriptcount < 1) ? ''."\n" : '';
}
$str .= ($scriptcount < 1) ? ''."\n" : '';
$scriptcount++;
if ($height) { // Usually with legacy calls
if ($rows < $minrows) {
$rows = $minrows;
}
}
if ($width) { // Usually with legacy calls
if ($cols < $mincols) {
$cols = $mincols;
}
}
}
}
$str .= ''."\n";
if ($usehtmleditor) {
// Show shortcuts button if HTML editor is in use, but only if JavaScript is enabled (MDL-9556)
$str .= '';
}
if ($return) {
return $str;
}
echo $str;
}
/**
* Sets up the HTML editor on textareas in the current page.
* If a field name is provided, then it will only be
* applied to that field - otherwise it will be used
* on every textarea in the page.
*
* In most cases no arguments need to be supplied
*
* @param string $name Form element to replace with HTMl editor by name
*/
function use_html_editor($name='', $editorhidebuttons='', $id='') {
global $THEME;
$editor = 'editor_'.md5($name); //name might contain illegal characters
if ($id === '') {
$id = 'edit-'.$name;
}
echo "\n".''."\n";
}
function print_editor_config($editorhidebuttons='', $return=false) {
global $CFG;
$str = "config.pageStyle = \"body {";
if (!(empty($CFG->editorbackgroundcolor))) {
$str .= " background-color: $CFG->editorbackgroundcolor;";
}
if (!(empty($CFG->editorfontfamily))) {
$str .= " font-family: $CFG->editorfontfamily;";
}
if (!(empty($CFG->editorfontsize))) {
$str .= " font-size: $CFG->editorfontsize;";
}
$str .= " }\";\n";
$str .= "config.killWordOnPaste = ";
$str .= (empty($CFG->editorkillword)) ? "false":"true";
$str .= ';'."\n";
$str .= 'config.fontname = {'."\n";
$fontlist = isset($CFG->editorfontlist) ? explode(';', $CFG->editorfontlist) : array();
$i = 1; // Counter is used to get rid of the last comma.
foreach ($fontlist as $fontline) {
if (!empty($fontline)) {
if ($i > 1) {
$str .= ','."\n";
}
list($fontkey, $fontvalue) = split(':', $fontline);
$str .= '"'. $fontkey ."\":\t'". $fontvalue ."'";
$i++;
}
}
$str .= '};';
if (!empty($editorhidebuttons)) {
$str .= "\nconfig.hideSomeButtons(\" ". $editorhidebuttons ." \");\n";
} else if (!empty($CFG->editorhidebuttons)) {
$str .= "\nconfig.hideSomeButtons(\" ". $CFG->editorhidebuttons ." \");\n";
}
if (!empty($CFG->editorspelling) && !empty($CFG->aspellpath)) {
$str .= print_speller_code($CFG->htmleditor, true);
}
if ($return) {
return $str;
}
echo $str;
}
/**
* Returns a turn edit on/off button for course in a self contained form.
* Used to be an icon, but it's now a simple form button
*
* Note that the caller is responsible for capchecks.
*
* @uses $CFG
* @uses $USER
* @param int $courseid The course to update by id as found in 'course' table
* @return string
*/
function update_course_icon($courseid) {
global $CFG, $USER;
if (!empty($USER->editing)) {
$string = get_string('turneditingoff');
$edit = '0';
} else {
$string = get_string('turneditingon');
$edit = '1';
}
return '';
}
/**
* Returns a little popup menu for switching roles
*
* @uses $CFG
* @uses $USER
* @param int $courseid The course to update by id as found in 'course' table
* @return string
*/
function switchroles_form($courseid) {
global $CFG, $USER;
if (!$context = get_context_instance(CONTEXT_COURSE, $courseid)) {
return '';
}
if (!empty($USER->access['rsw'][$context->path])){ // Just a button to return to normal
$options = array();
$options['id'] = $courseid;
$options['sesskey'] = sesskey();
$options['switchrole'] = 0;
return print_single_button($CFG->wwwroot.'/course/view.php', $options,
get_string('switchrolereturn'), 'post', '_self', true);
}
if (has_capability('moodle/role:switchroles', $context)) {
if (!$roles = get_assignable_roles_for_switchrole($context)) {
return ''; // Nothing to show!
}
// unset default user role - it would not work
unset($roles[$CFG->guestroleid]);
return popup_form($CFG->wwwroot.'/course/view.php?id='.$courseid.'&sesskey='.sesskey().'&switchrole=',
$roles, 'switchrole', '', get_string('switchroleto'), 'switchrole', get_string('switchroleto'), true);
}
return '';
}
/**
* Returns a turn edit on/off button for course in a self contained form.
* Used to be an icon, but it's now a simple form button
*
* @uses $CFG
* @uses $USER
* @param int $courseid The course to update by id as found in 'course' table
* @return string
*/
function update_mymoodle_icon() {
global $CFG, $USER;
if (!empty($USER->editing)) {
$string = get_string('updatemymoodleoff');
$edit = '0';
} else {
$string = get_string('updatemymoodleon');
$edit = '1';
}
return "";
}
/**
* Returns a turn edit on/off button for tag in a self contained form.
*
* @uses $CFG
* @uses $USER
* @return string
*/
function update_tag_button($tagid) {
global $CFG, $USER;
if (!empty($USER->editing)) {
$string = get_string('turneditingoff');
$edit = '0';
} else {
$string = get_string('turneditingon');
$edit = '1';
}
return "";
}
/**
* Prints the editing button on a module "view" page
*
* @uses $CFG
* @param type description
* @todo Finish documenting this function
*/
function update_module_button($moduleid, $courseid, $string) {
global $CFG, $USER;
if (has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_MODULE, $moduleid))) {
$string = get_string('updatethis', '', $string);
return "";
} else {
return '';
}
}
/**
* Prints the editing button on search results listing
* For bulk move courses to another category
*/
function update_categories_search_button($search,$page,$perpage) {
global $CFG, $USER;
// not sure if this capability is the best here
if (has_capability('moodle/category:manage', get_context_instance(CONTEXT_SYSTEM))) {
if (!empty($USER->categoryediting)) {
$string = get_string("turneditingoff");
$edit = "off";
$perpage = 30;
} else {
$string = get_string("turneditingon");
$edit = "on";
}
return "";
}
}
/**
* Given a course and a (current) coursemodule
* This function returns a small popup menu with all the
* course activity modules in it, as a navigation menu
* The data is taken from the serialised array stored in
* the course record
*
* @param course $course A {@link $COURSE} object.
* @param course $cm A {@link $COURSE} object.
* @param string $targetwindow ?
* @return string
* @todo Finish documenting this function
*/
function navmenu($course, $cm=NULL, $targetwindow='self') {
global $CFG, $THEME, $USER;
if (empty($THEME->navmenuwidth)) {
$width = 50;
} else {
$width = $THEME->navmenuwidth;
}
if ($cm) {
$cm = $cm->id;
}
if ($course->format == 'weeks') {
$strsection = get_string('week');
} else {
$strsection = get_string('topic');
}
$strjumpto = get_string('jumpto');
$modinfo = get_fast_modinfo($course);
$context = get_context_instance(CONTEXT_COURSE, $course->id);
$section = -1;
$selected = '';
$url = '';
$previousmod = NULL;
$backmod = NULL;
$nextmod = NULL;
$selectmod = NULL;
$logslink = NULL;
$flag = false;
$menu = array();
$menustyle = array();
$sections = get_records('course_sections','course',$course->id,'section','section,visible,summary');
if (!empty($THEME->makenavmenulist)) { /// A hack to produce an XHTML navmenu list for use in themes
$THEME->navmenulist = navmenulist($course, $sections, $modinfo, $strsection, $strjumpto, $width, $cm);
}
foreach ($modinfo->cms as $mod) {
if ($mod->modname == 'label') {
continue;
}
if ($mod->sectionnum > $course->numsections) { /// Don't show excess hidden sections
break;
}
if (!$mod->uservisible) { // do not icnlude empty sections at all
continue;
}
if ($mod->sectionnum > 0 and $section != $mod->sectionnum) {
$thissection = $sections[$mod->sectionnum];
if ($thissection->visible or !$course->hiddensections or
has_capability('moodle/course:viewhiddensections', $context)) {
$thissection->summary = strip_tags(format_string($thissection->summary,true));
if ($course->format == 'weeks' or empty($thissection->summary)) {
$menu[] = '--'.$strsection ." ". $mod->sectionnum;
} else {
if (strlen($thissection->summary) < ($width-3)) {
$menu[] = '--'.$thissection->summary;
} else {
$menu[] = '--'.substr($thissection->summary, 0, $width).'...';
}
}
$section = $mod->sectionnum;
} else {
// no activities from this hidden section shown
continue;
}
}
$url = $mod->modname.'/view.php?id='. $mod->id;
if ($flag) { // the current mod is the "next" mod
$nextmod = $mod;
$flag = false;
}
$localname = $mod->name;
if ($cm == $mod->id) {
$selected = $url;
$selectmod = $mod;
$backmod = $previousmod;
$flag = true; // set flag so we know to use next mod for "next"
$localname = $strjumpto;
$strjumpto = '';
} else {
$localname = strip_tags(format_string($localname,true));
$tl=textlib_get_instance();
if ($tl->strlen($localname) > ($width+5)) {
$localname = $tl->substr($localname, 0, $width).'...';
}
if (!$mod->visible) {
$localname = '('.$localname.')';
}
}
$menu[$url] = $localname;
if (empty($THEME->navmenuiconshide)) {
$menustyle[$url] = 'style="background-image: url('.$CFG->modpixpath.'/'.$mod->modname.'/icon.gif);"'; // Unfortunately necessary to do this here
}
$previousmod = $mod;
}
//Accessibility: added Alt text, replaced > < with 'silent' character and 'accesshide' text.
if ($selectmod and has_capability('coursereport/log:view', $context)) {
$logstext = get_string('alllogs');
$logslink = '
';
}
/**
* Given a course
* This function returns a small popup menu with all the
* course activity modules in it, as a navigation menu
* outputs a simple list structure in XHTML
* The data is taken from the serialised array stored in
* the course record
*
* @param course $course A {@link $COURSE} object.
* @return string
* @todo Finish documenting this function
*/
function navmenulist($course, $sections, $modinfo, $strsection, $strjumpto, $width=50, $cmid=0) {
global $CFG;
$section = -1;
$url = '';
$menu = array();
$doneheading = false;
$coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
$menu[] = '
'.$strjumpto.'
';
foreach ($modinfo->cms as $mod) {
if ($mod->modname == 'label') {
continue;
}
if ($mod->sectionnum > $course->numsections) { /// Don't show excess hidden sections
break;
}
if (!$mod->uservisible) { // do not icnlude empty sections at all
continue;
}
if ($mod->sectionnum >= 0 and $section != $mod->sectionnum) {
$thissection = $sections[$mod->sectionnum];
if ($thissection->visible or !$course->hiddensections or
has_capability('moodle/course:viewhiddensections', $coursecontext)) {
$thissection->summary = strip_tags(format_string($thissection->summary,true));
if (!$doneheading) {
$menu[] = '
');
if (! defined('HEADER_PRINTED')) {
//header not yet printed
@header('HTTP/1.0 404 Not Found');
print_header(get_string('error'));
} else {
print_container_end_all(false, $THEME->open_header_containers);
}
echo ' ';
print_simple_box($message, '', '', '', '', 'errorbox');
debugging('Stack trace:', DEBUG_DEVELOPER);
// in case we are logging upgrade in admin/index.php stop it
if (function_exists('upgrade_log_finish')) {
upgrade_log_finish();
}
if (!empty($link)) {
print_continue($link);
}
print_footer();
for ($i=0;$i<512;$i++) { // Padding to help IE work with 404
echo ' ';
}
die;
}
/**
* Print an error to STDOUT and exit with a non-zero code. For commandline scripts.
* Default errorcode is 1.
*
* Very useful for perl-like error-handling:
*
* do_somethting() or mdie("Something went wrong");
*
* @param string $msg Error message
* @param integer $errorcode Error code to emit
*/
function mdie($msg='', $errorcode=1) {
trigger_error($msg);
exit($errorcode);
}
/**
* Returns a string of html with an image of a help icon linked to a help page on a number of help topics.
* Should be used only with htmleditor or textarea.
* @param mixed $helptopics variable amount of params accepted. Each param may be a string or an array of arguments for
* helpbutton.
* @return string
*/
function editorhelpbutton(){
global $CFG, $SESSION;
$items = func_get_args();
$i = 1;
$urlparams = array();
$titles = array();
foreach ($items as $item){
if (is_array($item)){
$urlparams[] = "keyword$i=".urlencode($item[0]);
$urlparams[] = "title$i=".urlencode($item[1]);
if (isset($item[2])){
$urlparams[] = "module$i=".urlencode($item[2]);
}
$titles[] = trim($item[1], ". \t");
}elseif (is_string($item)){
$urlparams[] = "button$i=".urlencode($item);
switch ($item){
case 'reading' :
$titles[] = get_string("helpreading");
break;
case 'writing' :
$titles[] = get_string("helpwriting");
break;
case 'questions' :
$titles[] = get_string("helpquestions");
break;
case 'emoticons' :
$titles[] = get_string("helpemoticons");
break;
case 'richtext' :
$titles[] = get_string('helprichtext');
break;
case 'text' :
$titles[] = get_string('helptext');
break;
default :
error('Unknown help topic '.$item);
}
}
$i++;
}
if (count($titles)>1){
//join last two items with an 'and'
$a = new object();
$a->one = $titles[count($titles) - 2];
$a->two = $titles[count($titles) - 1];
$titles[count($titles) - 2] = get_string('and', '', $a);
unset($titles[count($titles) - 1]);
}
$alttag = join (', ', $titles);
$paramstring = join('&', $urlparams);
$linkobject = '';
return link_to_popup_window(s('/lib/form/editorhelp.php?'.$paramstring), 'popup', $linkobject, 400, 500, $alttag, 'none', true);
}
/**
* 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, $COURSE;
//warning if ever $text parameter is used
//$text option won't work properly because the text needs to be always cleaned and,
// when cleaned... html tags always break, so it's unusable.
if ( isset($text) && $text!='') {
debugging('Warning: it\'s not recommended to use $text parameter in helpbutton ($page=' . $page . ', $module=' . $module . ') function', DEBUG_DEVELOPER);
}
// fix for MDL-7734
if (!empty($COURSE->lang)) {
$forcelang = $COURSE->lang;
} else {
$forcelang = '';
}
if ($module == '') {
$module = 'moodle';
}
if ($title == '' && $linktext == '') {
debugging('Error in call to helpbutton function: at least one of $title and $linktext is required');
}
// Warn users about new window for Accessibility
$tooltip = get_string('helpprefix2', '', trim($title, ". \t")) .' ('.get_string('newwindow').')';
$linkobject = '';
if ($image) {
if ($linktext) {
// MDL-7469 If text link is displayed with help icon, change to alt to "help with this".
$linkobject .= $title.' ';
$tooltip = get_string('helpwiththis');
}
if ($imagetext) {
$linkobject .= $imagetext;
} else {
$linkobject .= '';
}
} else {
$linkobject .= $tooltip;
}
// fix for MDL-7734
if ($text) {
$url = '/help.php?module='. $module .'&text='. s(urlencode($text).'&forcelang='.$forcelang);
} else {
$url = '/help.php?module='. $module .'&file='. $page .'.html&forcelang='.$forcelang;
}
$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, $return = false) {
global $CFG, $SESSION;
$SESSION->inserttextform = $form;
$SESSION->inserttextfield = $field;
$imagetext = '';
$help = helpbutton('emoticons', get_string('helpemoticons'), 'moodle', true, true, '', true, $imagetext);
if (!$return){
echo $help;
} else {
return $help;
}
}
/**
* Print a help button.
*
* Prints a special help button for html editors (htmlarea in this case)
* @uses $CFG
*/
function editorshortcutshelpbutton() {
global $CFG;
$imagetext = '';
return helpbutton('editorshortcuts', get_string('editorshortcutkeys'), 'moodle', true, false, '', true, $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, $THEME, $COURSE;
$message = clean_text($message); // In case nasties are in here
if (defined('FULLME') && FULLME == 'cron') {
// notices in cron should be mtrace'd.
mtrace($message);
die;
}
if (! defined('HEADER_PRINTED')) {
//header not yet printed
print_header(get_string('notice'));
} else {
print_container_end_all(false, $THEME->open_header_containers);
}
print_box($message, 'generalbox', 'notice');
print_continue($link);
if (empty($course)) {
print_footer($COURSE);
} 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"
* TODO Document remaining arguments
*/
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_box_start('generalbox', 'notice');
echo '
';
print_box_end();
}
/**
* Provide an definition of error_get_last for PHP before 5.2.0. This simply
* returns NULL, since there is not way to get the right answer.
*/
if (!function_exists('error_get_last')) {
// the eval is needed to prevent PHP 5.2+ from getting a parse error!
eval('
function error_get_last() {
return NULL;
}
');
}
/**
* 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, $THEME;
if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()])) {
$url = sid_process_url($url);
}
$message = clean_text($message);
$encodedurl = preg_replace("/\&(?![a-zA-Z0-9#]{1,8};)/", "&", $url);
$encodedurl = preg_replace('/^.*href="([^"]*)".*$/', "\\1", clean_text(''));
$url = str_replace('&', '&', $encodedurl);
/// At developer debug level. Don't redirect if errors have been printed on screen.
/// Currenly only works in PHP 5.2+; we do not want strict PHP5 errors
$lasterror = error_get_last();
$error = defined('DEBUGGING_PRINTED') or (!empty($lasterror) && ($lasterror['type'] & DEBUG_DEVELOPER));
$errorprinted = debugging('', DEBUG_ALL) && $CFG->debugdisplay && $error;
if ($errorprinted) {
$message = "Error output, so disabling automatic redirect.
" . $message;
}
$performanceinfo = '';
if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) {
if (defined('MDL_PERFTOLOG') && !function_exists('register_shutdown_function')) {
$perf = get_performance_info();
error_log("PERF: " . $perf['txt']);
}
}
/// when no message and header printed yet, try to redirect
if (empty($message) and !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($_SERVER['SERVER_PROTOCOL'] . ' 303 See Other'); //302 might not work for POST requests, 303 is ignored by obsolete clients
@header('Location: '.$url);
//another way for older browsers and already sent headers (eg trailing whitespace in config.php)
echo '';
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('', '', '', '', $errorprinted ? '' : (''));
$delay += 3; // double redirect prevention, it was sometimes breaking upgrades before 1.7
} else {
print_container_end_all(false, $THEME->open_header_containers);
}
echo '
';
if (!$errorprinted) {
?>
docroot = false; // to prevent the link to moodle docs from being displayed on redirect page.
print_footer('none');
die;
}
/**
* Print a bold message in an optional color.
*
* @param string $message The message to print out
* @param string $style Optional style to display message text in
* @param string $align Alignment option
* @param bool $return whether to return an output string or echo now
*/
function notify($message, $style='notifyproblem', $align='center', $return=false) {
if ($style == 'green') {
$style = 'notifysuccess'; // backward compatible with old color system
}
$message = clean_text($message);
$output = '
'. $message .'
'."\n";
if ($return) {
return $output;
}
echo $output;
}
/**
* 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) && $email{$i}!='@') { //MDL-20619 some browsers have problems unobfuscating @
$obfuscated.='%'.dechex(ord($email{$i}));
} else {
$obfuscated.=$email{$i};
}
$i++;
}
return $obfuscated;
}
/**
* This function takes some text and replaces about half of the characters
* with HTML entity equivalents. Return string is obviously longer.
*
* @param string $plaintext The text to be obfuscated
* @return string
*/
function obfuscate_text($plaintext) {
$i=0;
$length = strlen($plaintext);
$obfuscated='';
$prev_obfuscated = false;
while ($i < $length) {
$c = ord($plaintext{$i});
$numerical = ($c >= ord('0')) && ($c <= ord('9'));
if ($prev_obfuscated and $numerical ) {
$obfuscated.=''.ord($plaintext{$i}).';';
} else if (rand(0,2)) {
$obfuscated.=''.ord($plaintext{$i}).';';
$prev_obfuscated = true;
} else {
$obfuscated.=$plaintext{$i};
$prev_obfuscated = false;
}
$i++;
}
return $obfuscated;
}
/**
* This function uses the {@link obfuscate_email()} and {@link obfuscate_text()}
* to generate a fully obfuscated email link, ready to use.
*
* @param string $email The email address to display
* @param string $label The text to dispalyed as hyperlink to $email
* @param boolean $dimmed If true then use css class 'dimmed' for hyperlink
* @return string
*/
function obfuscate_mailto($email, $label='', $dimmed=false) {
if (empty($label)) {
$label = $email;
}
if ($dimmed) {
$title = get_string('emaildisable');
$dimmed = ' class="dimmed"';
} else {
$title = '';
$dimmed = '';
}
return sprintf("%s",
obfuscate_text('mailto'), obfuscate_email($email),
obfuscate_text($label));
}
/**
* Prints a single paging bar to provide access to other pages (usually in a search)
*
* @param int $totalcount Thetotal number of entries available to be paged through
* @param int $page The page you are currently viewing
* @param int $perpage The number of entries that should be shown per page
* @param mixed $baseurl If this is a string then it is the url which will be appended with $pagevar, an equals sign and the page number.
* If this is a moodle_url object then the pagevar param will be replaced by the page no, for each page.
* @param string $pagevar This is the variable name that you use for the page number in your code (ie. 'tablepage', 'blogpage', etc)
* @param bool $nocurr do not display the current page as a link
* @param bool $return whether to return an output string or echo now
* @return bool or string
*/
function print_paging_bar($totalcount, $page, $perpage, $baseurl, $pagevar='page',$nocurr=false, $return=false) {
$maxdisplay = 18;
$output = '';
if ($totalcount > $perpage) {
$output .= '
';
}
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++;
$skip_text = get_string('skipa', 'access', strip_tags($title));
$skip_link = ''.$skip_text.'';
$skip_dest = '';
$strip_title = strip_tags($title);
if (! empty($strip_title)) {
echo $skip_link;
}
//ELSE: a single link on a page "Skip block 4" is too confusing - ignore.
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";
foreach ($list as $key => $string) {
echo '
';
if ($icons) {
echo '
'. $icons[$key] .'
';
}
echo '
' . $string . '
';
echo "
\n";
$row = $row ? 0:1;
}
echo "
\n";
}
if ($footer) {
echo '';
}
}
print_side_block_end($attributes, $title);
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, $THEME;
// If there are no special attributes, give a default CSS class
if (empty($attributes) || !is_array($attributes)) {
$attributes = array('class' => 'sideblock');
} else if(!isset($attributes['class'])) {
$attributes['class'] = 'sideblock';
} else if(!strpos($attributes['class'], 'sideblock')) {
$attributes['class'] .= ' sideblock';
}
// OK, the class is surely there and in addition to anything
// else, it's tagged as a sideblock
/*
// IE misery: if I do it this way, blocks which start hidden cannot be "unhidden"
// If there is a cookie to hide this thing, start it hidden
if (!empty($attributes['id']) && isset($_COOKIE['hide:'.$attributes['id']])) {
$attributes['class'] = 'hidden '.$attributes['class'];
}
*/
$attrtext = '';
foreach ($attributes as $attr => $val) {
$attrtext .= ' '.$attr.'="'.$val.'"';
}
echo '
';
if (!empty($THEME->customcorners)) {
echo '
'."\n";
}
if ($heading) {
//Accessibility: H2 more appropriate in moodleblock.class.php: _title_html.
echo '
';
if (!empty($THEME->customcorners)) {
echo '
';
echo '
';
echo '
';
}
echo $heading;
if (!empty($THEME->customcorners)) {
echo '
';
}
echo '
';
} else {
if (!empty($THEME->customcorners)) {
echo '
';
}
}
if (!empty($THEME->customcorners)) {
echo '
';
echo '
';
}
echo '
';
}
/**
* Print table ending tags for a side block box.
*/
function print_side_block_end($attributes = array(), $title='') {
global $CFG, $THEME;
echo '
';
if (!empty($THEME->customcorners)) {
echo '
';
}
echo '
';
$strshow = addslashes_js(get_string('showblocka', 'access', strip_tags($title)));
$strhide = addslashes_js(get_string('hideblocka', 'access', strip_tags($title)));
// IE workaround: if I do it THIS way, it works! WTF?
if (!empty($CFG->allowuserblockhiding) && isset($attributes['id'])) {
echo '';
}
}
/**
* Prints out code needed for spellchecking.
* Original idea by Ludo (Marc Alier).
*
* Opening CDATA and