Fixing a small css issue in the user class.
[elgg.git] / lib / elgglib.php
blob3a6a86ed0d6e484720e54950c691ac85f3cd43ed
1 <?php
3 /**
4 * Library of functions for handling input validation
5 * and some HTML generation
6 * This library is a combination of bits of lib/weblib.php
7 * and lib/moodlelib.php from moodle
8 * http://moodle.org || http://sourceforge.net/projects/moodle
9 * Copyright (C) 2001-2003 Martin Dougiamas http://dougiamas.com
10 * @author Martin Dougiamas and many others
11 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
15 /// PARAMETER HANDLING ////////////////////////////////////////////////////
17 /**
18 * Returns a particular value for the named variable, taken from
19 * POST or GET. If the parameter doesn't exist then an error is
20 * thrown because we require this variable.
22 * This function should be used to initialise all required values
23 * in a script that are based on parameters. Usually it will be
24 * used like this:
25 * $id = required_param('id');
27 * @param string $varname the name of the parameter variable we want
28 * @param int $options a bit field that specifies any cleaning needed
29 * @return mixed
31 function required_param($varname, $options=PARAM_CLEAN) {
33 // detect_unchecked_vars addition
34 global $CFG;
35 if (!empty($CFG->detect_unchecked_vars)) {
36 global $UNCHECKED_VARS;
37 unset ($UNCHECKED_VARS->vars[$varname]);
40 if (isset($_POST[$varname])) { // POST has precedence
41 $param = $_POST[$varname];
42 } else if (isset($_GET[$varname])) {
43 $param = $_GET[$varname];
44 } else {
45 error('A required parameter ('.$varname.') was missing');
48 return clean_param($param, $options);
51 /**
52 * Returns a particular value for the named variable, taken from
53 * POST or GET, otherwise returning a given default.
55 * This function should be used to initialise all optional values
56 * in a script that are based on parameters. Usually it will be
57 * used like this:
58 * $name = optional_param('name', 'Fred');
60 * @param string $varname the name of the parameter variable we want
61 * @param mixed $default the default value to return if nothing is found
62 * @param int $options a bit field that specifies any cleaning needed
63 * @return mixed
65 function optional_param($varname, $default=NULL, $options=PARAM_CLEAN) {
67 // detect_unchecked_vars addition
68 global $CFG;
69 if (!empty($CFG->detect_unchecked_vars)) {
70 global $UNCHECKED_VARS;
71 unset ($UNCHECKED_VARS->vars[$varname]);
74 if (isset($_POST[$varname])) { // POST has precedence
75 $param = $_POST[$varname];
76 } else if (isset($_GET[$varname])) {
77 $param = $_GET[$varname];
78 } else {
79 return $default;
82 return clean_param($param, $options);
86 /**
87 * Used by {@link optional_param()} and {@link required_param()} to
88 * clean the variables and/or cast to specific types, based on
89 * an options field.
90 * <code>
91 * $course->format = clean_param($course->format, PARAM_ALPHA);
92 * $selectedgrade_item = clean_param($selectedgrade_item, PARAM_CLEAN);
93 * </code>
95 * @uses $CFG
96 * @uses PARAM_CLEAN
97 * @uses PARAM_INT
98 * @uses PARAM_INTEGER
99 * @uses PARAM_ALPHA
100 * @uses PARAM_ALPHANUM
101 * @uses PARAM_NOTAGS
102 * @uses PARAM_ALPHATEXT
103 * @uses PARAM_BOOL
104 * @uses PARAM_SAFEDIR
105 * @uses PARAM_CLEANFILE
106 * @uses PARAM_FILE
107 * @uses PARAM_PATH
108 * @uses PARAM_HOST
109 * @uses PARAM_URL
110 * @uses PARAM_LOCALURL
111 * @uses PARAM_CLEANHTML
112 * @param mixed $param the variable we are cleaning
113 * @param int $options a bit field that specifies the cleaning needed. This field is specified by combining PARAM_* definitions together with a logical or.
114 * @return mixed
116 function clean_param($param, $options) {
118 global $CFG;
120 if (is_array($param)) { // Let's loop
121 $newparam = array();
122 foreach ($param as $key => $value) {
123 $newparam[$key] = clean_param($value, $options);
125 return $newparam;
128 if (!$options) {
129 return $param; // Return raw value
132 //this corrupts data - Sven
133 //if ((string)$param == (string)(int)$param) { // It's just an integer
134 // return (int)$param;
137 if ($options & PARAM_CLEAN) {
138 // this breaks backslashes in user input
139 // $param = stripslashes($param); // Needed by kses to work fine
140 $param = clean_text($param); // Sweep for scripts, etc
141 // and this unnecessarily escapes quotes, etc in user input
142 // $param = addslashes($param); // Restore original request parameter slashes
145 if ($options & PARAM_INT) {
146 $param = (int)$param; // Convert to integer
149 if ($options & PARAM_ALPHA) { // Remove everything not a-z
150 $param = eregi_replace('[^a-zA-Z]', '', $param);
153 if ($options & PARAM_ALPHANUM) { // Remove everything not a-zA-Z0-9
154 $param = eregi_replace('[^A-Za-z0-9]', '', $param);
157 if ($options & PARAM_ALPHAEXT) { // Remove everything not a-zA-Z/_-
158 $param = eregi_replace('[^a-zA-Z/_-]', '', $param);
161 if ($options & PARAM_BOOL) { // Convert to 1 or 0
162 $tempstr = strtolower($param);
163 if ($tempstr == 'on') {
164 $param = 1;
165 } else if ($tempstr == 'off') {
166 $param = 0;
167 } else {
168 $param = empty($param) ? 0 : 1;
172 if ($options & PARAM_NOTAGS) { // Strip all tags completely
173 $param = strip_tags($param);
176 if ($options & PARAM_SAFEDIR) { // Remove everything not a-zA-Z0-9_-
177 $param = eregi_replace('[^a-zA-Z0-9_-]', '', $param);
180 if ($options & PARAM_CLEANFILE) { // allow only safe characters
181 $param = clean_filename($param);
184 if ($options & PARAM_FILE) { // Strip all suspicious characters from filename
185 $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':\\/]', '', $param);
186 $param = ereg_replace('\.\.+', '', $param);
187 if($param == '.') {
188 $param = '';
192 if ($options & PARAM_PATH) { // Strip all suspicious characters from file path
193 $param = str_replace('\\\'', '\'', $param);
194 $param = str_replace('\\"', '"', $param);
195 $param = str_replace('\\', '/', $param);
196 $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':]', '', $param);
197 $param = ereg_replace('\.\.+', '', $param);
198 $param = ereg_replace('//+', '/', $param);
199 $param = ereg_replace('/(\./)+', '/', $param);
202 if ($options & PARAM_HOST) { // allow FQDN or IPv4 dotted quad
203 preg_replace('/[^\.\d\w-]/','', $param ); // only allowed chars
204 // match ipv4 dotted quad
205 if (preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/',$param, $match)){
206 // confirm values are ok
207 if ( $match[0] > 255
208 || $match[1] > 255
209 || $match[3] > 255
210 || $match[4] > 255 ) {
211 // hmmm, what kind of dotted quad is this?
212 $param = '';
214 } elseif ( preg_match('/^[\w\d\.-]+$/', $param) // dots, hyphens, numbers
215 && !preg_match('/^[\.-]/', $param) // no leading dots/hyphens
216 && !preg_match('/[\.-]$/', $param) // no trailing dots/hyphens
218 // all is ok - $param is respected
219 } else {
220 // all is not ok...
221 $param='';
225 if ($options & PARAM_URL) { // allow safe ftp, http, mailto urls
227 include_once($CFG->dirroot . 'lib/validateurlsyntax.php');
230 // Parameters to validateurlsyntax()
232 // s? scheme is optional
233 // H? http optional
234 // S? https optional
235 // F? ftp optional
236 // E? mailto optional
237 // u- user section not allowed
238 // P- password not allowed
239 // a? address optional
240 // I? Numeric IP address optional (can use IP or domain)
241 // p- port not allowed -- restrict to default port
242 // f? "file" path section optional
243 // q? query section optional
244 // r? fragment (anchor) optional
246 if (!empty($param) && validateUrlSyntax($param, 's?H?S?F?E?u-P-a?I?p-f?q?r?')) {
247 // all is ok, param is respected
248 } else {
249 $param =''; // not really ok
251 $options ^= PARAM_URL; // Turn off the URL bit so that simple PARAM_URLs don't test true for PARAM_LOCALURL
254 if ($options & PARAM_LOCALURL) {
255 // assume we passed the PARAM_URL test...
256 // allow http absolute, root relative and relative URLs within wwwroot
257 if (!empty($param)) {
258 if (preg_match(':^/:', $param)) {
259 // root-relative, ok!
260 } elseif (preg_match('/^'.preg_quote($CFG->wwwroot, '/').'/i',$param)) {
261 // absolute, and matches our wwwroot
262 } else {
263 // relative - let's make sure there are no tricks
264 if (validateUrlSyntax($param, 's-u-P-a-p-f+q?r?')) {
265 // looks ok.
266 } else {
267 $param = '';
273 if ($options & PARAM_CLEANHTML) {
274 // $param = stripslashes($param); // Remove any slashes
275 $param = clean_text($param); // Sweep for scripts, etc
276 // $param = trim($param); // Sweep for scripts, etc
279 return $param;
283 * Retrieves the list of plugins available in the $plugin
284 * directory. Defaults to 'mod'.
286 * NOTE: To get the list of enabled modules, do
287 * get_records('modules', 'enabled', true) instead.
289 * @return array
291 function get_list_of_plugins($plugin='mod', $exclude='') {
293 global $CFG;
294 static $plugincache = array();
295 $plugincachename = $plugin . "_" . $exclude;
297 if (isset($plugincache[$plugincachename])) {
298 $plugins = $plugincache[$plugincachename];
299 } else {
300 $plugins = array();
301 $basedir = opendir($CFG->dirroot . $plugin);
302 while (false !== ($dir = readdir($basedir))) {
303 $firstchar = substr($dir, 0, 1);
304 if ($firstchar == '.' or $dir == 'CVS' or $dir == '_vti_cnf' or $dir == $exclude) {
305 continue;
307 if (filetype($CFG->dirroot . $plugin .'/'. $dir) != 'dir') {
308 continue;
310 $plugins[] = $dir;
312 if ($plugins) {
313 asort($plugins);
315 $plugincache[$plugincachename] = $plugins;
317 return $plugins;
320 // Adds a function to the variables used to cycle through plugin extensions
321 // to actions on objects
322 function listen_for_event($object_type, $event, $function) {
324 global $CFG;
325 $CFG->event_hooks[$object_type][$event][] = $function;
329 function plugin_hook($object_type,$event,$object = null) {
331 global $CFG;
333 if (!empty($CFG->event_hooks['all']['all']) && is_array($CFG->event_hooks['all']['all'])) {
334 foreach($CFG->event_hooks['all']['all'] as $hook) {
335 $object = $hook($object_type,$event,$object);
338 if (!empty($CFG->event_hooks[$object_type]['all']) && is_array($CFG->event_hooks[$object_type]['all'])) {
339 foreach($CFG->event_hooks[$object_type]['all'] as $hook) {
340 $object = $hook($object_type,$event,$object);
343 if (!empty($CFG->event_hooks['all'][$event]) && is_array($CFG->event_hooks['all'][$event])) {
344 foreach($CFG->event_hooks['all'][$event] as $hook) {
345 $object = $hook($object_type,$event,$object);
348 if (!empty($CFG->event_hooks[$object_type][$event]) && is_array($CFG->event_hooks[$object_type][$event])) {
349 foreach($CFG->event_hooks[$object_type][$event] as $hook) {
350 $object = $hook($object_type,$event,$object);
354 return $object;
358 function report_session_error() {
359 global $CFG, $FULLME;
361 //clear session cookies
362 setcookie('ElggSession'.$CFG->sessioncookie, '', time() - 3600, $CFG->cookiepath);
363 setcookie('ElggSessionTest'.$CFG->sessioncookie, '', time() - 3600, $CFG->cookiepath);
364 //increment database error counters
365 //if (isset($CFG->session_error_counter)) {
366 // set_config('session_error_counter', 1 + $CFG->session_error_counter);
367 //} else {
368 // set_config('session_error_counter', 1);
370 //called from setup.php, so gettext module hasn't been loaded yet
371 redirect($FULLME, 'A server error that affects your login session was detected. Please login again or restart your browser.', 5);
374 // never called
376 * For security purposes, this function will check that the currently
377 * given sesskey (passed as a parameter to the script or this function)
378 * matches that of the current user.
380 * @param string $sesskey optionally provided sesskey
381 * @return bool
383 // function confirm_sesskey($sesskey=NULL) {
384 // global $USER;
386 // if (!empty($USER->ignoresesskey) || !empty($CFG->ignoresesskey)) {
387 // return true;
388 // }
390 // if (empty($sesskey)) {
391 // $sesskey = required_param('sesskey'); // Check script parameters
392 // }
394 // if (!isset($USER->sesskey)) {
395 // return false;
396 // }
398 // return ($USER->sesskey === $sesskey);
399 // }
403 * Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey
404 * if one does not already exist, but does not overwrite existing sesskeys. Returns the
405 * sesskey string if $USER exists, or boolean false if not.
407 * @uses $USER
408 * @return string
410 function sesskey() {
411 global $USER;
413 if(!isset($USER)) {
414 return false;
417 if (empty($USER->sesskey)) {
418 $USER->sesskey = random_string(10);
421 return $USER->sesskey;
426 * Send an email to a specified user
428 * @uses $CFG
429 * @param user $user A {@link $USER} object
430 * @param user $from A {@link $USER} object
431 * @param string $subject plain text subject line of the email
432 * @param string $messagetext plain text version of the message
433 * @param string $messagehtml complete html version of the message (optional)
434 * @param string $attachment a file on the filesystem
435 * @param string $attachname the name of the file (extension indicates MIME)
436 * @param bool $usetrueaddress determines whether $from email address should
437 * be sent out. Will be overruled by user profile setting for maildisplay
438 * @return bool|string Returns "true" if mail was sent OK, "emailstop" if email
439 * was blocked by user and "false" if there was another sort of error.
441 function email_to_user($user, $from, $subject, $messagetext, $messagehtml='', $attachment='', $attachname='', $usetrueaddress=true, $repyto='', $replytoname='') {
443 global $CFG;
444 $textlib = textlib_get_instance();
446 include_once($CFG->libdir .'/phpmailer/class.phpmailer.php');
448 if (empty($user)) {
449 return false;
453 if (over_bounce_threshold($user)) {
454 error_log("User $user->id (".fullname($user).") is over bounce threshold! Not sending.");
455 return false;
457 */ // this doesn't exist right now, we may bring it in later though.
459 $mail = new phpmailer;
461 $mail->Version = 'Elgg '; // mailer version (should have $CFG->version on here but we don't have it yet)
462 $mail->PluginDir = $CFG->libdir .'/phpmailer/'; // plugin directory (eg smtp plugin)
465 $mail->CharSet = 'UTF-8'; // everything is now uft8
467 if (empty($CFG->smtphosts)) {
468 $mail->IsMail(); // use PHP mail() = sendmail
469 } else if ($CFG->smtphosts == 'qmail') {
470 $mail->IsQmail(); // use Qmail system
471 } else {
472 $mail->IsSMTP(); // use SMTP directly
473 if ($CFG->debug > 7) {
474 echo '<pre>' . "\n";
475 $mail->SMTPDebug = true;
477 $mail->Host = $CFG->smtphosts; // specify main and backup servers
479 if ($CFG->smtpuser) { // Use SMTP authentication
480 $mail->SMTPAuth = true;
481 $mail->Username = $CFG->smtpuser;
482 $mail->Password = $CFG->smtppass;
486 /* not here yet, leave it in just in case.
487 // make up an email address for handling bounces
488 if (!empty($CFG->handlebounces)) {
489 $modargs = 'B'.base64_encode(pack('V',$user->ident)).substr(md5($user->email),0,16);
490 $mail->Sender = generate_email_processing_address(0,$modargs);
492 else {
493 $mail->Sender = $CFG->sysadminemail;
496 $mail->Sender = $CFG->sysadminemail; // for elgg. delete if we change the above.
498 // TODO add a preference for maildisplay
499 if (is_string($from)) { // So we can pass whatever we want if there is need
500 $mail->From = $CFG->noreplyaddress;
501 $mail->FromName = $from;
502 } else if (empty($from)) { // make stuff up
503 $mail->From = $CFG->sysadminemail;
504 $mail->FromName = $CFG->sitename.' '.__gettext('Administrator');
505 } else if ($usetrueaddress and !empty($from->maildisplay)) {
506 $mail->From = $from->email;
507 $mail->FromName = $from->name;
508 } else {
509 $mail->From = $CFG->noreplyaddress;
510 $mail->FromName = $from->name;
511 if (empty($replyto)) {
512 $mail->AddReplyTo($CFG->noreplyaddress,__gettext('Do not reply'));
516 if (!empty($replyto)) {
517 $mail->AddReplyTo($replyto,$replytoname);
520 $mail->Subject = $textlib->substr(stripslashes($subject), 0, 900);
522 $mail->AddAddress($user->email, $user->name);
524 $mail->WordWrap = 79; // set word wrap
526 if (!empty($from->customheaders)) { // Add custom headers
527 if (is_array($from->customheaders)) {
528 foreach ($from->customheaders as $customheader) {
529 $mail->AddCustomHeader($customheader);
531 } else {
532 $mail->AddCustomHeader($from->customheaders);
536 if (!empty($from->priority)) {
537 $mail->Priority = $from->priority;
540 //TODO add a user preference for this. right now just send plaintext
541 $user->mailformat = 0;
542 if ($messagehtml && $user->mailformat == 1) { // Don't ever send HTML to users who don't want it
543 $mail->IsHTML(true);
544 $mail->Encoding = 'quoted-printable'; // Encoding to use
545 $mail->Body = $messagehtml;
546 $mail->AltBody = "\n$messagetext\n";
547 } else {
548 $mail->IsHTML(false);
549 $mail->Body = "\n$messagetext\n";
552 if ($attachment && $attachname) {
553 if (ereg( "\\.\\." ,$attachment )) { // Security check for ".." in dir path
554 $mail->AddAddress($CFG->sysadminemail,$CFG->sitename.' '.__gettext('Administrator'));
555 $mail->AddStringAttachment('Error in attachment. User attempted to attach a filename with a unsafe name.', 'error.txt', '8bit', 'text/plain');
556 } else {
557 require_once($CFG->libdir.'/filelib.php');
558 $mimetype = mimeinfo('type', $attachname);
559 $mail->AddAttachment($attachment, $attachname, 'base64', $mimetype);
563 if ($mail->Send()) {
564 // set_send_count($user); // later
565 return true;
566 } else {
567 mtrace('ERROR: '. $mail->ErrorInfo);
568 return false;
573 * Returns an array with all the filenames in
574 * all subdirectories, relative to the given rootdir.
575 * If excludefile is defined, then that file/directory is ignored
576 * If getdirs is true, then (sub)directories are included in the output
577 * If getfiles is true, then files are included in the output
578 * (at least one of these must be true!)
580 * @param string $rootdir ?
581 * @param string $excludefile If defined then the specified file/directory is ignored
582 * @param bool $descend ?
583 * @param bool $getdirs If true then (sub)directories are included in the output
584 * @param bool $getfiles If true then files are included in the output
585 * @return array An array with all the filenames in
586 * all subdirectories, relative to the given rootdir
587 * @todo Finish documenting this function. Add examples of $excludefile usage.
589 function get_directory_list($rootdir, $excludefile='', $descend=true, $getdirs=false, $getfiles=true) {
591 $dirs = array();
593 if (!$getdirs and !$getfiles) { // Nothing to show
594 return $dirs;
597 if (!is_dir($rootdir)) { // Must be a directory
598 return $dirs;
601 if (!$dir = opendir($rootdir)) { // Can't open it for some reason
602 return $dirs;
605 while (false !== ($file = readdir($dir))) {
606 $firstchar = substr($file, 0, 1);
607 if ($firstchar == '.' or $file == 'CVS' or $file == $excludefile) {
608 continue;
610 $fullfile = $rootdir .'/'. $file;
611 if (filetype($fullfile) == 'dir') {
612 if ($getdirs) {
613 $dirs[] = $file;
615 if ($descend) {
616 $subdirs = get_directory_list($fullfile, $excludefile, $descend, $getdirs, $getfiles);
617 foreach ($subdirs as $subdir) {
618 $dirs[] = $file .'/'. $subdir;
621 } else if ($getfiles) {
622 $dirs[] = $file;
625 closedir($dir);
627 asort($dirs);
629 return $dirs;
633 * handy function to loop through an array of files and resolve any filename conflicts
634 * both in the array of filenames and for what is already on disk.
637 function resolve_filename_collisions($destination,$files,$format='%s_%d.%s') {
638 foreach ($files as $k => $f) {
639 if (check_potential_filename($destination,$f,$files)) {
640 $bits = explode('.', $f);
641 for ($i = 1; true; $i++) {
642 $try = sprintf($format, $bits[0], $i, $bits[1]);
643 if (!check_potential_filename($destination,$try,$files)) {
644 $files[$k] = $try;
645 break;
650 return $files;
654 * @used by resolve_filename_collisions
656 function check_potential_filename($destination,$filename,$files) {
657 if (file_exists($destination.'/'.$filename)) {
658 return true;
660 if (count(array_keys($files,$filename)) > 1) {
661 return true;
663 return false;
667 * Adds up all the files in a directory and works out the size.
669 * @param string $rootdir ?
670 * @param string $excludefile ?
671 * @return array
672 * @todo Finish documenting this function
674 function get_directory_size($rootdir, $excludefile='') {
676 global $CFG;
677 $textlib = textlib_get_instance();
679 // do it this way if we can, it's much faster
680 if (!empty($CFG->pathtodu) && is_executable(trim($CFG->pathtodu))) {
681 $command = trim($CFG->pathtodu).' -sk --apparent-size '.escapeshellarg($rootdir);
682 exec($command,$output,$return);
683 if (is_array($output)) {
684 return get_real_size(intval($output[0]).'k'); // we told it to return k.
688 $size = 0;
690 if (!is_dir($rootdir)) { // Must be a directory
691 return $dirs;
694 if (!$dir = @opendir($rootdir)) { // Can't open it for some reason
695 return $dirs;
698 while (false !== ($file = readdir($dir))) {
699 $firstchar = $textlib->substr($file, 0, 1);
700 if ($firstchar == '.' or $file == 'CVS' or $file == $excludefile) {
701 continue;
703 $fullfile = $rootdir .'/'. $file;
704 if (filetype($fullfile) == 'dir') {
705 $size += get_directory_size($fullfile, $excludefile);
706 } else {
707 $size += filesize($fullfile);
710 closedir($dir);
712 return $size;
716 * Converts numbers like 10M into bytes.
718 * @param mixed $size The size to be converted
719 * @return mixed
721 function get_real_size($size=0) {
722 if (!$size) {
723 return 0;
725 $scan['GB'] = 1073741824;
726 $scan['Gb'] = 1073741824;
727 $scan['G'] = 1073741824;
728 $scan['g'] = 1073741824;
729 $scan['MB'] = 1048576;
730 $scan['Mb'] = 1048576;
731 $scan['M'] = 1048576;
732 $scan['m'] = 1048576;
733 $scan['KB'] = 1024;
734 $scan['Kb'] = 1024;
735 $scan['K'] = 1024;
736 $scan['k'] = 1024;
738 while (list($key) = each($scan)) {
739 if ((strlen($size)>strlen($key))&&(substr($size, strlen($size) - strlen($key))==$key)) {
740 $size = substr($size, 0, strlen($size) - strlen($key)) * $scan[$key];
741 break;
744 return $size;
748 * Converts bytes into display form
750 * @param string $size ?
751 * @return string
752 * @staticvar string $gb Localized string for size in gigabytes
753 * @staticvar string $mb Localized string for size in megabytes
754 * @staticvar string $kb Localized string for size in kilobytes
755 * @staticvar string $b Localized string for size in bytes
756 * @todo Finish documenting this function. Verify return type.
758 function display_size($size) {
760 static $gb, $mb, $kb, $b;
762 if (empty($gb)) {
763 $gb = __gettext('GB');
764 $mb = __gettext('MB');
765 $kb = __gettext('KB');
766 $b = __gettext('bytes');
769 if ($size >= 1073741824) {
770 $size = round($size / 1073741824 * 10) / 10 . $gb;
771 } else if ($size >= 1048576) {
772 $size = round($size / 1048576 * 10) / 10 . $mb;
773 } else if ($size >= 1024) {
774 $size = round($size / 1024 * 10) / 10 . $kb;
775 } else {
776 $size = $size .' '. $b;
778 return $size;
782 * Convert high ascii characters into low ascii
783 * This code is from http://kalsey.com/2004/07/dirify_in_php/
786 function convert_high_ascii($s) {
787 $HighASCII = array(
788 "!\xc0!" => 'A', # A`
789 "!\xe0!" => 'a', # a`
790 "!\xc1!" => 'A', # A'
791 "!\xe1!" => 'a', # a'
792 "!\xc2!" => 'A', # A^
793 "!\xe2!" => 'a', # a^
794 "!\xc4!" => 'Ae', # A:
795 "!\xe4!" => 'ae', # a:
796 "!\xc3!" => 'A', # A~
797 "!\xe3!" => 'a', # a~
798 "!\xc8!" => 'E', # E`
799 "!\xe8!" => 'e', # e`
800 "!\xc9!" => 'E', # E'
801 "!\xe9!" => 'e', # e'
802 "!\xca!" => 'E', # E^
803 "!\xea!" => 'e', # e^
804 "!\xcb!" => 'Ee', # E:
805 "!\xeb!" => 'ee', # e:
806 "!\xcc!" => 'I', # I`
807 "!\xec!" => 'i', # i`
808 "!\xcd!" => 'I', # I'
809 "!\xed!" => 'i', # i'
810 "!\xce!" => 'I', # I^
811 "!\xee!" => 'i', # i^
812 "!\xcf!" => 'Ie', # I:
813 "!\xef!" => 'ie', # i:
814 "!\xd2!" => 'O', # O`
815 "!\xf2!" => 'o', # o`
816 "!\xd3!" => 'O', # O'
817 "!\xf3!" => 'o', # o'
818 "!\xd4!" => 'O', # O^
819 "!\xf4!" => 'o', # o^
820 "!\xd6!" => 'Oe', # O:
821 "!\xf6!" => 'oe', # o:
822 "!\xd5!" => 'O', # O~
823 "!\xf5!" => 'o', # o~
824 "!\xd8!" => 'Oe', # O/
825 "!\xf8!" => 'oe', # o/
826 "!\xd9!" => 'U', # U`
827 "!\xf9!" => 'u', # u`
828 "!\xda!" => 'U', # U'
829 "!\xfa!" => 'u', # u'
830 "!\xdb!" => 'U', # U^
831 "!\xfb!" => 'u', # u^
832 "!\xdc!" => 'Ue', # U:
833 "!\xfc!" => 'ue', # u:
834 "!\xc7!" => 'C', # ,C
835 "!\xe7!" => 'c', # ,c
836 "!\xd1!" => 'N', # N~
837 "!\xf1!" => 'n', # n~
838 "!\xdf!" => 'ss'
840 $find = array_keys($HighASCII);
841 $replace = array_values($HighASCII);
842 $s = preg_replace($find,$replace,$s);
843 return $s;
847 * Cleans a given filename by removing suspicious or troublesome characters
848 * Only these are allowed:
849 * alphanumeric _ - .
851 * @param string $string ?
852 * @return string
854 function clean_filename($string) {
855 $string = convert_high_ascii($string);
856 $string = eregi_replace("\.\.+", '', $string);
857 $string = preg_replace('/[^\.a-zA-Z\d\_-]/','_', $string ); // only allowed chars
858 $string = eregi_replace("_+", '_', $string);
859 return $string;
865 * Function to raise the memory limit to a new value.
866 * Will respect the memory limit if it is higher, thus allowing
867 * settings in php.ini, apache conf or command line switches
868 * to override it
870 * The memory limit should be expressed with a string (eg:'64M')
872 * @param string $newlimit the new memory limit
873 * @return bool
875 function raise_memory_limit ($newlimit) {
877 if (empty($newlimit)) {
878 return false;
881 $cur = @ini_get('memory_limit');
882 if (empty($cur)) {
883 // if php is compiled without --enable-memory-limits
884 // apparently memory_limit is set to ''
885 $cur=0;
886 } else {
887 if ($cur == -1){
888 return true; // unlimited mem!
890 $cur = get_real_size($cur);
893 $new = get_real_size($newlimit);
894 if ($new > $cur) {
895 ini_set('memory_limit', $newlimit);
896 return true;
898 return false;
902 * Converts string to lowercase using most compatible function available.
904 * @param string $string The string to convert to all lowercase characters.
905 * @param string $encoding The encoding on the string.
906 * @return string
907 * @todo Add examples of calling this function with/without encoding types
909 function elgg_strtolower ($string, $encoding='') {
910 $textlib = textlib_get_instance();
911 return $textlib->strtolower($string, $encoding?$encoding:'utf-8');
917 * Given a simple array, this shuffles it up just like shuffle()
918 * Unlike PHP's shuffle() ihis function works on any machine.
920 * @param array $array The array to be rearranged
921 * @return array
923 function swapshuffle($array) {
925 srand ((double) microtime() * 10000000);
926 $last = count($array) - 1;
927 for ($i=0;$i<=$last;$i++) {
928 $from = rand(0,$last);
929 $curr = $array[$i];
930 $array[$i] = $array[$from];
931 $array[$from] = $curr;
933 return $array;
937 * Like {@link swapshuffle()}, but works on associative arrays
939 * @param array $array The associative array to be rearranged
940 * @return array
942 function swapshuffle_assoc($array) {
945 $newkeys = swapshuffle(array_keys($array));
946 foreach ($newkeys as $newkey) {
947 $newarray[$newkey] = $array[$newkey];
949 return $newarray;
953 * Given an arbitrary array, and a number of draws,
954 * this function returns an array with that amount
955 * of items. The indexes are retained.
957 * @param array $array ?
958 * @param ? $draws ?
959 * @return ?
960 * @todo Finish documenting this function
962 function draw_rand_array($array, $draws) {
963 srand ((double) microtime() * 10000000);
965 $return = array();
967 $last = count($array);
969 if ($draws > $last) {
970 $draws = $last;
973 while ($draws > 0) {
974 $last--;
976 $keys = array_keys($array);
977 $rand = rand(0, $last);
979 $return[$keys[$rand]] = $array[$keys[$rand]];
980 unset($array[$keys[$rand]]);
982 $draws--;
985 return $return;
990 * Function to check the passed address is within the passed subnet
992 * The parameter is a comma separated string of subnet definitions.
993 * Subnet strings can be in one of two formats:
994 * 1: xxx.xxx.xxx.xxx/xx
995 * 2: xxx.xxx
996 * Code for type 1 modified from user posted comments by mediator at
997 * {@link http://au.php.net/manual/en/function.ip2long.php}
999 * @param string $addr The address you are checking
1000 * @param string $subnetstr The string of subnet addresses
1001 * @return bool
1003 function address_in_subnet($addr, $subnetstr) {
1005 $subnets = explode(',', $subnetstr);
1006 $found = false;
1007 $addr = trim($addr);
1009 foreach ($subnets as $subnet) {
1010 $subnet = trim($subnet);
1011 if (strpos($subnet, '/') !== false) { /// type 1
1013 list($ip, $mask) = explode('/', $subnet);
1014 $mask = 0xffffffff << (32 - $mask);
1015 $found = ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
1017 } else { /// type 2
1018 $found = (strpos($addr, $subnet) === 0);
1021 if ($found) {
1022 break;
1026 return $found;
1030 * For outputting debugging info
1032 * @uses STDOUT
1033 * @param string $string ?
1034 * @param string $eol ?
1035 * @todo Finish documenting this function
1037 function mtrace($string, $eol="\n", $sleep=0) {
1039 if (defined('STDOUT')) {
1040 fwrite(STDOUT, $string.$eol);
1041 } else {
1042 echo $string . $eol;
1045 flush();
1047 //delay to keep message on user's screen in case of subsequent redirect
1048 if ($sleep) {
1049 sleep($sleep);
1053 //Replace 1 or more slashes or backslashes to 1 slash
1054 function cleardoubleslashes ($path) {
1055 return preg_replace('/(\/|\\\){1,}/','/',$path);
1061 * Returns most reliable client address
1063 * @return string The remote IP address
1065 function getremoteaddr() {
1066 if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
1067 return cleanremoteaddr($_SERVER['HTTP_CLIENT_IP']);
1069 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
1070 return cleanremoteaddr($_SERVER['HTTP_X_FORWARDED_FOR']);
1072 if (!empty($_SERVER['REMOTE_ADDR'])) {
1073 return cleanremoteaddr($_SERVER['REMOTE_ADDR']);
1075 return '';
1078 /**
1079 * Cleans a remote address ready to put into the log table
1081 function cleanremoteaddr($addr) {
1082 $originaladdr = $addr;
1083 $matches = array();
1084 // first get all things that look like IP addresses.
1085 if (!preg_match_all('/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/',$addr,$matches,PREG_SET_ORDER)) {
1086 return '';
1088 $goodmatches = array();
1089 $lanmatches = array();
1090 foreach ($matches as $match) {
1091 // print_r($match);
1092 // check to make sure it's not an internal address.
1093 // the following are reserved for private lans...
1094 // 10.0.0.0 - 10.255.255.255
1095 // 172.16.0.0 - 172.31.255.255
1096 // 192.168.0.0 - 192.168.255.255
1097 // 169.254.0.0 -169.254.255.255
1098 $bits = explode('.',$match[0]);
1099 if (count($bits) != 4) {
1100 // weird, preg match shouldn't give us it.
1101 continue;
1103 if (($bits[0] == 10)
1104 || ($bits[0] == 172 && $bits[1] >= 16 && $bits[1] <= 31)
1105 || ($bits[0] == 192 && $bits[1] == 168)
1106 || ($bits[0] == 169 && $bits[1] == 254)) {
1107 $lanmatches[] = $match[0];
1108 continue;
1110 // finally, it's ok
1111 $goodmatches[] = $match[0];
1113 if (!count($goodmatches)) {
1114 // perhaps we have a lan match, it's probably better to return that.
1115 if (!count($lanmatches)) {
1116 return '';
1117 } else {
1118 return array_pop($lanmatches);
1121 if (count($goodmatches) == 1) {
1122 return $goodmatches[0];
1124 error_log("NOTICE: cleanremoteaddr gives us something funny: $originaladdr had ".count($goodmatches)." matches");
1125 // we need to return something, so
1126 return array_pop($goodmatches);
1130 * html_entity_decode is only supported by php 4.3.0 and higher
1131 * so if it is not predefined, define it here
1133 * @param string $string ?
1134 * @return string
1135 * @todo Finish documenting this function
1137 if(!function_exists('html_entity_decode')) {
1138 function html_entity_decode($string, $quote_style = ENT_COMPAT, $charset = 'ISO-8859-1') {
1139 $trans_tbl = get_html_translation_table(HTML_ENTITIES, $quote_style);
1140 $trans_tbl = array_flip($trans_tbl);
1141 return strtr($string, $trans_tbl);
1146 * The clone keyword is only supported from PHP 5 onwards.
1147 * The behaviour of $obj2 = $obj1 differs fundamentally
1148 * between PHP 4 and PHP 5. In PHP 4 a copy of $obj1 was
1149 * created, in PHP 5 $obj1 is referenced. To create a copy
1150 * in PHP 5 the clone keyword was introduced. This function
1151 * simulates this behaviour for PHP < 5.0.0.
1152 * See also: http://mjtsai.com/blog/2004/07/15/php-5-object-references/
1154 * Modified 2005-09-29 by Eloy (from Julian Sedding proposal)
1155 * Found a better implementation (more checks and possibilities) from PEAR:
1156 * http://cvs.php.net/co.php/pear/PHP_Compat/Compat/Function/clone.php
1158 * @param object $obj
1159 * @return object
1161 if(!check_php_version('5.0.0')) {
1162 // the eval is needed to prevent PHP 5 from getting a parse error!
1163 eval('
1164 function clone($obj) {
1165 /// Sanity check
1166 if (!is_object($obj)) {
1167 user_error(\'clone() __clone method called on non-object\', E_USER_WARNING);
1168 return;
1171 /// Use serialize/unserialize trick to deep copy the object
1172 $obj = unserialize(serialize($obj));
1174 /// If there is a __clone method call it on the "new" class
1175 if (method_exists($obj, \'__clone\')) {
1176 $obj->__clone();
1179 return $obj;
1186 * microtime_diff
1188 * @param string $a ?
1189 * @param string $b ?
1190 * @return string
1191 * @todo Finish documenting this function
1193 function microtime_diff($a, $b) {
1194 list($a_dec, $a_sec) = explode(' ', $a);
1195 list($b_dec, $b_sec) = explode(' ', $b);
1196 return $b_sec - $a_sec + $b_dec - $a_dec;
1201 *** get_performance_info() pairs up with init_performance_info()
1202 *** loaded in setup.php. Returns an array with 'html' and 'txt'
1203 *** values ready for use, and each of the individual stats provided
1204 *** separately as well.
1207 function get_performance_info() {
1208 global $CFG, $PERF;
1210 $info = array();
1211 $info['html'] = ''; // holds userfriendly HTML representation
1212 $info['txt'] = me() . ' '; // holds log-friendly representation
1214 $info['realtime'] = microtime_diff($PERF->starttime, microtime());
1216 $info['html'] .= '<span class="timeused">'.$info['realtime'].' secs</span> ';
1217 $info['txt'] .= 'time: '.$info['realtime'].'s ';
1219 if (function_exists('memory_get_usage')) {
1220 $info['memory_total'] = memory_get_usage();
1221 $info['memory_growth'] = memory_get_usage() - $PERF->startmemory;
1222 $info['html'] .= '<span class="memoryused">RAM: '.display_size($info['memory_total']).'</span> ';
1223 $info['txt'] .= 'memory_total: '.$info['memory_total'].'B (' . display_size($info['memory_total']).') memory_growth: '.$info['memory_growth'].'B ('.display_size($info['memory_growth']).') ';
1226 $inc = get_included_files();
1227 //error_log(print_r($inc,1));
1228 $info['includecount'] = count($inc);
1229 $info['html'] .= '<span class="included">Included '.$info['includecount'].' files</span> ';
1230 $info['txt'] .= 'includecount: '.$info['includecount'].' ';
1232 if (!empty($PERF->dbqueries)) {
1233 $info['dbqueries'] = $PERF->dbqueries;
1234 $info['html'] .= '<span class="dbqueries">DB queries '.$info['dbqueries'].'</span> ';
1235 $info['txt'] .= 'dbqueries: '.$info['dbqueries'].' ';
1238 if (!empty($PERF->logwrites)) {
1239 $info['logwrites'] = $PERF->logwrites;
1240 $info['html'] .= '<span class="logwrites">Log writes '.$info['logwrites'].'</span> ';
1241 $info['txt'] .= 'logwrites: '.$info['logwrites'].' ';
1244 if (function_exists('posix_times')) {
1245 $ptimes = posix_times();
1246 if ($ptimes) {
1247 foreach ($ptimes as $key => $val) {
1248 $info[$key] = $ptimes[$key] - $PERF->startposixtimes[$key];
1250 $info['html'] .= "<span class=\"posixtimes\">ticks: $info[ticks] user: $info[utime] sys: $info[stime] cuser: $info[cutime] csys: $info[cstime]</span> ";
1251 $info['txt'] .= "ticks: $info[ticks] user: $info[utime] sys: $info[stime] cuser: $info[cutime] csys: $info[cstime] ";
1255 // Grab the load average for the last minute
1256 // /proc will only work under some linux configurations
1257 // while uptime is there under MacOSX/Darwin and other unices
1258 if (is_readable('/proc/loadavg') && $loadavg = @file('/proc/loadavg')) {
1259 list($server_load) = explode(' ', $loadavg[0]);
1260 unset($loadavg);
1261 } else if ( function_exists('is_executable') && is_executable('/usr/bin/uptime') && $loadavg = `/usr/bin/uptime` ) {
1262 if (preg_match('/load averages?: (\d+[\.:]\d+)/', $loadavg, $matches)) {
1263 $server_load = $matches[1];
1264 } else {
1265 trigger_error('Could not parse uptime output!');
1268 if (!empty($server_load)) {
1269 $info['serverload'] = $server_load;
1270 $info['html'] .= '<span class="serverload">Load average: '.$info['serverload'].'</span> ';
1271 $info['txt'] .= 'serverload: '.$info['serverload'];
1275 $info['html'] = '<div class="performanceinfo">'.$info['html'].'</div>';
1276 return $info;
1279 if (!function_exists('file_get_contents')) {
1280 function file_get_contents($file) {
1281 $file = file($file);
1282 return $file ? implode('', $file) : false;
1290 * Detect if an object or a class contains a given property
1291 * will take an actual object or the name of a class
1292 * @param mix $obj Name of class or real object to test
1293 * @param string $property name of property to find
1294 * @return bool true if property exists
1296 function object_property_exists( $obj, $property ) {
1297 if (is_string( $obj )) {
1298 $properties = get_class_vars( $obj );
1300 else {
1301 $properties = get_object_vars( $obj );
1303 return array_key_exists( $property, $properties );
1308 * Add quotes to HTML characters
1310 * Returns $var with HTML characters (like "<", ">", etc.) properly quoted.
1311 * This function is very similar to {@link p()}
1313 * @param string $var the string potentially containing HTML characters
1314 * @return string
1316 function s($var) {
1317 if ($var == '0') { // for integer 0, boolean false, string '0'
1318 return '0';
1320 return preg_replace("/&amp;(#\d+);/iu", '&$1;', htmlspecialchars(stripslashes_safe($var), ENT_COMPAT, 'utf-8'));
1324 * Add quotes to HTML characters
1326 * Prints $var with HTML characters (like "<", ">", etc.) properly quoted.
1327 * This function is very similar to {@link s()}
1329 * @param string $var the string potentially containing HTML characters
1330 * @return string
1332 function p($var) {
1333 echo s($var);
1338 * Ensure that a variable is set
1340 * Return $var if it is defined, otherwise return $default,
1341 * This function is very similar to {@link optional_variable()}
1343 * @param mixed $var the variable which may be unset
1344 * @param mixed $default the value to return if $var is unset
1345 * @return mixed
1347 function nvl(&$var, $default='') {
1349 return isset($var) ? $var : $default;
1353 * Remove query string from url
1355 * Takes in a URL and returns it without the querystring portion
1357 * @param string $url the url which may have a query string attached
1358 * @return string
1360 function strip_querystring($url) {
1361 $textlib = textlib_get_instance();
1363 if ($commapos = $textlib->strpos($url, '?')) {
1364 return $textlib->substr($url, 0, $commapos);
1365 } else {
1366 return $url;
1371 * Returns the URL of the HTTP_REFERER, less the querystring portion
1372 * @return string
1374 function get_referer() {
1376 return strip_querystring(nvl($_SERVER['HTTP_REFERER']));
1381 * Returns the name of the current script, WITH the querystring portion.
1382 * this function is necessary because PHP_SELF and REQUEST_URI and SCRIPT_NAME
1383 * return different things depending on a lot of things like your OS, Web
1384 * server, and the way PHP is compiled (ie. as a CGI, module, ISAPI, etc.)
1385 * <b>NOTE:</b> This function returns false if the global variables needed are not set.
1387 * @return string
1389 function me() {
1391 if (!empty($_SERVER['REQUEST_URI'])) {
1392 return $_SERVER['REQUEST_URI'];
1394 } else if (!empty($_SERVER['PHP_SELF'])) {
1395 if (!empty($_SERVER['QUERY_STRING'])) {
1396 return $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING'];
1398 return $_SERVER['PHP_SELF'];
1400 } else if (!empty($_SERVER['SCRIPT_NAME'])) {
1401 if (!empty($_SERVER['QUERY_STRING'])) {
1402 return $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
1404 return $_SERVER['SCRIPT_NAME'];
1406 } else if (!empty($_SERVER['URL'])) { // May help IIS (not well tested)
1407 if (!empty($_SERVER['QUERY_STRING'])) {
1408 return $_SERVER['URL'] .'?'. $_SERVER['QUERY_STRING'];
1410 return $_SERVER['URL'];
1412 } else {
1413 notify('Warning: Could not find any of these web server variables: $REQUEST_URI, $PHP_SELF, $SCRIPT_NAME or $URL');
1414 return false;
1419 * Like {@link me()} but returns a full URL
1420 * @see me()
1421 * @return string
1423 function qualified_me() {
1425 global $CFG;
1427 if (!empty($CFG->wwwroot)) {
1428 $url = parse_url($CFG->wwwroot);
1431 if (!empty($url['host'])) {
1432 $hostname = $url['host'];
1433 } else if (!empty($_SERVER['SERVER_NAME'])) {
1434 $hostname = $_SERVER['SERVER_NAME'];
1435 } else if (!empty($_ENV['SERVER_NAME'])) {
1436 $hostname = $_ENV['SERVER_NAME'];
1437 } else if (!empty($_SERVER['HTTP_HOST'])) {
1438 $hostname = $_SERVER['HTTP_HOST'];
1439 } else if (!empty($_ENV['HTTP_HOST'])) {
1440 $hostname = $_ENV['HTTP_HOST'];
1441 } else {
1442 notify('Warning: could not find the name of this server!');
1443 return false;
1446 if (!empty($url['port'])) {
1447 $hostname .= ':'.$url['port'];
1448 } else if (!empty($_SERVER['SERVER_PORT'])) {
1449 if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
1450 $hostname .= ':'.$_SERVER['SERVER_PORT'];
1454 if (isset($_SERVER['HTTPS'])) {
1455 $protocol = ($_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
1456 } else if (isset($_SERVER['SERVER_PORT'])) { # Apache2 does not export $_SERVER['HTTPS']
1457 $protocol = ($_SERVER['SERVER_PORT'] == '443') ? 'https://' : 'http://';
1458 } else {
1459 $protocol = 'http://';
1462 $url_prefix = $protocol.$hostname;
1463 return $url_prefix . me();
1467 * Determine if a web referer is valid
1469 * Returns true if the referer is the same as the goodreferer. If
1470 * the referer to test is not specified, use {@link qualified_me()}.
1471 * If the admin has not set secure forms ($CFG->secureforms) then
1472 * this function returns true regardless of a match.
1474 * @uses $CFG
1475 * @param string $goodreferer the url to compare to referer
1476 * @return boolean
1478 function match_referer($goodreferer = '') {
1479 global $CFG;
1481 if (empty($CFG->secureforms)) { // Don't bother checking referer
1482 return true;
1485 if ($goodreferer == 'nomatch') { // Don't bother checking referer
1486 return true;
1489 if (empty($goodreferer)) {
1490 $goodreferer = qualified_me();
1493 $referer = get_referer();
1495 return (($referer == $goodreferer) or ($referer == $CFG->wwwroot) or ($referer == $CFG->wwwroot .'index.php'));
1499 * Determine if there is data waiting to be processed from a form
1501 * Used on most forms in Moodle to check for data
1502 * Returns the data as an object, if it's found.
1503 * This object can be used in foreach loops without
1504 * casting because it's cast to (array) automatically
1506 * Checks that submitted POST data exists, and also
1507 * checks the referer against the given url (it uses
1508 * the current page if none was specified.
1510 * @uses $CFG
1511 * @param string $url the url to compare to referer for secure forms
1512 * @return boolean
1514 function data_submitted($url='') {
1517 global $CFG;
1519 if (empty($_POST)) {
1520 return false;
1522 } else {
1523 if (match_referer($url)) {
1524 return (object)$_POST;
1525 } else {
1526 if ($CFG->debug > 10) {
1527 notice('The form did not come from this page! (referer = '. get_referer() .')');
1529 return false;
1535 * Moodle replacement for php stripslashes() function
1537 * The standard php stripslashes() removes ALL backslashes
1538 * even from strings - so C:\temp becomes C:temp - this isn't good.
1539 * This function should work as a fairly safe replacement
1540 * to be called on quoted AND unquoted strings (to be sure)
1542 * @param string the string to remove unsafe slashes from
1543 * @return string
1545 function stripslashes_safe($string) {
1547 $string = str_replace("\\'", "'", $string);
1548 $string = str_replace('\\"', '"', $string);
1549 $string = str_replace('\\\\', '\\', $string);
1550 return $string;
1554 * Recursive implementation of stripslashes()
1556 * This function will allow you to strip the slashes from a variable.
1557 * If the variable is an array or object, slashes will be stripped
1558 * from the items (or properties) it contains, even if they are arrays
1559 * or objects themselves.
1561 * @param mixed the variable to remove slashes from
1562 * @return mixed
1564 function stripslashes_recursive($var) {
1565 if(is_object($var)) {
1566 $properties = get_object_vars($var);
1567 foreach($properties as $property => $value) {
1568 $var->$property = stripslashes_recursive($value);
1571 else if(is_array($var)) {
1572 foreach($var as $property => $value) {
1573 $var[$property] = stripslashes_recursive($value);
1576 else if(is_string($var)) {
1577 $var = stripslashes($var);
1579 return $var;
1583 * This does a search and replace, ignoring case
1584 * This function is only used for versions of PHP older than version 5
1585 * which do not have a native version of this function.
1586 * Taken from the PHP manual, by bradhuizenga @ softhome.net
1588 * @param string $find the string to search for
1589 * @param string $replace the string to replace $find with
1590 * @param string $string the string to search through
1591 * return string
1593 if (!function_exists('str_ireplace')) { /// Only exists in PHP 5
1594 function str_ireplace($find, $replace, $string) {
1595 $textlib = textlib_get_instance();
1597 if (!is_array($find)) {
1598 $find = array($find);
1601 if(!is_array($replace)) {
1602 if (!is_array($find)) {
1603 $replace = array($replace);
1604 } else {
1605 // this will duplicate the string into an array the size of $find
1606 $c = count($find);
1607 $rString = $replace;
1608 unset($replace);
1609 for ($i = 0; $i < $c; $i++) {
1610 $replace[$i] = $rString;
1615 foreach ($find as $fKey => $fItem) {
1616 $between = explode($textlib->strtolower($fItem),$textlib->strtolower($string));
1617 $pos = 0;
1618 foreach($between as $bKey => $bItem) {
1619 $between[$bKey] = $textlib->substr($string,$pos,$textlib->strlen($bItem));
1620 $pos += $textlib->strlen($bItem) + $textlib->strlen($fItem);
1622 $string = implode($replace[$fKey],$between);
1624 return ($string);
1629 * Locate the position of a string in another string
1631 * This function is only used for versions of PHP older than version 5
1632 * which do not have a native version of this function.
1633 * Taken from the PHP manual, by dmarsh @ spscc.ctc.edu
1635 * @param string $haystack The string to be searched
1636 * @param string $needle The string to search for
1637 * @param int $offset The position in $haystack where the search should begin.
1639 if (!function_exists('stripos')) { /// Only exists in PHP 5
1640 function stripos($haystack, $needle, $offset=0) {
1641 $textlib = textlib_get_instance();
1643 return $textlib->strpos($textlib->strtoupper($haystack), $textlib->strtoupper($needle), $offset);
1649 * Returns true if the current version of PHP is greater that the specified one.
1651 * @param string $version The version of php being tested.
1652 * @return boolean
1653 * @todo Finish documenting this function
1655 function check_php_version($version='4.1.0') {
1656 return (version_compare(phpversion(), $version) >= 0);
1661 * Checks to see if is a browser matches the specified
1662 * brand and is equal or better version.
1664 * @uses $_SERVER
1665 * @param string $brand The browser identifier being tested
1666 * @param int $version The version of the browser
1667 * @return boolean
1668 * @todo Finish documenting this function
1670 function check_browser_version($brand='MSIE', $version=5.5) {
1671 $agent = $_SERVER['HTTP_USER_AGENT'];
1673 if (empty($agent)) {
1674 return false;
1677 switch ($brand) {
1679 case 'Gecko': /// Gecko based browsers
1681 if (substr_count($agent, 'Camino')) {
1682 // MacOS X Camino support
1683 $version = 20041110;
1686 // the proper string - Gecko/CCYYMMDD Vendor/Version
1687 // Faster version and work-a-round No IDN problem.
1688 if (preg_match("/Gecko\/([0-9]+)/i", $agent, $match)) {
1689 if ($match[1] > $version) {
1690 return true;
1693 break;
1696 case 'MSIE': /// Internet Explorer
1698 if (strpos($agent, 'Opera')) { // Reject Opera
1699 return false;
1701 $string = explode(';', $agent);
1702 if (!isset($string[1])) {
1703 return false;
1705 $string = explode(' ', trim($string[1]));
1706 if (!isset($string[0]) and !isset($string[1])) {
1707 return false;
1709 if ($string[0] == $brand and (float)$string[1] >= $version ) {
1710 return true;
1712 break;
1716 return false;
1721 * Set a variable's value depending on whether or not it already has a value.
1723 * If variable is set, set it to the set_value otherwise set it to the
1724 * unset_value. used to handle checkboxes when you are expecting them from
1725 * a form
1727 * @param mixed $var Passed in by reference. The variable to check.
1728 * @param mixed $set_value The value to set $var to if $var already has a value.
1729 * @param mixed $unset_value The value to set $var to if $var does not already have a value.
1731 function checked(&$var, $set_value = 1, $unset_value = 0) {
1733 if (empty($var)) {
1734 $var = $unset_value;
1735 } else {
1736 $var = $set_value;
1741 * Prints the word "checked" if a variable is true, otherwise prints nothing,
1742 * used for printing the word "checked" in a checkbox form element.
1744 * @param boolean $var Variable to be checked for true value
1745 * @param string $true_value Value to be printed if $var is true
1746 * @param string $false_value Value to be printed if $var is false
1748 function frmchecked(&$var, $true_value = 'checked', $false_value = '') {
1750 if ($var) {
1751 echo $true_value;
1752 } else {
1753 echo $false_value;
1758 * Prints a simple button to close a window
1760 function close_window_button($name='closewindow') {
1762 echo '<div style="text-align: center;">' . "\n";
1763 echo '<script type="text/javascript">' . "\n";
1764 echo '<!--' . "\n";
1765 echo "document.write('<form>');\n";
1766 echo "document.write('<input type=\"button\" onclick=\"self.close();\" value=\"".__gettext("Close this window")."\" />');\n";
1767 echo "document.write('<\/form>');\n";
1768 echo '-->' . "\n";
1769 echo '</script>' . "\n";
1770 echo '<noscript>' . "\n";
1771 print_string($name);
1772 echo '</noscript>' . "\n";
1773 echo '</div>' . "\n";
1777 * Try and close the current window immediately using Javascript
1779 function close_window($delay=0) {
1780 echo '<script language="JavaScript" type="text/javascript">'."\n";
1781 echo '<!--'."\n";
1782 if ($delay) {
1783 sleep($delay);
1785 echo 'self.close();'."\n";
1786 echo '-->'."\n";
1787 echo '</script>'."\n";
1788 exit;
1793 * Given an array of value, creates a popup menu to be part of a form
1794 * $options["value"]["label"]
1796 * @param type description
1797 * @todo Finish documenting this function
1799 function choose_from_menu ($options, $name, $selected='', $nothing='choose', $script='',
1800 $nothingvalue='0', $return=false, $disabled=false, $tabindex=0) {
1802 if ($nothing == 'choose') {
1803 $nothing = __gettext('Choose') .'...';
1806 $attributes = ($script) ? 'onchange="'. $script .'"' : '';
1807 if ($disabled) {
1808 $attributes .= ' disabled="disabled"';
1811 if ($tabindex) {
1812 $attributes .= ' tabindex="'.$tabindex.'"';
1815 $output = '<select id="menu'.$name.'" name="'. $name .'" '. $attributes .'>' . "\n";
1816 if ($nothing) {
1817 $output .= ' <option value="'. $nothingvalue .'"'. "\n";
1818 if ($nothingvalue === $selected) {
1819 $output .= ' selected="selected"';
1821 $output .= '>'. $nothing .'</option>' . "\n";
1823 if (!empty($options)) {
1824 foreach ($options as $value => $label) {
1825 $output .= ' <option value="'. $value .'"';
1826 if ($value === $selected) {
1827 $output .= ' selected="selected"';
1829 if ($label === '') {
1830 $output .= '>'. $value .'</option>' . "\n";
1831 } else {
1832 $output .= '>'. $label .'</option>' . "\n";
1836 $output .= '</select>' . "\n";
1838 if ($return) {
1839 return $output;
1840 } else {
1841 echo $output;
1846 * Just like choose_from_menu, but takes a nested array (2 levels) and makes a dropdown menu
1847 * including option headings with the first level.
1849 function choose_from_menu_nested($options,$name,$selected='',$nothing='choose',$script = '',
1850 $nothingvalue=0,$return=false,$disabled=false,$tabindex=0) {
1852 if ($nothing == 'choose') {
1853 $nothing = __gettext('Choose') .'...';
1856 $attributes = ($script) ? 'onchange="'. $script .'"' : '';
1857 if ($disabled) {
1858 $attributes .= ' disabled="disabled"';
1861 if ($tabindex) {
1862 $attributes .= ' tabindex="'.$tabindex.'"';
1865 $output = '<select id="menu'.$name.'" name="'. $name .'" '. $attributes .'>' . "\n";
1866 if ($nothing) {
1867 $output .= ' <option value="'. $nothingvalue .'"'. "\n";
1868 if ($nothingvalue === $selected) {
1869 $output .= ' selected="selected"';
1871 $output .= '>'. $nothing .'</option>' . "\n";
1873 if (!empty($options)) {
1874 foreach ($options as $section => $values) {
1875 $output .= ' <optgroup label="'.$section.'">'."\n";
1876 foreach ($values as $value => $label) {
1877 $output .= ' <option value="'. $value .'"';
1878 if ($value === $selected) {
1879 $output .= ' selected="selected"';
1881 if ($label === '') {
1882 $output .= '>'. $value .'</option>' . "\n";
1883 } else {
1884 $output .= '>'. $label .'</option>' . "\n";
1887 $output .= ' </optgroup>'."\n";
1890 $output .= '</select>' . "\n";
1892 if ($return) {
1893 return $output;
1894 } else {
1895 echo $output;
1901 * Given an array of values, creates a group of radio buttons to be part of a form
1903 * @param array $options An array of value-label pairs for the radio group (values as keys)
1904 * @param string $name Name of the radiogroup (unique in the form)
1905 * @param string $checked The value that is already checked
1907 function choose_from_radio ($options, $name, $checked='') {
1909 static $idcounter = 0;
1911 if (!$name) {
1912 $name = 'unnamed';
1915 $output = '<span class="radiogroup '.$name."\">\n";
1917 if (!empty($options)) {
1918 $currentradio = 0;
1919 foreach ($options as $value => $label) {
1920 $htmlid = 'auto-rb'.sprintf('%04d', ++$idcounter);
1921 $output .= ' <span class="radioelement '.$name.' rb'.$currentradio."\">";
1922 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="radio" value="'.$value.'"';
1923 if ($value == $checked) {
1924 $output .= ' checked="checked"';
1926 if ($label === '') {
1927 $output .= ' /> <label for="'.$htmlid.'">'. $value .'</label></span>' . "\n";
1928 } else {
1929 $output .= ' /> <label for="'.$htmlid.'">'. $label .'</label></span>' . "\n";
1931 $currentradio = ($currentradio + 1) % 2;
1935 $output .= '</span>' . "\n";
1937 echo $output;
1940 /** Display an standard html checkbox with an optional label
1942 * @param string $name The name of the checkbox
1943 * @param string $value The valus that the checkbox will pass when checked
1944 * @param boolean $checked The flag to tell the checkbox initial state
1945 * @param string $label The label to be showed near the checkbox
1946 * @param string $alt The info to be inserted in the alt tag
1948 function print_checkbox ($name, $value, $checked = true, $label = '', $alt = '', $script='',$return=false) {
1950 static $idcounter = 0;
1952 if (!$name) {
1953 $name = 'unnamed';
1956 if (!$alt) {
1957 $alt = 'checkbox';
1960 if ($checked) {
1961 $strchecked = ' checked="checked"';
1964 $htmlid = 'auto-cb'.sprintf('%04d', ++$idcounter);
1965 $output = '<span class="checkbox '.$name."\">";
1966 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="checkbox" value="'.$value.'" alt="'.$alt.'"'.$strchecked.' '.((!empty($script)) ? ' onclick="'.$script.'" ' : '').' />';
1967 if(!empty($label)) {
1968 $output .= ' <label for="'.$htmlid.'">'.$label.'</label>';
1970 $output .= '</span>'."\n";
1972 if (empty($return)) {
1973 echo $output;
1974 } else {
1975 return $output;
1980 /** Display an standard html text field with an optional label
1982 * @param string $name The name of the text field
1983 * @param string $value The value of the text field
1984 * @param string $label The label to be showed near the text field
1985 * @param string $alt The info to be inserted in the alt tag
1987 function print_textfield ($name, $value, $alt = '',$size=50,$maxlength= 0,$return=false) {
1989 static $idcounter = 0;
1991 if (empty($name)) {
1992 $name = 'unnamed';
1995 if (empty($alt)) {
1996 $alt = 'textfield';
1999 if (!empty($maxlength)) {
2000 $maxlength = ' maxlength="'.$maxlength.'" ';
2003 $htmlid = 'auto-cb'.sprintf('%04d', ++$idcounter);
2004 $output = '<span class="textfield '.$name."\">";
2005 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="text" value="'.$value.'" size="'.$size.'" '.$maxlength.' alt="'.$alt.'" />';
2007 $output .= '</span>'."\n";
2009 if (empty($return)) {
2010 echo $output;
2011 } else {
2012 return $output;
2019 * Validates an email to make sure it makes sense and adheres
2020 * to the email filter if it's set.
2022 * @param string $address The email address to validate.
2023 * @return boolean
2025 function validate_email($address) {
2027 global $CFG;
2029 if (ereg('^[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+'.
2030 '@'.
2031 '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'.
2032 '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$',
2033 $address)) {
2035 if ($CFG->emailfilter != "") {
2036 $domain = substr($address,strpos($address,"@")+1);
2037 if (substr_count($CFG->emailfilter, $domain) == 0) {
2038 return false;
2042 return true;
2044 } else {
2045 return false;
2050 * Check for bad characters ?
2052 * @param string $string ?
2053 * @param int $allowdots ?
2054 * @todo Finish documenting this function - more detail needed in description as well as details on arguments
2056 function detect_munged_arguments($string, $allowdots=1) {
2057 if (substr_count($string, '..') > $allowdots) { // Sometimes we allow dots in references
2058 return true;
2060 if (ereg('[\|\`]', $string)) { // check for other bad characters
2061 return true;
2063 if (empty($string) or $string == '/') {
2064 return true;
2067 return false;
2073 * Just returns an array of text formats suitable for a popup menu
2075 * @uses FORMAT_MOODLE
2076 * @uses FORMAT_HTML
2077 * @uses FORMAT_PLAIN
2078 * @uses FORMAT_MARKDOWN
2079 * @return array
2081 function format_text_menu() {
2083 return array (FORMAT_MOODLE => __gettext('Elgg auto-format'),
2084 FORMAT_HTML => __gettext('HTML format'),
2085 FORMAT_PLAIN => __gettext('Plain text format'),
2086 FORMAT_MARKDOWN => __gettext('Markdown format'));
2090 * Given text in a variety of format codings, this function returns
2091 * the text as safe HTML.
2093 * @uses $CFG
2094 * @uses FORMAT_MOODLE
2095 * @uses FORMAT_HTML
2096 * @uses FORMAT_PLAIN
2097 * @uses FORMAT_WIKI
2098 * @uses FORMAT_MARKDOWN
2099 * @param string $text The text to be formatted. This is raw text originally from user input.
2100 * @param int $format Identifier of the text format to be used
2101 * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
2102 * @param array $options ?
2103 * @param int $courseid ?
2104 * @return string
2105 * @todo Finish documenting this function
2107 function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL ) {
2109 global $CFG, $course;
2111 if (!isset($options->noclean)) {
2112 $options->noclean=false;
2114 if (!isset($options->smiley)) {
2115 $options->smiley=true;
2117 if (!isset($options->filter)) {
2118 $options->filter=true;
2120 if (!isset($options->para)) {
2121 $options->para=true;
2123 if (!isset($options->newlines)) {
2124 $options->newlines=true;
2127 if (empty($courseid)) {
2128 if (!empty($course->id)) { // An ugly hack for better compatibility
2129 $courseid = $course->id;
2134 if (!empty($CFG->cachetext)) {
2135 $time = time() - $CFG->cachetext;
2136 $md5key = md5($text.'-'.$courseid.$options->noclean.$options->smiley.$options->filter.$options->para.$options->newlines);
2137 if ($cacheitem = get_record_select('cache_text', "md5key = '$md5key' AND timemodified > '$time'")) {
2138 return $cacheitem->formattedtext;
2141 */ // DISABLED - there is no cache_text - Penny
2143 $CFG->currenttextiscacheable = true; // Default status - can be changed by any filter
2145 switch ($format) {
2146 case FORMAT_HTML:
2148 if (!empty($options->smiley)) {
2149 replace_smilies($text);
2152 if (!isset($options->noclean)) {
2153 $text = clean_text($text, $format, !empty($options->cleanuserfile));
2156 if (!empty($options->filter)) {
2157 $text = filter_text($text, $courseid);
2159 break;
2161 case FORMAT_PLAIN:
2162 $text = s($text);
2163 $text = rebuildnolinktag($text);
2164 $text = str_replace(' ', '&nbsp; ', $text);
2165 $text = nl2br($text);
2166 break;
2168 case FORMAT_WIKI:
2169 // this format is deprecated
2170 $text = '<p>NOTICE: Wiki-like formatting has been removed from Moodle. You should not be seeing
2171 this message as all texts should have been converted to Markdown format instead.
2172 Please post a bug report to http://moodle.org/bugs with information about where you
2173 saw this message.</p>'.s($text);
2174 break;
2176 case FORMAT_MARKDOWN:
2177 $text = markdown_to_html($text);
2178 if (!empty($options->smiley)) {
2179 replace_smilies($text);
2181 if (empty($options->noclean)) {
2182 $text = clean_text($text, $format);
2184 if (!empty($options->filter)) {
2185 $text = filter_text($text, $courseid);
2187 break;
2189 default: // FORMAT_MOODLE or anything else
2190 $text = text_to_html($text, $options->smiley, $options->para, $options->newlines);
2191 if (empty($options->noclean)) {
2192 $text = clean_text($text, $format);
2194 if (!empty($options->filter)) {
2195 $text = filter_text($text, $courseid);
2197 break;
2200 if (!empty($CFG->cachetext) and $CFG->currenttextiscacheable) {
2201 $newrecord->md5key = $md5key;
2202 $newrecord->formattedtext = $text;
2203 $newrecord->timemodified = time();
2204 @insert_record('cache_text', $newrecord);
2207 return $text;
2210 /** Converts the text format from the value to the 'internal'
2211 * name or vice versa. $key can either be the value or the name
2212 * and you get the other back.
2214 * @param mixed int 0-4 or string one of 'moodle','html','plain','markdown'
2215 * @return mixed as above but the other way around!
2217 function text_format_name( $key ) {
2218 $lookup = array();
2219 $lookup[FORMAT_MOODLE] = 'moodle';
2220 $lookup[FORMAT_HTML] = 'html';
2221 $lookup[FORMAT_PLAIN] = 'plain';
2222 $lookup[FORMAT_MARKDOWN] = 'markdown';
2223 $value = "error";
2224 if (!is_numeric($key)) {
2225 $key = strtolower( $key );
2226 $value = array_search( $key, $lookup );
2228 else {
2229 if (isset( $lookup[$key] )) {
2230 $value = $lookup[ $key ];
2233 return $value;
2236 /** Given a simple string, this function returns the string
2237 * processed by enabled filters if $CFG->filterall is enabled
2239 * @param string $string The string to be filtered.
2240 * @param boolean $striplinks To strip any link in the result text.
2241 * @param int $courseid Current course as filters can, potentially, use it
2242 * @return string
2244 function format_string ($string, $striplinks = false, $courseid=NULL ) {
2246 global $CFG, $course;
2248 //We'll use a in-memory cache here to speed up repeated strings
2249 static $strcache;
2251 //Calculate md5
2252 $md5 = md5($string.'<+>'.$striplinks);
2254 //Fetch from cache if possible
2255 if(isset($strcache[$md5])) {
2256 return $strcache[$md5];
2259 if (empty($courseid)) {
2260 if (!empty($course->id)) { // An ugly hack for better compatibility
2261 $courseid = $course->id; // (copied from format_text)
2265 if (!empty($CFG->filterall)) {
2266 $string = filter_text($string, $courseid);
2269 if ($striplinks) { //strip links in string
2270 $string = preg_replace('/(<a[^>]+?>)(.+?)(<\/a>)/is','$2',$string);
2273 //Store to cache
2274 $strcache[$md5] = $string;
2276 return $string;
2280 * Given text in a variety of format codings, this function returns
2281 * the text as plain text suitable for plain email.
2283 * @uses FORMAT_MOODLE
2284 * @uses FORMAT_HTML
2285 * @uses FORMAT_PLAIN
2286 * @uses FORMAT_WIKI
2287 * @uses FORMAT_MARKDOWN
2288 * @param string $text The text to be formatted. This is raw text originally from user input.
2289 * @param int $format Identifier of the text format to be used
2290 * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
2291 * @return string
2293 function format_text_email($text, $format) {
2295 switch ($format) {
2297 case FORMAT_PLAIN:
2298 return $text;
2299 break;
2301 case FORMAT_WIKI:
2302 $text = wiki_to_html($text);
2303 /// This expression turns links into something nice in a text format. (Russell Jungwirth)
2304 /// From: http://php.net/manual/en/function.eregi-replace.php and simplified
2305 $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
2306 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
2307 break;
2309 case FORMAT_HTML:
2310 return html_to_text($text);
2311 break;
2313 case FORMAT_MOODLE:
2314 case FORMAT_MARKDOWN:
2315 default:
2316 $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
2317 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
2318 break;
2323 * This function takes a string and examines it for HTML tags.
2324 * If tags are detected it passes the string to a helper function {@link cleanAttributes2()}
2325 * which checks for attributes and filters them for malicious content
2326 * 17/08/2004 :: Eamon DOT Costello AT dcu DOT ie
2328 * @param string $str The string to be examined for html tags
2329 * @return string
2331 function cleanAttributes($str){
2332 $result = preg_replace_callback(
2333 '%(<[^>]*(>|$)|>)%m', #search for html tags
2334 "cleanAttributes2",
2335 $str
2337 return $result;
2341 * This function takes a string with an html tag and strips out any unallowed
2342 * protocols e.g. javascript:
2343 * It calls ancillary functions in kses which are prefixed by kses
2344 * 17/08/2004 :: Eamon DOT Costello AT dcu DOT ie
2346 * @param array $htmlArray An array from {@link cleanAttributes()}, containing in its 1st
2347 * element the html to be cleared
2348 * @return string
2350 function cleanAttributes2($htmlArray){
2352 global $CFG, $ALLOWED_PROTOCOLS;
2353 require_once($CFG->libdir .'/kses.php');
2355 $htmlTag = $htmlArray[1];
2356 if (substr($htmlTag, 0, 1) != '<') {
2357 return '&gt;'; //a single character ">" detected
2359 if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $htmlTag, $matches)) {
2360 return ''; // It's seriously malformed
2362 $slash = trim($matches[1]); //trailing xhtml slash
2363 $elem = $matches[2]; //the element name
2364 $attrlist = $matches[3]; // the list of attributes as a string
2366 $attrArray = kses_hair($attrlist, $ALLOWED_PROTOCOLS);
2368 $attStr = '';
2369 foreach ($attrArray as $arreach) {
2370 $attStr .= ' '.strtolower($arreach['name']).'="'.$arreach['value'].'" ';
2373 // Remove last space from attribute list
2374 $attStr = rtrim($attStr);
2376 $xhtml_slash = '';
2377 if (preg_match('%/\s*$%', $attrlist)) {
2378 $xhtml_slash = ' /';
2380 return '<'. $slash . $elem . $attStr . $xhtml_slash .'>';
2384 * Replaces all known smileys in the text with image equivalents
2386 * @uses $CFG
2387 * @param string $text Passed by reference. The string to search for smily strings.
2388 * @return string
2390 function replace_smilies(&$text) {
2392 global $CFG;
2394 /// this builds the mapping array only once
2395 static $runonce = false;
2396 static $e = array();
2397 static $img = array();
2398 static $emoticons = array(
2399 ':-)' => 'smiley',
2400 ':)' => 'smiley',
2401 ':-D' => 'biggrin',
2402 ';-)' => 'wink',
2403 ':-/' => 'mixed',
2404 'V-.' => 'thoughtful',
2405 ':-P' => 'tongueout',
2406 'B-)' => 'cool',
2407 '^-)' => 'approve',
2408 '8-)' => 'wideeyes',
2409 ':o)' => 'clown',
2410 ':-(' => 'sad',
2411 ':(' => 'sad',
2412 '8-.' => 'shy',
2413 ':-I' => 'blush',
2414 ':-X' => 'kiss',
2415 '8-o' => 'surprise',
2416 'P-|' => 'blackeye',
2417 '8-[' => 'angry',
2418 'xx-P' => 'dead',
2419 '|-.' => 'sleepy',
2420 '}-]' => 'evil',
2423 if ($runonce == false) { /// After the first time this is not run again
2424 foreach ($emoticons as $emoticon => $image){
2425 $alttext = get_string($image, 'pix');
2427 $e[] = $emoticon;
2428 $img[] = '<img alt="'. $alttext .'" width="15" height="15" src="'. $CFG->pixpath .'/s/'. $image .'.gif" />';
2430 $runonce = true;
2433 // Exclude from transformations all the code inside <script> tags
2434 // Needed to solve Bug 1185. Thanks to jouse 2001 detecting it. :-)
2435 // Based on code from glossary fiter by Williams Castillo.
2436 // - Eloy
2438 // Detect all the <script> zones to take out
2439 $excludes = array();
2440 preg_match_all('/<script language(.+?)<\/script>/is',$text,$list_of_excludes);
2442 // Take out all the <script> zones from text
2443 foreach (array_unique($list_of_excludes[0]) as $key=>$value) {
2444 $excludes['<+'.$key.'+>'] = $value;
2446 if ($excludes) {
2447 $text = str_replace($excludes,array_keys($excludes),$text);
2450 /// this is the meat of the code - this is run every time
2451 $text = str_replace($e, $img, $text);
2453 // Recover all the <script> zones to text
2454 if ($excludes) {
2455 $text = str_replace(array_keys($excludes),$excludes,$text);
2460 * Given plain text, makes it into HTML as nicely as possible.
2461 * May contain HTML tags already
2463 * @uses $CFG
2464 * @param string $text The string to convert.
2465 * @param boolean $smiley Convert any smiley characters to smiley images?
2466 * @param boolean $para If true then the returned string will be wrapped in paragraph tags
2467 * @param boolean $newlines If true then lines newline breaks will be converted to HTML newline breaks.
2468 * @return string
2471 function text_to_html($text, $smiley=true, $para=true, $newlines=true) {
2474 global $CFG;
2476 /// Remove any whitespace that may be between HTML tags
2477 $text = eregi_replace(">([[:space:]]+)<", "><", $text);
2479 /// Remove any returns that precede or follow HTML tags
2480 $text = eregi_replace("([\n\r])<", " <", $text);
2481 $text = eregi_replace(">([\n\r])", "> ", $text);
2483 convert_urls_into_links($text);
2485 /// Make returns into HTML newlines.
2486 if ($newlines) {
2487 $text = nl2br($text);
2490 /// Turn smileys into images.
2491 if ($smiley) {
2492 replace_smilies($text);
2495 /// Wrap the whole thing in a paragraph tag if required
2496 if ($para) {
2497 return '<p>'.$text.'</p>';
2498 } else {
2499 return $text;
2504 * Given Markdown formatted text, make it into XHTML using external function
2506 * @uses $CFG
2507 * @param string $text The markdown formatted text to be converted.
2508 * @return string Converted text
2510 function markdown_to_html($text) {
2511 global $CFG;
2513 require_once($CFG->libdir .'/markdown.php');
2515 return Markdown($text);
2519 * Given HTML text, make it into plain text using external function
2521 * @uses $CFG
2522 * @param string $html The text to be converted.
2523 * @return string
2525 function html_to_text($html) {
2527 global $CFG;
2529 require_once($CFG->libdir .'/html2text.php');
2531 return html2text($html);
2535 * Given some text this function converts any URLs it finds into HTML links
2537 * @param string $text Passed in by reference. The string to be searched for urls.
2539 function convert_urls_into_links(&$text) {
2540 /// Make lone URLs into links. eg http://moodle.com/
2541 $text = eregi_replace("([[:space:]]|^|\(|\[)([[:alnum:]]+)://([^[:space:]]*)([[:alnum:]#?/&=])",
2542 "\\1<a href=\"\\2://\\3\\4\" target=\"_blank\">\\2://\\3\\4</a>", $text);
2544 /// eg www.moodle.com
2545 $text = eregi_replace("([[:space:]]|^|\(|\[)www\.([^[:space:]]*)([[:alnum:]#?/&=])",
2546 "\\1<a href=\"http://www.\\2\\3\" target=\"_blank\">www.\\2\\3</a>", $text);
2552 * This function will highlight search words in a given string
2553 * It cares about HTML and will not ruin links. It's best to use
2554 * this function after performing any conversions to HTML.
2555 * Function found here: http://forums.devshed.com/t67822/scdaa2d1c3d4bacb4671d075ad41f0854.html
2557 * @param string $needle The string to search for
2558 * @param string $haystack The string to search for $needle in
2559 * @param int $case ?
2560 * @return string
2561 * @todo Finish documenting this function
2563 function highlight($needle, $haystack, $case=0,
2564 $left_string='<span class="highlight">', $right_string='</span>') {
2565 if (empty($needle)) {
2566 return $haystack;
2569 //$list_of_words = eregi_replace("[^-a-zA-Z0-9&.']", " ", $needle); // bug 3101
2570 $list_of_words = $needle;
2571 $list_array = explode(' ', $list_of_words);
2572 for ($i=0; $i<sizeof($list_array); $i++) {
2573 if (strlen($list_array[$i]) == 1) {
2574 $list_array[$i] = '';
2577 $list_of_words = implode(' ', $list_array);
2578 $list_of_words_cp = $list_of_words;
2579 $final = array();
2580 preg_match_all('/<(.+?)>/is',$haystack,$list_of_words);
2582 foreach (array_unique($list_of_words[0]) as $key=>$value) {
2583 $final['<|'.$key.'|>'] = $value;
2586 $haystack = str_replace($final,array_keys($final),$haystack);
2587 $list_of_words_cp = eregi_replace(' +', '|', $list_of_words_cp);
2589 if ($list_of_words_cp{0}=='|') {
2590 $list_of_words_cp{0} = '';
2592 if ($list_of_words_cp{strlen($list_of_words_cp)-1}=='|') {
2593 $list_of_words_cp{strlen($list_of_words_cp)-1}='';
2596 $list_of_words_cp = trim($list_of_words_cp);
2598 if ($list_of_words_cp) {
2600 $list_of_words_cp = "(". $list_of_words_cp .")";
2602 if (!$case){
2603 $haystack = eregi_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
2604 } else {
2605 $haystack = ereg_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
2608 $haystack = str_replace(array_keys($final),$final,$haystack);
2610 return $haystack;
2614 * This function will highlight instances of $needle in $haystack
2615 * It's faster that the above function and doesn't care about
2616 * HTML or anything.
2618 * @param string $needle The string to search for
2619 * @param string $haystack The string to search for $needle in
2620 * @return string
2622 function highlightfast($needle, $haystack) {
2623 $textlib = textlib_get_instance();
2625 $parts = explode($textlib->strtolower($needle), $textlib->strtolower($haystack));
2627 $pos = 0;
2629 foreach ($parts as $key => $part) {
2630 $parts[$key] = $textlib->substr($haystack, $pos, $textlib->strlen($part));
2631 $pos += $textlib->strlen($part);
2633 $parts[$key] .= '<span class="highlight">'.$textlib->substr($haystack, $pos, $textlib->strlen($needle)).'</span>';
2634 $pos += $textlib->strlen($needle);
2637 return (join('', $parts));
2641 * Print a link to continue on to another page.
2643 * @uses $CFG
2644 * @param string $link The url to create a link to.
2646 function print_continue($link) {
2648 global $CFG;
2650 if (!$link) {
2651 $link = $_SERVER['HTTP_REFERER'];
2654 echo '<div class="continuebutton">';
2655 print_single_button($link, NULL, __gettext('Continue'), 'post', $CFG->framename);
2656 echo '</div>'."\n";
2660 * Print a message in a standard themed box.
2662 * @param string $message ?
2663 * @param string $align ?
2664 * @param string $width ?
2665 * @param string $color ?
2666 * @param int $padding ?
2667 * @param string $class ?
2668 * @todo Finish documenting this function
2670 function print_simple_box($message, $align='', $width='', $color='', $padding=5, $class='generalbox', $id='') {
2671 print_simple_box_start($align, $width, $color, $padding, $class, $id);
2672 echo stripslashes_safe($message);
2673 print_simple_box_end();
2677 * Print the top portion of a standard themed box.
2679 * @param string $align ?
2680 * @param string $width ?
2681 * @param string $color ?
2682 * @param int $padding ?
2683 * @param string $class ?
2684 * @todo Finish documenting this function
2686 function print_simple_box_start($align='', $width='', $color='', $padding=5, $class='generalbox', $id='') {
2688 if ($color) {
2689 $color = 'bgcolor="'. $color .'"';
2691 if ($align) {
2692 $align = 'align="'. $align .'"';
2694 if ($width) {
2695 $width = 'width="'. $width .'"';
2697 if ($id) {
2698 $id = 'id="'. $id .'"';
2700 $class = trim($class);
2701 $classcontent = preg_replace('/(\s+|$)/','content ', $class);
2703 echo "<table $align $width $id class=\"$class\" border=\"0\" cellpadding=\"$padding\" cellspacing=\"0\">".
2704 "<tr><td $color class=\"$classcontent\">";
2708 * Print the end portion of a standard themed box.
2710 function print_simple_box_end() {
2711 echo '</td></tr></table>';
2715 * Print a self contained form with a single submit button.
2717 * @param string $link ?
2718 * @param array $options ?
2719 * @param string $label ?
2720 * @param string $method ?
2721 * @todo Finish documenting this function
2723 function print_single_button($link, $options, $label='OK', $method='get', $target='_self') {
2724 echo '<div class="singlebutton">';
2725 echo '<form action="'. $link .'" method="'. $method .'" target="'.$target.'">';
2726 if ($options) {
2727 foreach ($options as $name => $value) {
2728 echo '<input type="hidden" name="'. $name .'" value="'. $value .'" />';
2731 echo '<input type="submit" value="'. $label .'" /></form></div>';
2735 * Print a png image.
2737 * @param string $url ?
2738 * @param int $sizex ?
2739 * @param int $sizey ?
2740 * @param boolean $returnstring ?
2741 * @param string $parameters ?
2742 * @todo Finish documenting this function
2744 function print_png($url, $sizex, $sizey, $returnstring, $parameters='alt=""') {
2745 global $CFG;
2746 static $recentIE;
2748 if (!isset($recentIE)) {
2749 $recentIE = check_browser_version('MSIE', '5.0');
2752 if ($recentIE) { // work around the HORRIBLE bug IE has with alpha transparencies
2753 $output .= '<img src="'. $CFG->pixpath .'/spacer.gif" width="'. $sizex .'" height="'. $sizey .'"'.
2754 ' border="0" class="png" style="width: '. $sizex .'px; height: '. $sizey .'px; '.
2755 ' filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='.
2756 "'$url', sizingMethod='scale') ".
2757 ' '. $parameters .' />';
2758 } else {
2759 $output .= '<img src="'. $url .'" border="0" width="'. $sizex .'" height="'. $sizey .'" '.
2760 ' '. $parameters .' />';
2763 if ($returnstring) {
2764 return $output;
2765 } else {
2766 echo $output;
2771 * Print a nicely formatted table.
2773 * @param array $table is an object with several properties.
2774 * <ul<li>$table->head - An array of heading names.
2775 * <li>$table->align - An array of column alignments
2776 * <li>$table->size - An array of column sizes
2777 * <li>$table->wrap - An array of "nowrap"s or nothing
2778 * <li>$table->data[] - An array of arrays containing the data.
2779 * <li>$table->width - A percentage of the page
2780 * <li>$table->cellpadding - Padding on each cell
2781 * <li>$table->cellspacing - Spacing between cells
2782 * </ul>
2783 * @return boolean
2784 * @todo Finish documenting this function
2786 function print_table($table) {
2788 if (isset($table->align)) {
2789 foreach ($table->align as $key => $aa) {
2790 if ($aa) {
2791 $align[$key] = ' align="'. $aa .'"';
2792 } else {
2793 $align[$key] = '';
2797 if (isset($table->size)) {
2798 foreach ($table->size as $key => $ss) {
2799 if ($ss) {
2800 $size[$key] = ' width="'. $ss .'"';
2801 } else {
2802 $size[$key] = '';
2806 if (isset($table->wrap)) {
2807 foreach ($table->wrap as $key => $ww) {
2808 if ($ww) {
2809 $wrap[$key] = ' nowrap="nowrap" ';
2810 } else {
2811 $wrap[$key] = '';
2816 if (empty($table->width)) {
2817 $table->width = '80%';
2820 if (empty($table->cellpadding)) {
2821 $table->cellpadding = '5';
2824 if (empty($table->cellspacing)) {
2825 $table->cellspacing = '1';
2828 if (empty($table->class)) {
2829 $table->class = 'generaltable';
2832 $tableid = empty($table->id) ? '' : 'id="'.$table->id.'"';
2834 print_simple_box_start('center', $table->width, '#ffffff', 0);
2835 echo '<table width="100%" border="0" align="center" ';
2836 echo " cellpadding=\"$table->cellpadding\" cellspacing=\"$table->cellspacing\" class=\"$table->class\" $tableid>\n";
2838 $countcols = 0;
2840 if (!empty($table->head)) {
2841 $countcols = count($table->head);
2842 echo '<tr>';
2843 foreach ($table->head as $key => $heading) {
2845 if (!isset($size[$key])) {
2846 $size[$key] = '';
2848 if (!isset($align[$key])) {
2849 $align[$key] = '';
2851 echo '<th valign="top" '. $align[$key].$size[$key] .' nowrap="nowrap" class="header c'.$key.'">'. $heading .'</th>';
2853 echo '</tr>'."\n";
2856 if (!empty($table->data)) {
2857 $oddeven = 1;
2858 foreach ($table->data as $key => $row) {
2859 $oddeven = $oddeven ? 0 : 1;
2860 echo '<tr class="r'.$oddeven.'">'."\n";
2861 if ($row == 'hr' and $countcols) {
2862 echo '<td colspan="'. $countcols .'"><div class="tabledivider"></div></td>';
2863 } else { /// it's a normal row of data
2864 foreach ($row as $key => $item) {
2865 if (!isset($size[$key])) {
2866 $size[$key] = '';
2868 if (!isset($align[$key])) {
2869 $align[$key] = '';
2871 if (!isset($wrap[$key])) {
2872 $wrap[$key] = '';
2874 echo '<td '. $align[$key].$size[$key].$wrap[$key] .' class="cell c'.$key.'">'. $item .'</td>';
2877 echo '</tr>'."\n";
2880 echo '</table>'."\n";
2881 print_simple_box_end();
2883 return true;
2887 * Prints form items with the names $day, $month and $year
2889 * @param int $day ?
2890 * @param int $month ?
2891 * @param int $year ?
2892 * @param int $currenttime A default timestamp in GMT
2893 * @todo Finish documenting this function
2895 function print_date_selector($day, $month, $year, $currenttime=0) {
2897 if (!$currenttime) {
2898 $currenttime = time();
2900 $currentdate = usergetdate($currenttime);
2902 for ($i=1; $i<=31; $i++) {
2903 $days[$i] = $i;
2905 for ($i=1; $i<=12; $i++) {
2906 $months[$i] = userdate(gmmktime(12,0,0,$i,1,2000), "%B");
2908 for ($i=2000; $i<=2010; $i++) {
2909 $years[$i] = $i;
2911 choose_from_menu($days, $day, $currentdate['mday'], '');
2912 choose_from_menu($months, $month, $currentdate['mon'], '');
2913 choose_from_menu($years, $year, $currentdate['year'], '');
2917 *Prints form items with the names $hour and $minute
2919 * @param ? $hour ?
2920 * @param ? $minute ?
2921 * @param $currenttime A default timestamp in GMT
2922 * @param int $step ?
2923 * @todo Finish documenting this function
2925 function print_time_selector($hour, $minute, $currenttime=0, $step=5 ,$return=false) {
2927 if (!$currenttime) {
2928 $currenttime = time();
2930 $currentdate = usergetdate($currenttime);
2931 if ($step != 1) {
2932 $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
2934 for ($i=0; $i<=23; $i++) {
2935 $hours[$i] = sprintf("%02d",$i);
2937 for ($i=0; $i<=59; $i+=$step) {
2938 $minutes[$i] = sprintf("%02d",$i);
2941 return choose_from_menu($hours, $hour, $currentdate['hours'], '','','',$return)
2942 .choose_from_menu($minutes, $minute, $currentdate['minutes'], '','','',$return);
2946 *Returns an A link tag
2948 * @param $url
2949 * @param $str
2950 * @param $extrattr Extra attribs for the A tag
2951 * @return string
2953 function a_href($url, $str, $extrattr='') {
2954 $str = htmlspecialchars($str, ENT_COMPAT, 'utf-8');
2955 return "<a href=\"$url\" $extrattr >$str</a>";
2959 *Returns an A link tag using __gettext()
2961 * @param $url
2962 * @param $str for __gettext()
2963 * @param $extrattr Extra attribs for the A tag
2964 * @return string
2966 function a_hrefg($url, $str, $extrattr='') {
2967 $str = htmlspecialchars(__gettext($str), ENT_COMPAT, 'utf-8');
2968 return "<a href=\"$url\" $extrattr >$str</a>";
2972 * Print an error page displaying an error message.
2973 * Old method, don't call directly in new code - use print_error instead.
2976 * @uses $SESSION
2977 * @uses $CFG
2978 * @param string $message The message to display to the user about the error.
2979 * @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.
2981 function error ($message, $link='') {
2982 global $CFG, $SESSION;
2984 @header('HTTP/1.0 404 Not Found');
2986 print_header(__gettext('Error'));
2987 echo '<br />';
2989 $message = clean_text($message); // In case nasties are in here
2991 print_simple_box($message, 'center', '', '#FFBBBB', 5, 'errorbox');
2993 if (!$link) {
2994 if ( !empty($SESSION->fromurl) ) {
2995 $link = $SESSION->fromurl;
2996 unset($SESSION->fromurl);
2997 } else {
2998 $link = $CFG->wwwroot;
3001 print_continue($link);
3002 print_footer();
3003 for ($i=0;$i<512;$i++) { // Padding to help IE work with 404
3004 echo ' ';
3006 die;
3010 * Print an error page displaying an error message. New method - use this for new code.
3012 * @uses $SESSION
3013 * @uses $CFG
3014 * @param string $string The name of the string from error.php to print
3015 * @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.
3017 function print_error ($string, $link='') {
3019 $string = get_string($string, 'error');
3020 error($string, $link);
3026 * Print a message and exit.
3028 * @uses $CFG
3029 * @param string $message ?
3030 * @param string $link ?
3031 * @todo Finish documenting this function
3033 function notice ($message, $link='') {
3034 global $CFG;
3036 $message = clean_text($message);
3037 $link = clean_text($link);
3039 if (!$link) {
3040 if (!empty($_SERVER['HTTP_REFERER'])) {
3041 $link = $_SERVER['HTTP_REFERER'];
3042 } else {
3043 $link = $CFG->wwwroot;
3047 echo '<br />';
3048 print_simple_box($message, 'center', '50%', '', '20', 'noticebox');
3049 print_continue($link);
3050 print_footer(get_site());
3051 die;
3055 * Print a message along with "Yes" and "No" links for the user to continue.
3057 * @param string $message The text to display
3058 * @param string $linkyes The link to take the user to if they choose "Yes"
3059 * @param string $linkno The link to take the user to if they choose "No"
3061 function notice_yesno ($message, $linkyes, $linkno) {
3063 global $CFG;
3065 $message = clean_text($message);
3066 $linkyes = clean_text($linkyes);
3067 $linkno = clean_text($linkno);
3069 print_simple_box_start('center', '60%', '', 5, 'noticebox', 'notice');
3070 echo '<p align="center">'. $message .'</p>';
3071 echo '<table align="center" cellpadding="20"><tr><td>';
3072 print_single_button($linkyes, NULL, __gettext('Yes'), 'post', $CFG->framename);
3073 echo '</td><td>';
3074 print_single_button($linkno, NULL, __gettext('No'), 'post', $CFG->framename);
3075 echo '</td></tr></table>';
3076 print_simple_box_end();
3080 * Redirects the user to another page, after printing a notice
3082 * @param string $url The url to take the user to
3083 * @param string $message The text message to display to the user about the redirect, if any
3084 * @param string $delay How long before refreshing to the new page at $url?
3085 * @todo '&' needs to be encoded into '&amp;' for XHTML compliance,
3086 * however, this is not true for javascript. Therefore we
3087 * first decode all entities in $url (since we cannot rely on)
3088 * the correct input) and then encode for where it's needed
3089 * echo "<script type='text/javascript'>alert('Redirect $url');</script>";
3091 function redirect($url, $message='', $delay='0') {
3093 $url = clean_text($url);
3094 $message = clean_text($message);
3096 $url = htmlspecialchars_decode($url, ENT_COMPAT); // for php < 5.1.0 this is defined in elgglib.php
3097 $url = str_replace(array("\n", "\r"), '', $url); // some more cleaning
3098 $encodedurl = htmlspecialchars($url, ENT_COMPAT, 'utf-8');
3100 if (empty($message)) {
3101 echo '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />';
3102 echo '<script type="text/javascript">'. "\n" .'<!--'. "\n". "location.replace('$url');". "\n". '//-->'. "\n". '</script>'; // To cope with Mozilla bug
3103 } else {
3105 if (empty($delay)) {
3106 $delay = 3; // There's no point having a message with no delay
3108 print_header('', '', '', '', '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />');
3109 echo '<div style="text-align: center;">';
3110 echo '<p>'. $message .'</p>';
3111 //called from setup.php, so gettext module hasn't been loaded yet
3112 if (function_exists("__gettext")) {
3113 $continue = __gettext('Continue');
3114 } else {
3115 $continue = 'Continue';
3117 echo '<p>( <a href="'. $encodedurl .'">'. $continue .'</a> )</p>';
3118 echo '</div>';
3121 <script type="text/javascript">
3122 <!--
3124 function redirect() {
3125 document.location.replace('<?php echo $url ?>');
3127 setTimeout("redirect()", <?php echo ($delay * 1000) ?>);
3129 </script>
3130 <?php
3133 die;
3137 * Print a bold message in an optional color.
3139 * @param string $message The message to print out
3140 * @param string $style Optional style to display message text in
3141 * @param string $align Alignment option
3143 function notify ($message, $style='notifyproblem', $align='center') {
3145 if ($style == 'green') {
3146 $style = 'notifysuccess'; // backward compatible with old color system
3149 $message = clean_text($message);
3151 echo '<div class="'.$style.'" align="'. $align .'">'. $message .'</div>'."<br />\n";
3156 * This function is used to rebuild the <nolink> tag because some formats (PLAIN and WIKI)
3157 * will transform it to html entities
3159 * @param string $text Text to search for nolink tag in
3160 * @return string
3162 function rebuildnolinktag($text) {
3164 $text = preg_replace('/&lt;(\/*nolink)&gt;/i','<$1>',$text);
3166 return $text;
3171 * Adjust the list of allowed tags based on $CFG->allowobjectembed and user roles (admin)
3173 function adjust_allowed_tags() {
3175 global $CFG, $ALLOWED_TAGS;
3177 if (!empty($CFG->allowobjectembed)) {
3178 $ALLOWED_TAGS .= '<embed><object><param>';
3184 * This function makes the return value of ini_get consistent if you are
3185 * setting server directives through the .htaccess file in apache.
3186 * Current behavior for value set from php.ini On = 1, Off = [blank]
3187 * Current behavior for value set from .htaccess On = On, Off = Off
3188 * Contributed by jdell @ unr.edu
3190 * @param string $ini_get_arg ?
3191 * @return boolean
3192 * @todo Finish documenting this function
3194 function ini_get_bool($ini_get_arg) {
3195 $temp = ini_get($ini_get_arg);
3197 if ($temp == '1' or strtolower($temp) == 'on') {
3198 return true;
3200 return false;
3204 * Generate and return a random string of the specified length.
3206 * @param int $length The length of the string to be created.
3207 * @return string
3209 function random_string ($length=15) {
3210 $pool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
3211 $pool .= 'abcdefghijklmnopqrstuvwxyz';
3212 $pool .= '0123456789';
3213 $poollen = strlen($pool);
3214 mt_srand ((double) microtime() * 1000000);
3215 $string = '';
3216 for ($i = 0; $i < $length; $i++) {
3217 $string .= substr($pool, (mt_rand()%($poollen)), 1);
3219 return $string;
3222 // Fills user information
3223 function init_user_var($user) {
3225 global $CFG;
3227 $user->loggedin = true;
3228 $user->site = $CFG->wwwroot; // for added security, store the site in the session
3229 $user->sesskey = random_string(10);
3230 $user->sessionIP = md5(getremoteaddr()); // Store the current IP in the session
3231 // backwards compatibility (TODO this will have to go eventually)
3232 fill_legacy_user_session($user);
3233 return $user;
3238 // Authentication Function
3239 // Returns true or false
3240 function authenticate_account($username,$password) {
3242 global $CFG,$USER;
3244 if (empty($CFG->auth)) {
3245 $CFG->auth = 'internal';
3247 if (!file_exists($CFG->dirroot . 'auth/' . $CFG->auth . '/lib.php')) {
3248 $CFG->auth = 'internal';
3251 require_once($CFG->dirroot . 'auth/' . $CFG->auth . '/lib.php');
3253 // Module authentication function
3254 $function = $CFG->auth.'_authenticate_user_login';
3256 // Does the function exist
3257 if (!function_exists($function)) {
3258 print 'Error: function '.$function.' not found in auth/' . $CFG->auth . '/lib.php';
3259 return false;
3262 if (!$user = $function($username,$password)) {
3263 return false;
3266 $ok = true;
3267 if (user_flag_get("banned", $user->ident)) { // this needs to change.
3268 $ok = false;
3269 $user = false;
3270 $USER = false;
3271 global $messages;
3272 $messages[] = __gettext("You have been banned from the system!");
3273 return false;
3276 // Set Persistent Cookie
3277 $rememberme = optional_param('remember',0);
3278 if (!empty($rememberme)) {
3279 remember_login($user->ident);
3283 $USER = init_user_var($user);
3284 return $ok;
3287 // Attempts to get login from a cookie
3288 function cookied_login() {
3289 global $USER;
3290 if((!empty($_COOKIE[AUTH_COOKIE])) && $ticket = md5($_COOKIE[AUTH_COOKIE])) {
3291 if ($user = get_record('users','code',$ticket)) {
3292 $USER = $user;
3294 /*** TODO: Create Proper Abstraction Interface - don't use file binding -- ugh ***/
3295 if (!user_flag_get("banned",$USER->ident)) {
3296 $USER = init_user_var($USER);
3297 return true;
3298 } else {
3299 global $messages;
3300 $messages[] = __gettext("You have been banned from the system!");
3301 return false;
3308 * elgg doesn't have a 'login' page yet, but it will so this can stay here for now
3310 function require_login() {
3311 global $USER, $SESSION,$FULLME;
3313 // Check to see if there's a persistent cookie
3314 cookied_login();
3316 // First check that the user is logged in to the site.
3317 if (empty($USER->loggedin) || $USER->site != $CFG->wwwroot) {
3318 $SESSION->wantsurl = $FULLME;
3319 if (!empty($_SERVER['HTTP_REFERER'])) {
3320 $SESSION->fromurl = $_SERVER['HTTP_REFERER'];
3322 $USER = NULL;
3323 redirect($CFG->wwwroot .'login/index.php');
3324 exit;
3327 // Make sure current IP matches the one for this session (if required)
3328 if (!empty($CFG->tracksessionip)) {
3329 if ($USER->sessionIP != md5(getremoteaddr())) {
3330 error(__gettext('Sorry, but your IP number seems to have changed from when you first logged in. This security feature prevents crackers stealing your identity while logged in to this site. Normal users should not be seeing this message - please ask the site administrator for help.'));
3334 // Make sure the USER has a sesskey set up. Used for checking script parameters.
3335 sesskey();
3337 return true;
3341 function remember_login($id) {
3342 global $CFG;
3343 $id = (int) $id;
3344 if(!$id) return 0;
3346 // Double MD5
3347 if (!defined("SECRET_SALT")) {
3348 define("SECRET_SALT", "SECRET_SALT");
3350 $ticket = md5(SECRET_SALT . $id . time());
3351 $md5ticket = md5($ticket);
3353 // Update MD5 of authticket
3354 $user->code = $md5ticket;
3355 $user->ident = $id;
3356 update_record('users',$user);
3358 setcookie(AUTH_COOKIE, $ticket, time()+AUTH_COOKIE_LENGTH, $CFG->cookiepath);
3359 global $messages;
3360 $messages[] = __gettext("The system will remember you and automatically log you in next time.");
3362 return 1;
3365 // Returns whether the user is logged in or not;
3366 // if not logged in, checks for persistent cookie
3367 function isloggedin() {
3368 global $USER;
3369 if (empty($USER->ident) && empty($USER->loggedin)) {
3370 cookied_login();
3372 return (!empty($USER->ident) && !empty($USER->loggedin));
3375 function get_string($s) {
3376 return __gettext($s);
3379 function print_header() {
3380 $args = func_get_args();
3381 echo $args[0];
3384 function print_footer() {
3385 $args = func_get_args();
3386 echo $args[0];
3389 function clean_text($text, $format=FORMAT_MOODLE) {
3391 global $ALLOWED_TAGS;
3393 switch ($format) {
3394 case FORMAT_PLAIN:
3395 return $text;
3397 default:
3399 /// Remove tags that are not allowed
3400 $text = strip_tags($text, $ALLOWED_TAGS);
3402 /// Add some breaks into long strings of &nbsp;
3403 $text = preg_replace('/((&nbsp;){10})&nbsp;/', '\\1 ', $text);
3405 /// Clean up embedded scripts and , using kses
3406 $text = cleanAttributes($text);
3408 /// Remove script events
3409 $text = eregi_replace("([^a-z])language([[:space:]]*)=", "\\1Xlanguage=", $text);
3410 $text = eregi_replace("([^a-z])on([a-z]+)([[:space:]]*)=", "\\1Xon\\2=", $text);
3412 return $text;
3417 * Set a key in global configuration
3419 * Set a key/value pair in both this session's {@link $CFG} global variable
3420 * and in the 'config' database table for future sessions.
3422 * Can also be used to update keys for plugin-scoped configs in config_plugin table.
3423 * In that case it doesn't affect $CFG.
3425 * @param string $name the key to set
3426 * @param string $value the value to set
3427 * @uses $CFG
3428 * @return bool
3430 function set_config($name, $value) {
3431 /// No need for get_config because they are usually always available in $CFG
3433 global $CFG;
3435 $CFG->$name = $value; // So it's defined for this invocation at least
3437 if (get_field('datalists', 'name', 'name', $name)) {
3438 return set_field('datalists', 'value', $value, 'name', $name);
3439 } else {
3440 $config->name = $name;
3441 $config->value = $value;
3442 return insert_record('datalists', $config);
3447 * Get configuration values from the global config table
3448 * or the config_plugins table.
3450 * If called with no parameters it will do the right thing
3451 * generating $CFG safely from the database without overwriting
3452 * existing values.
3454 * @param string $name
3455 * @uses $CFG
3456 * @return hash-like object or single value
3459 function get_config($name=NULL) {
3461 global $CFG;
3463 if (!empty($name)) { // the user is asking for a specific value
3464 return get_record('datalists', 'name', $name);
3467 // this was originally in setup.php
3468 if ($configs = get_records('datalists')) {
3469 $localcfg = (array)$CFG;
3470 foreach ($configs as $config) {
3471 if (!isset($localcfg[$config->name])) {
3472 $localcfg[$config->name] = $config->value;
3473 } else {
3474 if ($localcfg[$config->name] != $config->value ) {
3475 // complain if the DB has a different
3476 // value than config.php does
3477 error_log("\$CFG->{$config->name} in config.php ({$localcfg[$config->name]}) overrides database setting ({$config->value})");
3482 $localcfg = (object)$localcfg;
3483 return $localcfg;
3484 } else {
3485 // preserve $CFG if DB returns nothing or error
3486 return $CFG;
3491 function guest_user() {
3492 $user = new stdClass();
3494 $user->ident = 0;
3495 $user->username = '';
3496 $user->name = '';
3497 $user->email = '';
3498 $user->icon = -1;
3499 $user->icon_quota = 0;
3501 return $user;
3504 function fill_legacy_user_session($user = NULL) {
3506 if (!$user || $user == NULL) {
3507 $user = guest_user();
3510 /// Fills up all legacy user session data
3511 /// This function provides backward compatibility
3512 $_SESSION['userid'] = (int) $user->ident;
3513 $_SESSION['username'] = $user->username;
3514 $_SESSION['name'] = stripslashes($user->name);
3515 $_SESSION['email'] = stripslashes($user->email);
3516 $iconid = (int) $user->icon;
3517 if ($iconid == -1) {
3518 $_SESSION['icon'] = "default.png";
3519 } else {
3520 $icon = get_record('icons','ident',$iconid);
3521 $_SESSION['icon'] = $icon->filename;
3523 $_SESSION['icon_quota'] = (int) $user->icon_quota;
3527 * Replace function htmlspecialchars_decode()
3529 * @category PHP
3530 * @package PHP_Compat
3531 * @link http://php.net/function.htmlspecialchars_decode
3532 * @author Aidan Lister <aidan@php.net>
3533 * @version $Revision: 1.3 $
3534 * @since PHP 5.1.0
3535 * @require PHP 4.0.0 (user_error)
3537 if (!function_exists('htmlspecialchars_decode')) {
3538 function htmlspecialchars_decode($string, $quote_style = null)
3540 // Sanity check
3541 if (!is_scalar($string)) {
3542 user_error('htmlspecialchars_decode() expects parameter 1 to be string, ' .
3543 gettype($string) . ' given', E_USER_WARNING);
3544 return;
3547 if (!is_int($quote_style) && $quote_style !== null) {
3548 user_error('htmlspecialchars_decode() expects parameter 2 to be integer, ' .
3549 gettype($quote_style) . ' given', E_USER_WARNING);
3550 return;
3553 // Init
3554 $from = array('&amp;', '&lt;', '&gt;');
3555 $to = array('&', '<', '>');
3557 // The function does not behave as documented
3558 // This matches the actual behaviour of the function
3559 if ($quote_style & ENT_COMPAT || $quote_style & ENT_QUOTES) {
3560 $from[] = '&quot;';
3561 $to[] = '"';
3563 $from[] = '&#039;';
3564 $to[] = "'";
3567 return str_replace($from, $to, $string);
3573 * Returns the maximum size for uploading files.
3575 * There are five possible upload limits:
3576 * 1. in Apache using LimitRequestBody (no way of checking or changing this)
3577 * 2. in php.ini for 'upload_max_filesize' (can not be changed inside PHP)
3578 * 3. in .htaccess for 'upload_max_filesize' (can not be changed inside PHP)
3579 * 4. in php.ini for 'post_max_size' (can not be changed inside PHP)
3580 * 5. by the limitations on the current situation (eg file quota)
3582 * The last one is passed to this function as an argument (in bytes).
3583 * Anything defined as 0 is ignored.
3584 * The smallest of all the non-zero numbers is returned.
3586 * @param int $maxbytes Current maxbytes (in bytes)
3587 * @return int The maximum size for uploading files.
3588 * @todo Finish documenting this function
3590 function get_max_upload_file_size($maxbytes=0) {
3591 global $CFG;
3593 if (! $filesize = ini_get('upload_max_filesize')) {
3594 if (!empty($CFG->absmaxuploadsize)) {
3595 $filesize = $CFG->absmaxuploadsize;
3596 } else {
3597 $filesize = '5M';
3600 $minimumsize = get_real_size($filesize);
3602 if ($postsize = ini_get('post_max_size')) {
3603 $postsize = get_real_size($postsize);
3604 if ($postsize < $minimumsize) {
3605 $minimumsize = $postsize;
3609 if ($maxbytes and $maxbytes < $minimumsize) {
3610 $minimumsize = $maxbytes;
3613 return $minimumsize;
3616 function remove_dir($dir, $content_only=false) {
3617 // if content_only=true then delete all but
3618 // the directory itself
3620 $handle = opendir($dir);
3621 while (false!==($item = readdir($handle))) {
3622 if($item != '.' && $item != '..') {
3623 if(is_dir($dir.'/'.$item)) {
3624 remove_dir($dir.'/'.$item);
3625 }else{
3626 unlink($dir.'/'.$item);
3630 closedir($handle);
3631 if ($content_only) {
3632 return true;
3634 return rmdir($dir);
3637 //Function to check if a directory exists
3638 //and, optionally, create it
3639 function check_dir_exists($dir,$create=false) {
3641 global $CFG;
3643 $status = true;
3644 if(!is_dir($dir)) {
3645 if (!$create) {
3646 $status = false;
3647 } else {
3648 umask(0000);
3649 $status = mkdir ($dir,$CFG->directorypermissions);
3652 return $status;
3655 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3656 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3657 //This functions are used to copy any file or directory ($from_file)
3658 //to a new file or directory ($to_file). It works recursively and
3659 //mantains file perms.
3660 //I've copied it from: http://www.php.net/manual/en/function.copy.php
3661 //Little modifications done
3663 function copy_file ($from_file,$to_file) {
3665 global $CFG;
3667 if (is_file($from_file)) {
3668 umask(0000);
3669 if (copy($from_file,$to_file)) {
3670 chmod($to_file,$CFG->filepermissions);
3671 return true;
3673 return false;
3675 else if (is_dir($from_file)) {
3676 return copy_dir($from_file,$to_file);
3678 else{
3679 return false;
3683 function copy_dir($from_file,$to_file) {
3685 global $CFG;
3687 if (!is_dir($to_file)) {
3688 umask(0000);
3689 $status = mkdir($to_file,$CFG->directorypermissions);
3691 $dir = opendir($from_file);
3692 while ($file=readdir($dir)) {
3693 if ($file=="." || $file=="..") {
3694 continue;
3696 $status = copy_file ("$from_file/$file","$to_file/$file");
3698 closedir($dir);
3699 return $status;
3703 function zip_files ($originalfiles, $destination) {
3704 //Zip an array of files/dirs to a destination zip file
3705 //Both parameters must be FULL paths to the files/dirs
3707 global $CFG;
3709 //Extract everything from destination
3710 $path_parts = pathinfo(cleardoubleslashes($destination));
3711 $destpath = $path_parts["dirname"]; //The path of the zip file
3712 $destfilename = $path_parts["basename"]; //The name of the zip file
3713 $extension = $path_parts["extension"]; //The extension of the file
3715 //If no file, error
3716 if (empty($destfilename)) {
3717 return false;
3720 //If no extension, add it
3721 if (empty($extension)) {
3722 $extension = 'zip';
3723 $destfilename = $destfilename.'.'.$extension;
3726 //Check destination path exists
3727 if (!is_dir($destpath)) {
3728 return false;
3731 //Check destination path is writable. TODO!!
3733 //Clean destination filename
3734 $destfilename = clean_filename($destfilename);
3736 //Now check and prepare every file
3737 $files = array();
3738 $origpath = NULL;
3740 foreach ($originalfiles as $file) { //Iterate over each file
3741 //Check for every file
3742 $tempfile = cleardoubleslashes($file); // no doubleslashes!
3743 //Calculate the base path for all files if it isn't set
3744 if ($origpath === NULL) {
3745 $origpath = rtrim(cleardoubleslashes(dirname($tempfile)), "/");
3747 //See if the file is readable
3748 if (!is_readable($tempfile)) { //Is readable
3749 continue;
3751 //See if the file/dir is in the same directory than the rest
3752 if (rtrim(cleardoubleslashes(dirname($tempfile)), "/") != $origpath) {
3753 continue;
3755 //Add the file to the array
3756 $files[] = $tempfile;
3759 //Everything is ready:
3760 // -$origpath is the path where ALL the files to be compressed reside (dir).
3761 // -$destpath is the destination path where the zip file will go (dir).
3762 // -$files is an array of files/dirs to compress (fullpath)
3763 // -$destfilename is the name of the zip file (without path)
3765 //print_object($files); //Debug
3767 if (empty($CFG->zip)) { // Use built-in php-based zip function
3769 include_once("$CFG->libdir/pclzip/pclzip.lib.php");
3770 $archive = new PclZip(cleardoubleslashes("$destpath/$destfilename"));
3771 if (($list = $archive->create($files, PCLZIP_OPT_REMOVE_PATH,$origpath) == 0)) {
3772 notice($archive->errorInfo(true));
3773 return false;
3776 } else { // Use external zip program
3778 $filestozip = "";
3779 foreach ($files as $filetozip) {
3780 $filestozip .= escapeshellarg(basename($filetozip));
3781 $filestozip .= " ";
3783 //Construct the command
3784 $separator = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? ' &' : ' ;';
3785 $command = 'cd '.escapeshellarg($origpath).$separator.
3786 escapeshellarg($CFG->zip).' -r '.
3787 escapeshellarg(cleardoubleslashes("$destpath/$destfilename")).' '.$filestozip;
3788 //All converted to backslashes in WIN
3789 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
3790 $command = str_replace('/','\\',$command);
3792 Exec($command);
3794 return true;
3797 function unzip_file ($zipfile, $destination = '', $showstatus = true) {
3798 //Unzip one zip file to a destination dir
3799 //Both parameters must be FULL paths
3800 //If destination isn't specified, it will be the
3801 //SAME directory where the zip file resides.
3803 global $CFG;
3805 //Extract everything from zipfile
3806 $path_parts = pathinfo(cleardoubleslashes($zipfile));
3807 $zippath = $path_parts["dirname"]; //The path of the zip file
3808 $zipfilename = $path_parts["basename"]; //The name of the zip file
3809 $extension = $path_parts["extension"]; //The extension of the file
3811 //If no file, error
3812 if (empty($zipfilename)) {
3813 return false;
3816 //If no extension, error
3817 if (empty($extension)) {
3818 return false;
3821 //Clear $zipfile
3822 $zipfile = cleardoubleslashes($zipfile);
3824 //Check zipfile exists
3825 if (!file_exists($zipfile)) {
3826 return false;
3829 //If no destination, passed let's go with the same directory
3830 if (empty($destination)) {
3831 $destination = $zippath;
3834 //Clear $destination
3835 $destpath = rtrim(cleardoubleslashes($destination), "/");
3837 //Check destination path exists
3838 if (!is_dir($destpath)) {
3839 return false;
3842 //Check destination path is writable. TODO!!
3844 //Everything is ready:
3845 // -$zippath is the path where the zip file resides (dir)
3846 // -$zipfilename is the name of the zip file (without path)
3847 // -$destpath is the destination path where the zip file will uncompressed (dir)
3849 if (empty($CFG->unzip)) { // Use built-in php-based unzip function
3851 include_once("$CFG->libdir/pclzip/pclzip.lib.php");
3852 $archive = new PclZip(cleardoubleslashes("$zippath/$zipfilename"));
3853 if (!$list = $archive->extract(PCLZIP_OPT_PATH, $destpath,
3854 PCLZIP_CB_PRE_EXTRACT, 'unzip_cleanfilename')) {
3855 notice($archive->errorInfo(true));
3856 return false;
3859 } else { // Use external unzip program
3861 $separator = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? ' &' : ' ;';
3862 $redirection = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? '' : ' 2>&1';
3864 $command = 'cd '.escapeshellarg($zippath).$separator.
3865 escapeshellarg($CFG->unzip).' -o '.
3866 escapeshellarg(cleardoubleslashes("$zippath/$zipfilename")).' -d '.
3867 escapeshellarg($destpath).$redirection;
3868 //All converted to backslashes in WIN
3869 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
3870 $command = str_replace('/','\\',$command);
3872 Exec($command,$list);
3875 //Display some info about the unzip execution
3876 if ($showstatus) {
3877 unzip_show_status($list,$destpath);
3880 return true;
3883 function unzip_cleanfilename ($p_event, &$p_header) {
3884 //This function is used as callback in unzip_file() function
3885 //to clean illegal characters for given platform and to prevent directory traversal.
3886 //Produces the same result as info-zip unzip.
3887 $p_header['filename'] = ereg_replace('[[:cntrl:]]', '', $p_header['filename']); //strip control chars first!
3888 $p_header['filename'] = ereg_replace('\.\.+', '', $p_header['filename']); //directory traversal protection
3889 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
3890 $p_header['filename'] = ereg_replace('[:*"?<>|]', '_', $p_header['filename']); //replace illegal chars
3891 $p_header['filename'] = ereg_replace('^([a-zA-Z])_', '\1:', $p_header['filename']); //repair drive letter
3892 } else {
3893 //Add filtering for other systems here
3894 // BSD: none (tested)
3895 // Linux: ??
3896 // MacosX: ??
3898 $p_header['filename'] = cleardoubleslashes($p_header['filename']); //normalize the slashes/backslashes
3899 return 1;
3903 function unzip_show_status ($list,$removepath) {
3904 //This function shows the results of the unzip execution
3905 //depending of the value of the $CFG->zip, results will be
3906 //text or an array of files.
3908 global $CFG;
3910 if (empty($CFG->unzip)) { // Use built-in php-based zip function
3911 $strname = get_string("name");
3912 $strsize = get_string("size");
3913 $strmodified = get_string("modified");
3914 $strstatus = get_string("status");
3915 echo "<table cellpadding=\"4\" cellspacing=\"2\" border=\"0\" width=\"640\">";
3916 echo "<tr><th class=\"header\" align=\"left\">$strname</th>";
3917 echo "<th class=\"header\" align=\"right\">$strsize</th>";
3918 echo "<th class=\"header\" align=\"right\">$strmodified</th>";
3919 echo "<th class=\"header\" align=\"right\">$strstatus</th></tr>";
3920 foreach ($list as $item) {
3921 echo "<tr>";
3922 $item['filename'] = str_replace(cleardoubleslashes($removepath).'/', "", $item['filename']);
3923 print_cell("left", $item['filename']);
3924 if (! $item['folder']) {
3925 print_cell("right", display_size($item['size']));
3926 } else {
3927 echo "<td>&nbsp;</td>";
3929 $filedate = userdate($item['mtime'], get_string("strftimedatetime"));
3930 print_cell("right", $filedate);
3931 print_cell("right", $item['status']);
3932 echo "</tr>";
3934 echo "</table>";
3936 } else { // Use external zip program
3937 print_simple_box_start("center");
3938 echo "<pre>";
3939 foreach ($list as $item) {
3940 echo str_replace(cleardoubleslashes($removepath.'/'), '', $item).'<br />';
3942 echo "</pre>";
3943 print_simple_box_end();
3947 function isadmin($userid=0) {
3948 global $USER;
3950 static $admins, $nonadmins;
3952 if (!isset($admins)) {
3953 $admins = array();
3954 $nonadmins = array();
3957 if (empty($userid)) {
3958 if (empty($USER)) { // maybe not logged in
3959 return false;
3960 } else {
3961 $userid = (int) $USER->ident;
3965 if (in_array($userid, $admins)) {
3966 return true;
3967 } else if (in_array($userid, $nonadmins)) {
3968 return false;
3969 } else if (user_flag_get('admin', $userid)) {
3970 $admins[] = $userid;
3971 return true;
3972 } else {
3973 $nonadmins[] = $userid;
3974 return false;
3981 function get_admins() {
3982 global $CFG;
3983 return get_records_sql('SELECT u.* FROM '.$CFG->prefix.'users u
3984 JOIN '.$CFG->prefix.'user_flags uf ON u.ident = uf.user_id
3985 WHERE flag = ?',array('admin'));
3989 function get_admin() {
3990 global $CFG;
3991 return get_record_sql('SELECT u.* FROM '.$CFG->prefix.'users u
3992 JOIN '.$CFG->prefix.'user_flags uf ON u.ident = uf.user_id
3993 WHERE flag = ? ORDER BY ident',array('admin'),true);
3998 // cli_die($str) - a perlish die()
4000 // this function call will exit with a warning and an exit code
4001 // that clearly indicates that something went wrong.
4003 // We shouldn't need this, but due to PHP's web-centric heritage,
4004 // die()/exit() cant print a warnign _and_ set a non-success exit
4005 // code. Silly thing -- disregarding POSIX and friends doesn't get
4006 // you very far ;-D
4007 function cli_die ($str, $code) {
4008 trigger_error($str);
4009 exit(1);
4015 // Take a comma-separated string of keywords and create the relevant tag entries
4016 // in the database. Returns a cleaned comma-separated keyword string.
4017 function insert_tags_from_string ($string, $tagtype, $ref, $access, $owner) {
4019 $ref = (int) $ref;
4020 $owner = (int) $owner;
4021 $tagtype = trim($tagtype);
4022 $access = trim($access);
4023 $string = trim($string);
4024 $keywords = "";
4026 $string = str_replace("\n", "", $string);
4027 $string = str_replace("\r", "", $string);
4028 if ($string) {
4029 $keyword_list = explode(",", $string);
4030 $keyword_list = array_unique($keyword_list);
4031 sort($keyword_list);
4032 if (sizeof($keyword_list) > 0) {
4033 foreach($keyword_list as $key => $list_item) {
4034 $list_item = trim($list_item);
4035 if ($list_item) {
4036 if ($key > 0) {
4037 $keywords .= ", ";
4039 $keywords .= $list_item;
4040 $t = new StdClass;
4041 $t->tagtype = $tagtype;
4042 $t->access = $access;
4043 $t->tag = $list_item;
4044 $t->ref = $ref;
4045 $t->owner = $owner;
4046 insert_record('tags', $t);
4052 return($keywords);
4056 // return the "this is restricted" text for a given access value
4057 // functionised to reduce code duplication
4058 function get_access_description ($accessvalue) {
4060 if ($accessvalue != "PUBLIC") {
4061 if ($accessvalue == "LOGGED_IN") {
4062 $title = "[" . __gettext("Logged in users") . "] ";
4063 } else if (substr_count($accessvalue, "user") > 0) {
4064 $title = "[" . __gettext("Private") . "] ";
4065 } else {
4066 $title = "[" . __gettext("Restricted") . "] ";
4068 } else {
4069 $title = "";
4072 return $title;
4075 // Activate URLs - turns any URLs found into links
4076 function activate_urls ($str) {
4078 // Function for URL autodiscovery
4080 $search = array();
4081 $replace = array();
4083 // lift all links, images and image maps
4084 $url_tags = array (
4085 "'<a[^>]*>.*?</a>'si",
4086 "'<map[^>]*>.*?</map>'si",
4087 "'<script[^>]*>.*?</script>'si",
4088 "'<style[^>]*>.*?</style>'si",
4089 "'<[^>]+>'si"
4092 foreach($url_tags as $url_tag)
4094 preg_match_all($url_tag, $str, $matches, PREG_SET_ORDER);
4095 foreach($matches as $match)
4097 $key = "<" . md5($match[0]) . ">";
4098 $search[] = $key;
4099 $replace[] = $match[0];
4103 $str = str_replace($replace, $search, $str);
4105 // indicate where urls end if they have these trailing special chars
4106 $sentinals = array("/&(quot|#34);/i", // Replace html entities
4107 "/&(lt|#60);/i",
4108 "/&(gt|#62);/i",
4109 "/&(nbsp|#160);/i",
4110 "/&(iexcl|#161);/i",
4111 "/&(cent|#162);/i",
4112 "/&(pound|#163);/i",
4113 "/&(copy|#169);/i");
4115 $str = preg_replace($sentinals, "<marker>\\0", $str);
4117 // URL into links
4118 $str =
4119 preg_replace( "|\w{3,10}://[\w\.\-_]+(:\d+)?[^\s\"\'<>\(\)\{\}]*|",
4120 "<a href=\"\\0\">[".__gettext("Click to view link") . "]</a>", $str );
4122 $str = str_replace("<marker>", '', $str);
4123 return str_replace($search, $replace, $str);