|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00003 // This file is part of Moodle - http://moodle.org/ 00004 // 00005 // Moodle is free software: you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation, either version 3 of the License, or 00008 // (at your option) any later version. 00009 // 00010 // Moodle is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00017 00032 defined('MOODLE_INTERNAL') || die(); 00033 00035 00037 00040 define('YEARSECS', 31536000); 00041 00045 define('WEEKSECS', 604800); 00046 00050 define('DAYSECS', 86400); 00051 00055 define('HOURSECS', 3600); 00056 00060 define('MINSECS', 60); 00061 00065 define('DAYMINS', 1440); 00066 00070 define('HOURMINS', 60); 00071 00074 00075 00076 00080 define('PARAM_ALPHA', 'alpha'); 00081 00086 define('PARAM_ALPHAEXT', 'alphaext'); 00087 00091 define('PARAM_ALPHANUM', 'alphanum'); 00092 00096 define('PARAM_ALPHANUMEXT', 'alphanumext'); 00097 00101 define('PARAM_AUTH', 'auth'); 00102 00106 define('PARAM_BASE64', 'base64'); 00107 00111 define('PARAM_BOOL', 'bool'); 00112 00117 define('PARAM_CAPABILITY', 'capability'); 00118 00122 define('PARAM_CLEANHTML', 'cleanhtml'); 00123 00127 define('PARAM_EMAIL', 'email'); 00128 00132 define('PARAM_FILE', 'file'); 00133 00137 define('PARAM_FLOAT', 'float'); 00138 00142 define('PARAM_HOST', 'host'); 00143 00147 define('PARAM_INT', 'int'); 00148 00152 define('PARAM_LANG', 'lang'); 00153 00157 define('PARAM_LOCALURL', 'localurl'); 00158 00162 define('PARAM_NOTAGS', 'notags'); 00163 00168 define('PARAM_PATH', 'path'); 00169 00173 define('PARAM_PEM', 'pem'); 00174 00178 define('PARAM_PERMISSION', 'permission'); 00179 00183 define('PARAM_RAW', 'raw'); 00184 00188 define('PARAM_RAW_TRIMMED', 'raw_trimmed'); 00189 00193 define('PARAM_SAFEDIR', 'safedir'); 00194 00198 define('PARAM_SAFEPATH', 'safepath'); 00199 00203 define('PARAM_SEQUENCE', 'sequence'); 00204 00208 define('PARAM_TAG', 'tag'); 00209 00213 define('PARAM_TAGLIST', 'taglist'); 00214 00218 define('PARAM_TEXT', 'text'); 00219 00223 define('PARAM_THEME', 'theme'); 00224 00228 define('PARAM_URL', 'url'); 00229 00233 define('PARAM_USERNAME', 'username'); 00234 00238 define('PARAM_STRINGID', 'stringid'); 00239 00241 00246 define('PARAM_CLEAN', 'clean'); 00247 00251 define('PARAM_INTEGER', 'int'); 00252 00256 define('PARAM_NUMBER', 'float'); 00257 00262 define('PARAM_ACTION', 'alphanumext'); 00263 00268 define('PARAM_FORMAT', 'alphanumext'); 00269 00273 define('PARAM_MULTILANG', 'text'); 00274 00280 define('PARAM_TIMEZONE', 'timezone'); 00281 00285 define('PARAM_CLEANFILE', 'file'); 00286 00293 define('PARAM_COMPONENT', 'component'); 00294 00300 define('PARAM_AREA', 'area'); 00301 00307 define('PARAM_PLUGIN', 'plugin'); 00308 00309 00311 00315 define('VALUE_REQUIRED', 1); 00316 00320 define('VALUE_OPTIONAL', 2); 00321 00325 define('VALUE_DEFAULT', 0); 00326 00330 define('NULL_NOT_ALLOWED', false); 00331 00335 define('NULL_ALLOWED', true); 00336 00338 00341 define('PAGE_COURSE_VIEW', 'course-view'); 00342 00344 define('GETREMOTEADDR_SKIP_HTTP_CLIENT_IP', '1'); 00346 define('GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR', '2'); 00347 00349 define ('BLOG_USER_LEVEL', 1); 00350 define ('BLOG_GROUP_LEVEL', 2); 00351 define ('BLOG_COURSE_LEVEL', 3); 00352 define ('BLOG_SITE_LEVEL', 4); 00353 define ('BLOG_GLOBAL_LEVEL', 5); 00354 00355 00357 00364 define('TAG_MAX_LENGTH', 50); 00365 00367 define ('PASSWORD_LOWER', 'abcdefghijklmnopqrstuvwxyz'); 00368 define ('PASSWORD_UPPER', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'); 00369 define ('PASSWORD_DIGITS', '0123456789'); 00370 define ('PASSWORD_NONALPHANUM', '.,;:!?_-+/*@#&$'); 00371 00373 // Used for plugin_supports() to report features that are, or are not, supported by a module. 00374 00376 define('FEATURE_GRADE_HAS_GRADE', 'grade_has_grade'); 00378 define('FEATURE_GRADE_OUTCOMES', 'outcomes'); 00380 define('FEATURE_ADVANCED_GRADING', 'grade_advanced_grading'); 00381 00383 define('FEATURE_COMPLETION_TRACKS_VIEWS', 'completion_tracks_views'); 00385 define('FEATURE_COMPLETION_HAS_RULES', 'completion_has_rules'); 00386 00388 define('FEATURE_NO_VIEW_LINK', 'viewlink'); 00390 define('FEATURE_IDNUMBER', 'idnumber'); 00392 define('FEATURE_GROUPS', 'groups'); 00394 define('FEATURE_GROUPINGS', 'groupings'); 00396 define('FEATURE_GROUPMEMBERSONLY', 'groupmembersonly'); 00397 00399 define('FEATURE_MOD_ARCHETYPE', 'mod_archetype'); 00401 define('FEATURE_MOD_INTRO', 'mod_intro'); 00403 define('FEATURE_MODEDIT_DEFAULT_COMPLETION', 'modedit_default_completion'); 00404 00405 define('FEATURE_COMMENT', 'comment'); 00406 00407 define('FEATURE_RATE', 'rate'); 00409 define('FEATURE_BACKUP_MOODLE2', 'backup_moodle2'); 00410 00412 define('FEATURE_SHOW_DESCRIPTION', 'showdescription'); 00413 00415 define('MOD_ARCHETYPE_OTHER', 0); 00417 define('MOD_ARCHETYPE_RESOURCE', 1); 00419 define('MOD_ARCHETYPE_ASSIGNMENT', 2); 00420 00428 define('EXTERNAL_TOKEN_PERMANENT', 0); 00429 00436 define('EXTERNAL_TOKEN_EMBEDDED', 1); 00437 00441 define('HOMEPAGE_SITE', 0); 00445 define('HOMEPAGE_MY', 1); 00449 define('HOMEPAGE_USER', 2); 00450 00454 define('HUB_HUBDIRECTORYURL', "http://hubdirectory.moodle.org"); 00455 00456 00460 define('HUB_MOODLEORGHUBURL', "http://hub.moodle.org"); 00461 00465 define('MOODLE_OFFICIAL_MOBILE_SERVICE', 'moodle_mobile_app'); 00466 00468 00485 function required_param($parname, $type) { 00486 if (func_num_args() != 2 or empty($parname) or empty($type)) { 00487 throw new coding_exception('required_param() requires $parname and $type to be specified (parameter: '.$parname.')'); 00488 } 00489 if (isset($_POST[$parname])) { // POST has precedence 00490 $param = $_POST[$parname]; 00491 } else if (isset($_GET[$parname])) { 00492 $param = $_GET[$parname]; 00493 } else { 00494 print_error('missingparam', '', '', $parname); 00495 } 00496 00497 if (is_array($param)) { 00498 debugging('Invalid array parameter detected in required_param(): '.$parname); 00499 // TODO: switch to fatal error in Moodle 2.3 00500 //print_error('missingparam', '', '', $parname); 00501 return required_param_array($parname, $type); 00502 } 00503 00504 return clean_param($param, $type); 00505 } 00506 00523 function required_param_array($parname, $type) { 00524 if (func_num_args() != 2 or empty($parname) or empty($type)) { 00525 throw new coding_exception('required_param_array() requires $parname and $type to be specified (parameter: '.$parname.')'); 00526 } 00527 if (isset($_POST[$parname])) { // POST has precedence 00528 $param = $_POST[$parname]; 00529 } else if (isset($_GET[$parname])) { 00530 $param = $_GET[$parname]; 00531 } else { 00532 print_error('missingparam', '', '', $parname); 00533 } 00534 if (!is_array($param)) { 00535 print_error('missingparam', '', '', $parname); 00536 } 00537 00538 $result = array(); 00539 foreach($param as $key=>$value) { 00540 if (!preg_match('/^[a-z0-9_-]+$/i', $key)) { 00541 debugging('Invalid key name in required_param_array() detected: '.$key.', parameter: '.$parname); 00542 continue; 00543 } 00544 $result[$key] = clean_param($value, $type); 00545 } 00546 00547 return $result; 00548 } 00549 00566 function optional_param($parname, $default, $type) { 00567 if (func_num_args() != 3 or empty($parname) or empty($type)) { 00568 throw new coding_exception('optional_param() requires $parname, $default and $type to be specified (parameter: '.$parname.')'); 00569 } 00570 if (!isset($default)) { 00571 $default = null; 00572 } 00573 00574 if (isset($_POST[$parname])) { // POST has precedence 00575 $param = $_POST[$parname]; 00576 } else if (isset($_GET[$parname])) { 00577 $param = $_GET[$parname]; 00578 } else { 00579 return $default; 00580 } 00581 00582 if (is_array($param)) { 00583 debugging('Invalid array parameter detected in required_param(): '.$parname); 00584 // TODO: switch to $default in Moodle 2.3 00585 //return $default; 00586 return optional_param_array($parname, $default, $type); 00587 } 00588 00589 return clean_param($param, $type); 00590 } 00591 00608 function optional_param_array($parname, $default, $type) { 00609 if (func_num_args() != 3 or empty($parname) or empty($type)) { 00610 throw new coding_exception('optional_param_array() requires $parname, $default and $type to be specified (parameter: '.$parname.')'); 00611 } 00612 00613 if (isset($_POST[$parname])) { // POST has precedence 00614 $param = $_POST[$parname]; 00615 } else if (isset($_GET[$parname])) { 00616 $param = $_GET[$parname]; 00617 } else { 00618 return $default; 00619 } 00620 if (!is_array($param)) { 00621 debugging('optional_param_array() expects array parameters only: '.$parname); 00622 return $default; 00623 } 00624 00625 $result = array(); 00626 foreach($param as $key=>$value) { 00627 if (!preg_match('/^[a-z0-9_-]+$/i', $key)) { 00628 debugging('Invalid key name in optional_param_array() detected: '.$key.', parameter: '.$parname); 00629 continue; 00630 } 00631 $result[$key] = clean_param($value, $type); 00632 } 00633 00634 return $result; 00635 } 00636 00650 function validate_param($param, $type, $allownull=NULL_NOT_ALLOWED, $debuginfo='') { 00651 if (is_null($param)) { 00652 if ($allownull == NULL_ALLOWED) { 00653 return null; 00654 } else { 00655 throw new invalid_parameter_exception($debuginfo); 00656 } 00657 } 00658 if (is_array($param) or is_object($param)) { 00659 throw new invalid_parameter_exception($debuginfo); 00660 } 00661 00662 $cleaned = clean_param($param, $type); 00663 if ((string)$param !== (string)$cleaned) { 00664 // conversion to string is usually lossless 00665 throw new invalid_parameter_exception($debuginfo); 00666 } 00667 00668 return $cleaned; 00669 } 00670 00683 function clean_param_array(array $param = null, $type, $recursive = false) { 00684 $param = (array)$param; // convert null to empty array 00685 foreach ($param as $key => $value) { 00686 if (is_array($value)) { 00687 if ($recursive) { 00688 $param[$key] = clean_param_array($value, $type, true); 00689 } else { 00690 throw new coding_exception('clean_param_array() can not process multidimensional arrays when $recursive is false.'); 00691 } 00692 } else { 00693 $param[$key] = clean_param($value, $type); 00694 } 00695 } 00696 return $param; 00697 } 00698 00712 function clean_param($param, $type) { 00713 00714 global $CFG; 00715 00716 if (is_array($param)) { 00717 throw new coding_exception('clean_param() can not process arrays, please use clean_param_array() instead.'); 00718 } else if (is_object($param)) { 00719 if (method_exists($param, '__toString')) { 00720 $param = $param->__toString(); 00721 } else { 00722 throw new coding_exception('clean_param() can not process objects, please use clean_param_array() instead.'); 00723 } 00724 } 00725 00726 switch ($type) { 00727 case PARAM_RAW: // no cleaning at all 00728 $param = fix_utf8($param); 00729 return $param; 00730 00731 case PARAM_RAW_TRIMMED: // no cleaning, but strip leading and trailing whitespace. 00732 $param = fix_utf8($param); 00733 return trim($param); 00734 00735 case PARAM_CLEAN: // General HTML cleaning, try to use more specific type if possible 00736 // this is deprecated!, please use more specific type instead 00737 if (is_numeric($param)) { 00738 return $param; 00739 } 00740 $param = fix_utf8($param); 00741 return clean_text($param); // Sweep for scripts, etc 00742 00743 case PARAM_CLEANHTML: // clean html fragment 00744 $param = fix_utf8($param); 00745 $param = clean_text($param, FORMAT_HTML); // Sweep for scripts, etc 00746 return trim($param); 00747 00748 case PARAM_INT: 00749 return (int)$param; // Convert to integer 00750 00751 case PARAM_FLOAT: 00752 case PARAM_NUMBER: 00753 return (float)$param; // Convert to float 00754 00755 case PARAM_ALPHA: // Remove everything not a-z 00756 return preg_replace('/[^a-zA-Z]/i', '', $param); 00757 00758 case PARAM_ALPHAEXT: // Remove everything not a-zA-Z_- (originally allowed "/" too) 00759 return preg_replace('/[^a-zA-Z_-]/i', '', $param); 00760 00761 case PARAM_ALPHANUM: // Remove everything not a-zA-Z0-9 00762 return preg_replace('/[^A-Za-z0-9]/i', '', $param); 00763 00764 case PARAM_ALPHANUMEXT: // Remove everything not a-zA-Z0-9_- 00765 return preg_replace('/[^A-Za-z0-9_-]/i', '', $param); 00766 00767 case PARAM_SEQUENCE: // Remove everything not 0-9, 00768 return preg_replace('/[^0-9,]/i', '', $param); 00769 00770 case PARAM_BOOL: // Convert to 1 or 0 00771 $tempstr = strtolower($param); 00772 if ($tempstr === 'on' or $tempstr === 'yes' or $tempstr === 'true') { 00773 $param = 1; 00774 } else if ($tempstr === 'off' or $tempstr === 'no' or $tempstr === 'false') { 00775 $param = 0; 00776 } else { 00777 $param = empty($param) ? 0 : 1; 00778 } 00779 return $param; 00780 00781 case PARAM_NOTAGS: // Strip all tags 00782 $param = fix_utf8($param); 00783 return strip_tags($param); 00784 00785 case PARAM_TEXT: // leave only tags needed for multilang 00786 $param = fix_utf8($param); 00787 // if the multilang syntax is not correct we strip all tags 00788 // because it would break xhtml strict which is required for accessibility standards 00789 // please note this cleaning does not strip unbalanced '>' for BC compatibility reasons 00790 do { 00791 if (strpos($param, '</lang>') !== false) { 00792 // old and future mutilang syntax 00793 $param = strip_tags($param, '<lang>'); 00794 if (!preg_match_all('/<.*>/suU', $param, $matches)) { 00795 break; 00796 } 00797 $open = false; 00798 foreach ($matches[0] as $match) { 00799 if ($match === '</lang>') { 00800 if ($open) { 00801 $open = false; 00802 continue; 00803 } else { 00804 break 2; 00805 } 00806 } 00807 if (!preg_match('/^<lang lang="[a-zA-Z0-9_-]+"\s*>$/u', $match)) { 00808 break 2; 00809 } else { 00810 $open = true; 00811 } 00812 } 00813 if ($open) { 00814 break; 00815 } 00816 return $param; 00817 00818 } else if (strpos($param, '</span>') !== false) { 00819 // current problematic multilang syntax 00820 $param = strip_tags($param, '<span>'); 00821 if (!preg_match_all('/<.*>/suU', $param, $matches)) { 00822 break; 00823 } 00824 $open = false; 00825 foreach ($matches[0] as $match) { 00826 if ($match === '</span>') { 00827 if ($open) { 00828 $open = false; 00829 continue; 00830 } else { 00831 break 2; 00832 } 00833 } 00834 if (!preg_match('/^<span(\s+lang="[a-zA-Z0-9_-]+"|\s+class="multilang"){2}\s*>$/u', $match)) { 00835 break 2; 00836 } else { 00837 $open = true; 00838 } 00839 } 00840 if ($open) { 00841 break; 00842 } 00843 return $param; 00844 } 00845 } while (false); 00846 // easy, just strip all tags, if we ever want to fix orphaned '&' we have to do that in format_string() 00847 return strip_tags($param); 00848 00849 case PARAM_COMPONENT: 00850 // we do not want any guessing here, either the name is correct or not 00851 // please note only normalised component names are accepted 00852 if (!preg_match('/^[a-z]+(_[a-z][a-z0-9_]*)?[a-z0-9]$/', $param)) { 00853 return ''; 00854 } 00855 if (strpos($param, '__') !== false) { 00856 return ''; 00857 } 00858 if (strpos($param, 'mod_') === 0) { 00859 // module names must not contain underscores because we need to differentiate them from invalid plugin types 00860 if (substr_count($param, '_') != 1) { 00861 return ''; 00862 } 00863 } 00864 return $param; 00865 00866 case PARAM_PLUGIN: 00867 case PARAM_AREA: 00868 // we do not want any guessing here, either the name is correct or not 00869 if (!preg_match('/^[a-z][a-z0-9_]*[a-z0-9]$/', $param)) { 00870 return ''; 00871 } 00872 if (strpos($param, '__') !== false) { 00873 return ''; 00874 } 00875 return $param; 00876 00877 case PARAM_SAFEDIR: // Remove everything not a-zA-Z0-9_- 00878 return preg_replace('/[^a-zA-Z0-9_-]/i', '', $param); 00879 00880 case PARAM_SAFEPATH: // Remove everything not a-zA-Z0-9/_- 00881 return preg_replace('/[^a-zA-Z0-9\/_-]/i', '', $param); 00882 00883 case PARAM_FILE: // Strip all suspicious characters from filename 00884 $param = fix_utf8($param); 00885 $param = preg_replace('~[[:cntrl:]]|[&<>"`\|\':\\\\/]~u', '', $param); 00886 $param = preg_replace('~\.\.+~', '', $param); 00887 if ($param === '.') { 00888 $param = ''; 00889 } 00890 return $param; 00891 00892 case PARAM_PATH: // Strip all suspicious characters from file path 00893 $param = fix_utf8($param); 00894 $param = str_replace('\\', '/', $param); 00895 $param = preg_replace('~[[:cntrl:]]|[&<>"`\|\':]~u', '', $param); 00896 $param = preg_replace('~\.\.+~', '', $param); 00897 $param = preg_replace('~//+~', '/', $param); 00898 return preg_replace('~/(\./)+~', '/', $param); 00899 00900 case PARAM_HOST: // allow FQDN or IPv4 dotted quad 00901 $param = preg_replace('/[^\.\d\w-]/','', $param ); // only allowed chars 00902 // match ipv4 dotted quad 00903 if (preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/',$param, $match)){ 00904 // confirm values are ok 00905 if ( $match[0] > 255 00906 || $match[1] > 255 00907 || $match[3] > 255 00908 || $match[4] > 255 ) { 00909 // hmmm, what kind of dotted quad is this? 00910 $param = ''; 00911 } 00912 } elseif ( preg_match('/^[\w\d\.-]+$/', $param) // dots, hyphens, numbers 00913 && !preg_match('/^[\.-]/', $param) // no leading dots/hyphens 00914 && !preg_match('/[\.-]$/', $param) // no trailing dots/hyphens 00915 ) { 00916 // all is ok - $param is respected 00917 } else { 00918 // all is not ok... 00919 $param=''; 00920 } 00921 return $param; 00922 00923 case PARAM_URL: // allow safe ftp, http, mailto urls 00924 $param = fix_utf8($param); 00925 include_once($CFG->dirroot . '/lib/validateurlsyntax.php'); 00926 if (!empty($param) && validateUrlSyntax($param, 's?H?S?F?E?u-P-a?I?p?f?q?r?')) { 00927 // all is ok, param is respected 00928 } else { 00929 $param =''; // not really ok 00930 } 00931 return $param; 00932 00933 case PARAM_LOCALURL: // allow http absolute, root relative and relative URLs within wwwroot 00934 $param = clean_param($param, PARAM_URL); 00935 if (!empty($param)) { 00936 if (preg_match(':^/:', $param)) { 00937 // root-relative, ok! 00938 } elseif (preg_match('/^'.preg_quote($CFG->wwwroot, '/').'/i',$param)) { 00939 // absolute, and matches our wwwroot 00940 } else { 00941 // relative - let's make sure there are no tricks 00942 if (validateUrlSyntax('/' . $param, 's-u-P-a-p-f+q?r?')) { 00943 // looks ok. 00944 } else { 00945 $param = ''; 00946 } 00947 } 00948 } 00949 return $param; 00950 00951 case PARAM_PEM: 00952 $param = trim($param); 00953 // PEM formatted strings may contain letters/numbers and the symbols 00954 // forward slash: / 00955 // plus sign: + 00956 // equal sign: = 00957 // , surrounded by BEGIN and END CERTIFICATE prefix and suffixes 00958 if (preg_match('/^-----BEGIN CERTIFICATE-----([\s\w\/\+=]+)-----END CERTIFICATE-----$/', trim($param), $matches)) { 00959 list($wholething, $body) = $matches; 00960 unset($wholething, $matches); 00961 $b64 = clean_param($body, PARAM_BASE64); 00962 if (!empty($b64)) { 00963 return "-----BEGIN CERTIFICATE-----\n$b64\n-----END CERTIFICATE-----\n"; 00964 } else { 00965 return ''; 00966 } 00967 } 00968 return ''; 00969 00970 case PARAM_BASE64: 00971 if (!empty($param)) { 00972 // PEM formatted strings may contain letters/numbers and the symbols 00973 // forward slash: / 00974 // plus sign: + 00975 // equal sign: = 00976 if (0 >= preg_match('/^([\s\w\/\+=]+)$/', trim($param))) { 00977 return ''; 00978 } 00979 $lines = preg_split('/[\s]+/', $param, -1, PREG_SPLIT_NO_EMPTY); 00980 // Each line of base64 encoded data must be 64 characters in 00981 // length, except for the last line which may be less than (or 00982 // equal to) 64 characters long. 00983 for ($i=0, $j=count($lines); $i < $j; $i++) { 00984 if ($i + 1 == $j) { 00985 if (64 < strlen($lines[$i])) { 00986 return ''; 00987 } 00988 continue; 00989 } 00990 00991 if (64 != strlen($lines[$i])) { 00992 return ''; 00993 } 00994 } 00995 return implode("\n",$lines); 00996 } else { 00997 return ''; 00998 } 00999 01000 case PARAM_TAG: 01001 $param = fix_utf8($param); 01002 // Please note it is not safe to use the tag name directly anywhere, 01003 // it must be processed with s(), urlencode() before embedding anywhere. 01004 // remove some nasties 01005 $param = preg_replace('~[[:cntrl:]]|[<>`]~u', '', $param); 01006 //convert many whitespace chars into one 01007 $param = preg_replace('/\s+/', ' ', $param); 01008 $textlib = textlib_get_instance(); 01009 $param = $textlib->substr(trim($param), 0, TAG_MAX_LENGTH); 01010 return $param; 01011 01012 case PARAM_TAGLIST: 01013 $param = fix_utf8($param); 01014 $tags = explode(',', $param); 01015 $result = array(); 01016 foreach ($tags as $tag) { 01017 $res = clean_param($tag, PARAM_TAG); 01018 if ($res !== '') { 01019 $result[] = $res; 01020 } 01021 } 01022 if ($result) { 01023 return implode(',', $result); 01024 } else { 01025 return ''; 01026 } 01027 01028 case PARAM_CAPABILITY: 01029 if (get_capability_info($param)) { 01030 return $param; 01031 } else { 01032 return ''; 01033 } 01034 01035 case PARAM_PERMISSION: 01036 $param = (int)$param; 01037 if (in_array($param, array(CAP_INHERIT, CAP_ALLOW, CAP_PREVENT, CAP_PROHIBIT))) { 01038 return $param; 01039 } else { 01040 return CAP_INHERIT; 01041 } 01042 01043 case PARAM_AUTH: 01044 $param = clean_param($param, PARAM_PLUGIN); 01045 if (empty($param)) { 01046 return ''; 01047 } else if (exists_auth_plugin($param)) { 01048 return $param; 01049 } else { 01050 return ''; 01051 } 01052 01053 case PARAM_LANG: 01054 $param = clean_param($param, PARAM_SAFEDIR); 01055 if (get_string_manager()->translation_exists($param)) { 01056 return $param; 01057 } else { 01058 return ''; // Specified language is not installed or param malformed 01059 } 01060 01061 case PARAM_THEME: 01062 $param = clean_param($param, PARAM_PLUGIN); 01063 if (empty($param)) { 01064 return ''; 01065 } else if (file_exists("$CFG->dirroot/theme/$param/config.php")) { 01066 return $param; 01067 } else if (!empty($CFG->themedir) and file_exists("$CFG->themedir/$param/config.php")) { 01068 return $param; 01069 } else { 01070 return ''; // Specified theme is not installed 01071 } 01072 01073 case PARAM_USERNAME: 01074 $param = fix_utf8($param); 01075 $param = str_replace(" " , "", $param); 01076 $param = moodle_strtolower($param); // Convert uppercase to lowercase MDL-16919 01077 if (empty($CFG->extendedusernamechars)) { 01078 // regular expression, eliminate all chars EXCEPT: 01079 // alphanum, dash (-), underscore (_), at sign (@) and period (.) characters. 01080 $param = preg_replace('/[^-\.@_a-z0-9]/', '', $param); 01081 } 01082 return $param; 01083 01084 case PARAM_EMAIL: 01085 $param = fix_utf8($param); 01086 if (validate_email($param)) { 01087 return $param; 01088 } else { 01089 return ''; 01090 } 01091 01092 case PARAM_STRINGID: 01093 if (preg_match('|^[a-zA-Z][a-zA-Z0-9\.:/_-]*$|', $param)) { 01094 return $param; 01095 } else { 01096 return ''; 01097 } 01098 01099 case PARAM_TIMEZONE: //can be int, float(with .5 or .0) or string seperated by '/' and can have '-_' 01100 $param = fix_utf8($param); 01101 $timezonepattern = '/^(([+-]?(0?[0-9](\.[5|0])?|1[0-3]|1[0-2]\.5))|(99)|[[:alnum:]]+(\/?[[:alpha:]_-])+)$/'; 01102 if (preg_match($timezonepattern, $param)) { 01103 return $param; 01104 } else { 01105 return ''; 01106 } 01107 01108 default: // throw error, switched parameters in optional_param or another serious problem 01109 print_error("unknownparamtype", '', '', $type); 01110 } 01111 } 01112 01121 function fix_utf8($value) { 01122 if (is_null($value) or $value === '') { 01123 return $value; 01124 01125 } else if (is_string($value)) { 01126 if ((string)(int)$value === $value) { 01127 // shortcut 01128 return $value; 01129 } 01130 return iconv('UTF-8', 'UTF-8//IGNORE', $value); 01131 01132 } else if (is_array($value)) { 01133 foreach ($value as $k=>$v) { 01134 $value[$k] = fix_utf8($v); 01135 } 01136 return $value; 01137 01138 } else if (is_object($value)) { 01139 $value = clone($value); // do not modify original 01140 foreach ($value as $k=>$v) { 01141 $value->$k = fix_utf8($v); 01142 } 01143 return $value; 01144 01145 } else { 01146 // this is some other type, no utf-8 here 01147 return $value; 01148 } 01149 } 01150 01157 function is_number($value) { 01158 if (is_int($value)) { 01159 return true; 01160 } else if (is_string($value)) { 01161 return ((string)(int)$value) === $value; 01162 } else { 01163 return false; 01164 } 01165 } 01166 01172 function get_host_from_url($url) { 01173 preg_match('|^[a-z]+://([a-zA-Z0-9-.]+)|i', $url, $matches); 01174 if ($matches) { 01175 return $matches[1]; 01176 } 01177 return null; 01178 } 01179 01191 function html_is_blank($string) { 01192 return trim(strip_tags($string, '<img><object><applet><input><select><textarea><hr>')) == ''; 01193 } 01194 01213 function set_config($name, $value, $plugin=NULL) { 01214 global $CFG, $DB; 01215 01216 if (empty($plugin)) { 01217 if (!array_key_exists($name, $CFG->config_php_settings)) { 01218 // So it's defined for this invocation at least 01219 if (is_null($value)) { 01220 unset($CFG->$name); 01221 } else { 01222 $CFG->$name = (string)$value; // settings from db are always strings 01223 } 01224 } 01225 01226 if ($DB->get_field('config', 'name', array('name'=>$name))) { 01227 if ($value === null) { 01228 $DB->delete_records('config', array('name'=>$name)); 01229 } else { 01230 $DB->set_field('config', 'value', $value, array('name'=>$name)); 01231 } 01232 } else { 01233 if ($value !== null) { 01234 $config = new stdClass(); 01235 $config->name = $name; 01236 $config->value = $value; 01237 $DB->insert_record('config', $config, false); 01238 } 01239 } 01240 01241 } else { // plugin scope 01242 if ($id = $DB->get_field('config_plugins', 'id', array('name'=>$name, 'plugin'=>$plugin))) { 01243 if ($value===null) { 01244 $DB->delete_records('config_plugins', array('name'=>$name, 'plugin'=>$plugin)); 01245 } else { 01246 $DB->set_field('config_plugins', 'value', $value, array('id'=>$id)); 01247 } 01248 } else { 01249 if ($value !== null) { 01250 $config = new stdClass(); 01251 $config->plugin = $plugin; 01252 $config->name = $name; 01253 $config->value = $value; 01254 $DB->insert_record('config_plugins', $config, false); 01255 } 01256 } 01257 } 01258 01259 return true; 01260 } 01261 01276 function get_config($plugin, $name = NULL) { 01277 global $CFG, $DB; 01278 01279 // normalise component name 01280 if ($plugin === 'moodle' or $plugin === 'core') { 01281 $plugin = NULL; 01282 } 01283 01284 if (!empty($name)) { // the user is asking for a specific value 01285 if (!empty($plugin)) { 01286 if (isset($CFG->forced_plugin_settings[$plugin]) and array_key_exists($name, $CFG->forced_plugin_settings[$plugin])) { 01287 // setting forced in config file 01288 return $CFG->forced_plugin_settings[$plugin][$name]; 01289 } else { 01290 return $DB->get_field('config_plugins', 'value', array('plugin'=>$plugin, 'name'=>$name)); 01291 } 01292 } else { 01293 if (array_key_exists($name, $CFG->config_php_settings)) { 01294 // setting force in config file 01295 return $CFG->config_php_settings[$name]; 01296 } else { 01297 return $DB->get_field('config', 'value', array('name'=>$name)); 01298 } 01299 } 01300 } 01301 01302 // the user is after a recordset 01303 if ($plugin) { 01304 $localcfg = $DB->get_records_menu('config_plugins', array('plugin'=>$plugin), '', 'name,value'); 01305 if (isset($CFG->forced_plugin_settings[$plugin])) { 01306 foreach($CFG->forced_plugin_settings[$plugin] as $n=>$v) { 01307 if (is_null($v) or is_array($v) or is_object($v)) { 01308 // we do not want any extra mess here, just real settings that could be saved in db 01309 unset($localcfg[$n]); 01310 } else { 01311 //convert to string as if it went through the DB 01312 $localcfg[$n] = (string)$v; 01313 } 01314 } 01315 } 01316 if ($localcfg) { 01317 return (object)$localcfg; 01318 } else { 01319 return new stdClass(); 01320 } 01321 01322 } else { 01323 // this part is not really used any more, but anyway... 01324 $localcfg = $DB->get_records_menu('config', array(), '', 'name,value'); 01325 foreach($CFG->config_php_settings as $n=>$v) { 01326 if (is_null($v) or is_array($v) or is_object($v)) { 01327 // we do not want any extra mess here, just real settings that could be saved in db 01328 unset($localcfg[$n]); 01329 } else { 01330 //convert to string as if it went through the DB 01331 $localcfg[$n] = (string)$v; 01332 } 01333 } 01334 return (object)$localcfg; 01335 } 01336 } 01337 01346 function unset_config($name, $plugin=NULL) { 01347 global $CFG, $DB; 01348 01349 if (empty($plugin)) { 01350 unset($CFG->$name); 01351 $DB->delete_records('config', array('name'=>$name)); 01352 } else { 01353 $DB->delete_records('config_plugins', array('name'=>$name, 'plugin'=>$plugin)); 01354 } 01355 01356 return true; 01357 } 01358 01365 function unset_all_config_for_plugin($plugin) { 01366 global $DB; 01367 $DB->delete_records('config_plugins', array('plugin' => $plugin)); 01368 $like = $DB->sql_like('name', '?', true, true, false, '|'); 01369 $params = array($DB->sql_like_escape($plugin.'_', '|') . '%'); 01370 $DB->delete_records_select('config', $like, $params); 01371 return true; 01372 } 01373 01384 function get_users_from_config($value, $capability, $includeadmins = true) { 01385 global $CFG, $DB; 01386 01387 if (empty($value) or $value === '$@NONE@$') { 01388 return array(); 01389 } 01390 01391 // we have to make sure that users still have the necessary capability, 01392 // it should be faster to fetch them all first and then test if they are present 01393 // instead of validating them one-by-one 01394 $users = get_users_by_capability(get_context_instance(CONTEXT_SYSTEM), $capability); 01395 if ($includeadmins) { 01396 $admins = get_admins(); 01397 foreach ($admins as $admin) { 01398 $users[$admin->id] = $admin; 01399 } 01400 } 01401 01402 if ($value === '$@ALL@$') { 01403 return $users; 01404 } 01405 01406 $result = array(); // result in correct order 01407 $allowed = explode(',', $value); 01408 foreach ($allowed as $uid) { 01409 if (isset($users[$uid])) { 01410 $user = $users[$uid]; 01411 $result[$user->id] = $user; 01412 } 01413 } 01414 01415 return $result; 01416 } 01417 01418 01423 function purge_all_caches() { 01424 global $CFG; 01425 01426 reset_text_filters_cache(); 01427 js_reset_all_caches(); 01428 theme_reset_all_caches(); 01429 get_string_manager()->reset_caches(); 01430 01431 // purge all other caches: rss, simplepie, etc. 01432 remove_dir($CFG->cachedir.'', true); 01433 01434 // make sure cache dir is writable, throws exception if not 01435 make_cache_directory(''); 01436 01437 // hack: this script may get called after the purifier was initialised, 01438 // but we do not want to verify repeatedly this exists in each call 01439 make_cache_directory('htmlpurifier'); 01440 } 01441 01449 function get_cache_flags($type, $changedsince=NULL) { 01450 global $DB; 01451 01452 $params = array('type'=>$type, 'expiry'=>time()); 01453 $sqlwhere = "flagtype = :type AND expiry >= :expiry"; 01454 if ($changedsince !== NULL) { 01455 $params['changedsince'] = $changedsince; 01456 $sqlwhere .= " AND timemodified > :changedsince"; 01457 } 01458 $cf = array(); 01459 01460 if ($flags = $DB->get_records_select('cache_flags', $sqlwhere, $params, '', 'name,value')) { 01461 foreach ($flags as $flag) { 01462 $cf[$flag->name] = $flag->value; 01463 } 01464 } 01465 return $cf; 01466 } 01467 01476 function get_cache_flag($type, $name, $changedsince=NULL) { 01477 global $DB; 01478 01479 $params = array('type'=>$type, 'name'=>$name, 'expiry'=>time()); 01480 01481 $sqlwhere = "flagtype = :type AND name = :name AND expiry >= :expiry"; 01482 if ($changedsince !== NULL) { 01483 $params['changedsince'] = $changedsince; 01484 $sqlwhere .= " AND timemodified > :changedsince"; 01485 } 01486 01487 return $DB->get_field_select('cache_flags', 'value', $sqlwhere, $params); 01488 } 01489 01499 function set_cache_flag($type, $name, $value, $expiry=NULL) { 01500 global $DB; 01501 01502 $timemodified = time(); 01503 if ($expiry===NULL || $expiry < $timemodified) { 01504 $expiry = $timemodified + 24 * 60 * 60; 01505 } else { 01506 $expiry = (int)$expiry; 01507 } 01508 01509 if ($value === NULL) { 01510 unset_cache_flag($type,$name); 01511 return true; 01512 } 01513 01514 if ($f = $DB->get_record('cache_flags', array('name'=>$name, 'flagtype'=>$type), '*', IGNORE_MULTIPLE)) { // this is a potential problem in DEBUG_DEVELOPER 01515 if ($f->value == $value and $f->expiry == $expiry and $f->timemodified == $timemodified) { 01516 return true; //no need to update; helps rcache too 01517 } 01518 $f->value = $value; 01519 $f->expiry = $expiry; 01520 $f->timemodified = $timemodified; 01521 $DB->update_record('cache_flags', $f); 01522 } else { 01523 $f = new stdClass(); 01524 $f->flagtype = $type; 01525 $f->name = $name; 01526 $f->value = $value; 01527 $f->expiry = $expiry; 01528 $f->timemodified = $timemodified; 01529 $DB->insert_record('cache_flags', $f); 01530 } 01531 return true; 01532 } 01533 01542 function unset_cache_flag($type, $name) { 01543 global $DB; 01544 $DB->delete_records('cache_flags', array('name'=>$name, 'flagtype'=>$type)); 01545 return true; 01546 } 01547 01553 function gc_cache_flags() { 01554 global $DB; 01555 $DB->delete_records_select('cache_flags', 'expiry < ?', array(time())); 01556 return true; 01557 } 01558 01560 01571 function check_user_preferences_loaded(stdClass $user, $cachelifetime = 120) { 01572 global $DB; 01573 static $loadedusers = array(); // Static cache, we need to check on each page load, not only every 2 minutes. 01574 01575 if (!isset($user->id)) { 01576 throw new coding_exception('Invalid $user parameter in check_user_preferences_loaded() call, missing id field'); 01577 } 01578 01579 if (empty($user->id) or isguestuser($user->id)) { 01580 // No permanent storage for not-logged-in users and guest 01581 if (!isset($user->preference)) { 01582 $user->preference = array(); 01583 } 01584 return; 01585 } 01586 01587 $timenow = time(); 01588 01589 if (isset($loadedusers[$user->id]) and isset($user->preference) and isset($user->preference['_lastloaded'])) { 01590 // Already loaded at least once on this page. Are we up to date? 01591 if ($user->preference['_lastloaded'] + $cachelifetime > $timenow) { 01592 // no need to reload - we are on the same page and we loaded prefs just a moment ago 01593 return; 01594 01595 } else if (!get_cache_flag('userpreferenceschanged', $user->id, $user->preference['_lastloaded'])) { 01596 // no change since the lastcheck on this page 01597 $user->preference['_lastloaded'] = $timenow; 01598 return; 01599 } 01600 } 01601 01602 // OK, so we have to reload all preferences 01603 $loadedusers[$user->id] = true; 01604 $user->preference = $DB->get_records_menu('user_preferences', array('userid'=>$user->id), '', 'name,value'); // All values 01605 $user->preference['_lastloaded'] = $timenow; 01606 } 01607 01617 function mark_user_preferences_changed($userid) { 01618 global $CFG; 01619 01620 if (empty($userid) or isguestuser($userid)) { 01621 // no cache flags for guest and not-logged-in users 01622 return; 01623 } 01624 01625 set_cache_flag('userpreferenceschanged', $userid, 1, time() + $CFG->sessiontimeout); 01626 } 01627 01639 function set_user_preference($name, $value, $user = null) { 01640 global $USER, $DB; 01641 01642 if (empty($name) or is_numeric($name) or $name === '_lastloaded') { 01643 throw new coding_exception('Invalid preference name in set_user_preference() call'); 01644 } 01645 01646 if (is_null($value)) { 01647 // null means delete current 01648 return unset_user_preference($name, $user); 01649 } else if (is_object($value)) { 01650 throw new coding_exception('Invalid value in set_user_preference() call, objects are not allowed'); 01651 } else if (is_array($value)) { 01652 throw new coding_exception('Invalid value in set_user_preference() call, arrays are not allowed'); 01653 } 01654 $value = (string)$value; 01655 if (textlib::strlen($value) > 1333) { //value column maximum length is 1333 characters 01656 throw new coding_exception('Invalid value in set_user_preference() call, value is is too long for the value column'); 01657 } 01658 01659 if (is_null($user)) { 01660 $user = $USER; 01661 } else if (isset($user->id)) { 01662 // $user is valid object 01663 } else if (is_numeric($user)) { 01664 $user = (object)array('id'=>(int)$user); 01665 } else { 01666 throw new coding_exception('Invalid $user parameter in set_user_preference() call'); 01667 } 01668 01669 check_user_preferences_loaded($user); 01670 01671 if (empty($user->id) or isguestuser($user->id)) { 01672 // no permanent storage for not-logged-in users and guest 01673 $user->preference[$name] = $value; 01674 return true; 01675 } 01676 01677 if ($preference = $DB->get_record('user_preferences', array('userid'=>$user->id, 'name'=>$name))) { 01678 if ($preference->value === $value and isset($user->preference[$name]) and $user->preference[$name] === $value) { 01679 // preference already set to this value 01680 return true; 01681 } 01682 $DB->set_field('user_preferences', 'value', $value, array('id'=>$preference->id)); 01683 01684 } else { 01685 $preference = new stdClass(); 01686 $preference->userid = $user->id; 01687 $preference->name = $name; 01688 $preference->value = $value; 01689 $DB->insert_record('user_preferences', $preference); 01690 } 01691 01692 // update value in cache 01693 $user->preference[$name] = $value; 01694 01695 // set reload flag for other sessions 01696 mark_user_preferences_changed($user->id); 01697 01698 return true; 01699 } 01700 01710 function set_user_preferences(array $prefarray, $user = null) { 01711 foreach ($prefarray as $name => $value) { 01712 set_user_preference($name, $value, $user); 01713 } 01714 return true; 01715 } 01716 01726 function unset_user_preference($name, $user = null) { 01727 global $USER, $DB; 01728 01729 if (empty($name) or is_numeric($name) or $name === '_lastloaded') { 01730 throw new coding_exception('Invalid preference name in unset_user_preference() call'); 01731 } 01732 01733 if (is_null($user)) { 01734 $user = $USER; 01735 } else if (isset($user->id)) { 01736 // $user is valid object 01737 } else if (is_numeric($user)) { 01738 $user = (object)array('id'=>(int)$user); 01739 } else { 01740 throw new coding_exception('Invalid $user parameter in unset_user_preference() call'); 01741 } 01742 01743 check_user_preferences_loaded($user); 01744 01745 if (empty($user->id) or isguestuser($user->id)) { 01746 // no permanent storage for not-logged-in user and guest 01747 unset($user->preference[$name]); 01748 return true; 01749 } 01750 01751 // delete from DB 01752 $DB->delete_records('user_preferences', array('userid'=>$user->id, 'name'=>$name)); 01753 01754 // delete the preference from cache 01755 unset($user->preference[$name]); 01756 01757 // set reload flag for other sessions 01758 mark_user_preferences_changed($user->id); 01759 01760 return true; 01761 } 01762 01781 function get_user_preferences($name = null, $default = null, $user = null) { 01782 global $USER; 01783 01784 if (is_null($name)) { 01785 // all prefs 01786 } else if (is_numeric($name) or $name === '_lastloaded') { 01787 throw new coding_exception('Invalid preference name in get_user_preferences() call'); 01788 } 01789 01790 if (is_null($user)) { 01791 $user = $USER; 01792 } else if (isset($user->id)) { 01793 // $user is valid object 01794 } else if (is_numeric($user)) { 01795 $user = (object)array('id'=>(int)$user); 01796 } else { 01797 throw new coding_exception('Invalid $user parameter in get_user_preferences() call'); 01798 } 01799 01800 check_user_preferences_loaded($user); 01801 01802 if (empty($name)) { 01803 return $user->preference; // All values 01804 } else if (isset($user->preference[$name])) { 01805 return $user->preference[$name]; // The single string value 01806 } else { 01807 return $default; // Default value (null if not specified) 01808 } 01809 } 01810 01812 01828 function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0, $timezone=99, $applydst=true) { 01829 01830 //save input timezone, required for dst offset check. 01831 $passedtimezone = $timezone; 01832 01833 $timezone = get_user_timezone_offset($timezone); 01834 01835 if (abs($timezone) > 13) { //server time 01836 $time = mktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year); 01837 } else { 01838 $time = gmmktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year); 01839 $time = usertime($time, $timezone); 01840 01841 //Apply dst for string timezones or if 99 then try dst offset with user's default timezone 01842 if ($applydst && ((99 == $passedtimezone) || !is_numeric($passedtimezone))) { 01843 $time -= dst_offset_on($time, $passedtimezone); 01844 } 01845 } 01846 01847 return $time; 01848 01849 } 01850 01865 function format_time($totalsecs, $str=NULL) { 01866 01867 $totalsecs = abs($totalsecs); 01868 01869 if (!$str) { // Create the str structure the slow way 01870 $str = new stdClass(); 01871 $str->day = get_string('day'); 01872 $str->days = get_string('days'); 01873 $str->hour = get_string('hour'); 01874 $str->hours = get_string('hours'); 01875 $str->min = get_string('min'); 01876 $str->mins = get_string('mins'); 01877 $str->sec = get_string('sec'); 01878 $str->secs = get_string('secs'); 01879 $str->year = get_string('year'); 01880 $str->years = get_string('years'); 01881 } 01882 01883 01884 $years = floor($totalsecs/YEARSECS); 01885 $remainder = $totalsecs - ($years*YEARSECS); 01886 $days = floor($remainder/DAYSECS); 01887 $remainder = $totalsecs - ($days*DAYSECS); 01888 $hours = floor($remainder/HOURSECS); 01889 $remainder = $remainder - ($hours*HOURSECS); 01890 $mins = floor($remainder/MINSECS); 01891 $secs = $remainder - ($mins*MINSECS); 01892 01893 $ss = ($secs == 1) ? $str->sec : $str->secs; 01894 $sm = ($mins == 1) ? $str->min : $str->mins; 01895 $sh = ($hours == 1) ? $str->hour : $str->hours; 01896 $sd = ($days == 1) ? $str->day : $str->days; 01897 $sy = ($years == 1) ? $str->year : $str->years; 01898 01899 $oyears = ''; 01900 $odays = ''; 01901 $ohours = ''; 01902 $omins = ''; 01903 $osecs = ''; 01904 01905 if ($years) $oyears = $years .' '. $sy; 01906 if ($days) $odays = $days .' '. $sd; 01907 if ($hours) $ohours = $hours .' '. $sh; 01908 if ($mins) $omins = $mins .' '. $sm; 01909 if ($secs) $osecs = $secs .' '. $ss; 01910 01911 if ($years) return trim($oyears .' '. $odays); 01912 if ($days) return trim($odays .' '. $ohours); 01913 if ($hours) return trim($ohours .' '. $omins); 01914 if ($mins) return trim($omins .' '. $osecs); 01915 if ($secs) return $osecs; 01916 return get_string('now'); 01917 } 01918 01941 function userdate($date, $format = '', $timezone = 99, $fixday = true) { 01942 01943 global $CFG; 01944 01945 if (empty($format)) { 01946 $format = get_string('strftimedaydatetime', 'langconfig'); 01947 } 01948 01949 if (!empty($CFG->nofixday)) { // Config.php can force %d not to be fixed. 01950 $fixday = false; 01951 } else if ($fixday) { 01952 $formatnoday = str_replace('%d', 'DD', $format); 01953 $fixday = ($formatnoday != $format); 01954 } 01955 01956 //add daylight saving offset for string timezones only, as we can't get dst for 01957 //float values. if timezone is 99 (user default timezone), then try update dst. 01958 if ((99 == $timezone) || !is_numeric($timezone)) { 01959 $date += dst_offset_on($date, $timezone); 01960 } 01961 01962 $timezone = get_user_timezone_offset($timezone); 01963 01964 if (abs($timezone) > 13) { 01965 if ($fixday) { 01966 $datestring = strftime($formatnoday, $date); 01967 $daystring = ltrim(str_replace(array(' 0', ' '), '', strftime(' %d', $date))); 01968 $datestring = str_replace('DD', $daystring, $datestring); 01969 } else { 01970 $datestring = strftime($format, $date); 01971 } 01972 } else { 01973 $date += (int)($timezone * 3600); 01974 if ($fixday) { 01975 $datestring = gmstrftime($formatnoday, $date); 01976 $daystring = ltrim(str_replace(array(' 0', ' '), '', gmstrftime(' %d', $date))); 01977 $datestring = str_replace('DD', $daystring, $datestring); 01978 } else { 01979 $datestring = gmstrftime($format, $date); 01980 } 01981 } 01982 01985 01986 if ($CFG->ostype == 'WINDOWS') { 01987 if ($localewincharset = get_string('localewincharset', 'langconfig')) { 01988 $textlib = textlib_get_instance(); 01989 $datestring = $textlib->convert($datestring, $localewincharset, 'utf-8'); 01990 } 01991 } 01992 01993 return $datestring; 01994 } 01995 02007 function usergetdate($time, $timezone=99) { 02008 02009 //save input timezone, required for dst offset check. 02010 $passedtimezone = $timezone; 02011 02012 $timezone = get_user_timezone_offset($timezone); 02013 02014 if (abs($timezone) > 13) { // Server time 02015 return getdate($time); 02016 } 02017 02018 //add daylight saving offset for string timezones only, as we can't get dst for 02019 //float values. if timezone is 99 (user default timezone), then try update dst. 02020 if ($passedtimezone == 99 || !is_numeric($passedtimezone)) { 02021 $time += dst_offset_on($time, $passedtimezone); 02022 } 02023 02024 $time += intval((float)$timezone * HOURSECS); 02025 02026 $datestring = gmstrftime('%B_%A_%j_%Y_%m_%w_%d_%H_%M_%S', $time); 02027 02028 //be careful to ensure the returned array matches that produced by getdate() above 02029 list( 02030 $getdate['month'], 02031 $getdate['weekday'], 02032 $getdate['yday'], 02033 $getdate['year'], 02034 $getdate['mon'], 02035 $getdate['wday'], 02036 $getdate['mday'], 02037 $getdate['hours'], 02038 $getdate['minutes'], 02039 $getdate['seconds'] 02040 ) = explode('_', $datestring); 02041 02042 return $getdate; 02043 } 02044 02054 function usertime($date, $timezone=99) { 02055 02056 $timezone = get_user_timezone_offset($timezone); 02057 02058 if (abs($timezone) > 13) { 02059 return $date; 02060 } 02061 return $date - (int)($timezone * HOURSECS); 02062 } 02063 02072 function usergetmidnight($date, $timezone=99) { 02073 02074 $userdate = usergetdate($date, $timezone); 02075 02076 // Time of midnight of this user's day, in GMT 02077 return make_timestamp($userdate['year'], $userdate['mon'], $userdate['mday'], 0, 0, 0, $timezone); 02078 02079 } 02080 02087 function usertimezone($timezone=99) { 02088 02089 $tz = get_user_timezone($timezone); 02090 02091 if (!is_float($tz)) { 02092 return $tz; 02093 } 02094 02095 if(abs($tz) > 13) { // Server time 02096 return get_string('serverlocaltime'); 02097 } 02098 02099 if($tz == intval($tz)) { 02100 // Don't show .0 for whole hours 02101 $tz = intval($tz); 02102 } 02103 02104 if($tz == 0) { 02105 return 'UTC'; 02106 } 02107 else if($tz > 0) { 02108 return 'UTC+'.$tz; 02109 } 02110 else { 02111 return 'UTC'.$tz; 02112 } 02113 02114 } 02115 02125 function get_user_timezone_offset($tz = 99) { 02126 02127 global $USER, $CFG; 02128 02129 $tz = get_user_timezone($tz); 02130 02131 if (is_float($tz)) { 02132 return $tz; 02133 } else { 02134 $tzrecord = get_timezone_record($tz); 02135 if (empty($tzrecord)) { 02136 return 99.0; 02137 } 02138 return (float)$tzrecord->gmtoff / HOURMINS; 02139 } 02140 } 02141 02149 function get_timezone_offset($tz) { 02150 global $CFG; 02151 02152 if ($tz == 99) { 02153 return false; 02154 } 02155 02156 if (is_numeric($tz)) { 02157 return intval($tz * 60*60); 02158 } 02159 02160 if (!$tzrecord = get_timezone_record($tz)) { 02161 return false; 02162 } 02163 return intval($tzrecord->gmtoff * 60); 02164 } 02165 02177 function get_user_timezone($tz = 99) { 02178 global $USER, $CFG; 02179 02180 $timezones = array( 02181 $tz, 02182 isset($CFG->forcetimezone) ? $CFG->forcetimezone : 99, 02183 isset($USER->timezone) ? $USER->timezone : 99, 02184 isset($CFG->timezone) ? $CFG->timezone : 99, 02185 ); 02186 02187 $tz = 99; 02188 02189 while(($tz == '' || $tz == 99 || $tz == NULL) && $next = each($timezones)) { 02190 $tz = $next['value']; 02191 } 02192 02193 return is_numeric($tz) ? (float) $tz : $tz; 02194 } 02195 02204 function get_timezone_record($timezonename) { 02205 global $CFG, $DB; 02206 static $cache = NULL; 02207 02208 if ($cache === NULL) { 02209 $cache = array(); 02210 } 02211 02212 if (isset($cache[$timezonename])) { 02213 return $cache[$timezonename]; 02214 } 02215 02216 return $cache[$timezonename] = $DB->get_record_sql('SELECT * FROM {timezone} 02217 WHERE name = ? ORDER BY year DESC', array($timezonename), IGNORE_MULTIPLE); 02218 } 02219 02231 function calculate_user_dst_table($from_year = NULL, $to_year = NULL, $strtimezone = NULL) { 02232 global $CFG, $SESSION, $DB; 02233 02234 $usertz = get_user_timezone($strtimezone); 02235 02236 if (is_float($usertz)) { 02237 // Trivial timezone, no DST 02238 return false; 02239 } 02240 02241 if (!empty($SESSION->dst_offsettz) && $SESSION->dst_offsettz != $usertz) { 02242 // We have precalculated values, but the user's effective TZ has changed in the meantime, so reset 02243 unset($SESSION->dst_offsets); 02244 unset($SESSION->dst_range); 02245 } 02246 02247 if (!empty($SESSION->dst_offsets) && empty($from_year) && empty($to_year)) { 02248 // Repeat calls which do not request specific year ranges stop here, we have already calculated the table 02249 // This will be the return path most of the time, pretty light computationally 02250 return true; 02251 } 02252 02253 // Reaching here means we either need to extend our table or create it from scratch 02254 02255 // Remember which TZ we calculated these changes for 02256 $SESSION->dst_offsettz = $usertz; 02257 02258 if(empty($SESSION->dst_offsets)) { 02259 // If we 're creating from scratch, put the two guard elements in there 02260 $SESSION->dst_offsets = array(1 => NULL, 0 => NULL); 02261 } 02262 if(empty($SESSION->dst_range)) { 02263 // If creating from scratch 02264 $from = max((empty($from_year) ? intval(date('Y')) - 3 : $from_year), 1971); 02265 $to = min((empty($to_year) ? intval(date('Y')) + 3 : $to_year), 2035); 02266 02267 // Fill in the array with the extra years we need to process 02268 $yearstoprocess = array(); 02269 for($i = $from; $i <= $to; ++$i) { 02270 $yearstoprocess[] = $i; 02271 } 02272 02273 // Take note of which years we have processed for future calls 02274 $SESSION->dst_range = array($from, $to); 02275 } 02276 else { 02277 // If needing to extend the table, do the same 02278 $yearstoprocess = array(); 02279 02280 $from = max((empty($from_year) ? $SESSION->dst_range[0] : $from_year), 1971); 02281 $to = min((empty($to_year) ? $SESSION->dst_range[1] : $to_year), 2035); 02282 02283 if($from < $SESSION->dst_range[0]) { 02284 // Take note of which years we need to process and then note that we have processed them for future calls 02285 for($i = $from; $i < $SESSION->dst_range[0]; ++$i) { 02286 $yearstoprocess[] = $i; 02287 } 02288 $SESSION->dst_range[0] = $from; 02289 } 02290 if($to > $SESSION->dst_range[1]) { 02291 // Take note of which years we need to process and then note that we have processed them for future calls 02292 for($i = $SESSION->dst_range[1] + 1; $i <= $to; ++$i) { 02293 $yearstoprocess[] = $i; 02294 } 02295 $SESSION->dst_range[1] = $to; 02296 } 02297 } 02298 02299 if(empty($yearstoprocess)) { 02300 // This means that there was a call requesting a SMALLER range than we have already calculated 02301 return true; 02302 } 02303 02304 // From now on, we know that the array has at least the two guard elements, and $yearstoprocess has the years we need 02305 // Also, the array is sorted in descending timestamp order! 02306 02307 // Get DB data 02308 02309 static $presets_cache = array(); 02310 if (!isset($presets_cache[$usertz])) { 02311 $presets_cache[$usertz] = $DB->get_records('timezone', array('name'=>$usertz), 'year DESC', 'year, gmtoff, dstoff, dst_month, dst_startday, dst_weekday, dst_skipweeks, dst_time, std_month, std_startday, std_weekday, std_skipweeks, std_time'); 02312 } 02313 if(empty($presets_cache[$usertz])) { 02314 return false; 02315 } 02316 02317 // Remove ending guard (first element of the array) 02318 reset($SESSION->dst_offsets); 02319 unset($SESSION->dst_offsets[key($SESSION->dst_offsets)]); 02320 02321 // Add all required change timestamps 02322 foreach($yearstoprocess as $y) { 02323 // Find the record which is in effect for the year $y 02324 foreach($presets_cache[$usertz] as $year => $preset) { 02325 if($year <= $y) { 02326 break; 02327 } 02328 } 02329 02330 $changes = dst_changes_for_year($y, $preset); 02331 02332 if($changes === NULL) { 02333 continue; 02334 } 02335 if($changes['dst'] != 0) { 02336 $SESSION->dst_offsets[$changes['dst']] = $preset->dstoff * MINSECS; 02337 } 02338 if($changes['std'] != 0) { 02339 $SESSION->dst_offsets[$changes['std']] = 0; 02340 } 02341 } 02342 02343 // Put in a guard element at the top 02344 $maxtimestamp = max(array_keys($SESSION->dst_offsets)); 02345 $SESSION->dst_offsets[($maxtimestamp + DAYSECS)] = NULL; // DAYSECS is arbitrary, any "small" number will do 02346 02347 // Sort again 02348 krsort($SESSION->dst_offsets); 02349 02350 return true; 02351 } 02352 02362 function dst_changes_for_year($year, $timezone) { 02363 02364 if($timezone->dst_startday == 0 && $timezone->dst_weekday == 0 && $timezone->std_startday == 0 && $timezone->std_weekday == 0) { 02365 return NULL; 02366 } 02367 02368 $monthdaydst = find_day_in_month($timezone->dst_startday, $timezone->dst_weekday, $timezone->dst_month, $year); 02369 $monthdaystd = find_day_in_month($timezone->std_startday, $timezone->std_weekday, $timezone->std_month, $year); 02370 02371 list($dst_hour, $dst_min) = explode(':', $timezone->dst_time); 02372 list($std_hour, $std_min) = explode(':', $timezone->std_time); 02373 02374 $timedst = make_timestamp($year, $timezone->dst_month, $monthdaydst, 0, 0, 0, 99, false); 02375 $timestd = make_timestamp($year, $timezone->std_month, $monthdaystd, 0, 0, 0, 99, false); 02376 02377 // Instead of putting hour and minute in make_timestamp(), we add them afterwards. 02378 // This has the advantage of being able to have negative values for hour, i.e. for timezones 02379 // where GMT time would be in the PREVIOUS day than the local one on which DST changes. 02380 02381 $timedst += $dst_hour * HOURSECS + $dst_min * MINSECS; 02382 $timestd += $std_hour * HOURSECS + $std_min * MINSECS; 02383 02384 return array('dst' => $timedst, 0 => $timedst, 'std' => $timestd, 1 => $timestd); 02385 } 02386 02397 function dst_offset_on($time, $strtimezone = NULL) { 02398 global $SESSION; 02399 02400 if(!calculate_user_dst_table(NULL, NULL, $strtimezone) || empty($SESSION->dst_offsets)) { 02401 return 0; 02402 } 02403 02404 reset($SESSION->dst_offsets); 02405 while(list($from, $offset) = each($SESSION->dst_offsets)) { 02406 if($from <= $time) { 02407 break; 02408 } 02409 } 02410 02411 // This is the normal return path 02412 if($offset !== NULL) { 02413 return $offset; 02414 } 02415 02416 // Reaching this point means we haven't calculated far enough, do it now: 02417 // Calculate extra DST changes if needed and recurse. The recursion always 02418 // moves toward the stopping condition, so will always end. 02419 02420 if($from == 0) { 02421 // We need a year smaller than $SESSION->dst_range[0] 02422 if($SESSION->dst_range[0] == 1971) { 02423 return 0; 02424 } 02425 calculate_user_dst_table($SESSION->dst_range[0] - 5, NULL, $strtimezone); 02426 return dst_offset_on($time, $strtimezone); 02427 } 02428 else { 02429 // We need a year larger than $SESSION->dst_range[1] 02430 if($SESSION->dst_range[1] == 2035) { 02431 return 0; 02432 } 02433 calculate_user_dst_table(NULL, $SESSION->dst_range[1] + 5, $strtimezone); 02434 return dst_offset_on($time, $strtimezone); 02435 } 02436 } 02437 02448 function find_day_in_month($startday, $weekday, $month, $year) { 02449 02450 $daysinmonth = days_in_month($month, $year); 02451 02452 if($weekday == -1) { 02453 // Don't care about weekday, so return: 02454 // abs($startday) if $startday != -1 02455 // $daysinmonth otherwise 02456 return ($startday == -1) ? $daysinmonth : abs($startday); 02457 } 02458 02459 // From now on we 're looking for a specific weekday 02460 02461 // Give "end of month" its actual value, since we know it 02462 if($startday == -1) { 02463 $startday = -1 * $daysinmonth; 02464 } 02465 02466 // Starting from day $startday, the sign is the direction 02467 02468 if($startday < 1) { 02469 02470 $startday = abs($startday); 02471 $lastmonthweekday = strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year)); 02472 02473 // This is the last such weekday of the month 02474 $lastinmonth = $daysinmonth + $weekday - $lastmonthweekday; 02475 if($lastinmonth > $daysinmonth) { 02476 $lastinmonth -= 7; 02477 } 02478 02479 // Find the first such weekday <= $startday 02480 while($lastinmonth > $startday) { 02481 $lastinmonth -= 7; 02482 } 02483 02484 return $lastinmonth; 02485 02486 } 02487 else { 02488 02489 $indexweekday = strftime('%w', mktime(12, 0, 0, $month, $startday, $year)); 02490 02491 $diff = $weekday - $indexweekday; 02492 if($diff < 0) { 02493 $diff += 7; 02494 } 02495 02496 // This is the first such weekday of the month equal to or after $startday 02497 $firstfromindex = $startday + $diff; 02498 02499 return $firstfromindex; 02500 02501 } 02502 } 02503 02511 function days_in_month($month, $year) { 02512 return intval(date('t', mktime(12, 0, 0, $month, 1, $year))); 02513 } 02514 02523 function dayofweek($day, $month, $year) { 02524 // I wonder if this is any different from 02525 // strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year, 0)); 02526 return intval(date('w', mktime(12, 0, 0, $month, $day, $year))); 02527 } 02528 02530 02536 function get_login_url() { 02537 global $CFG; 02538 02539 $url = "$CFG->wwwroot/login/index.php"; 02540 02541 if (!empty($CFG->loginhttps)) { 02542 $url = str_replace('http:', 'https:', $url); 02543 } 02544 02545 return $url; 02546 } 02547 02575 function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $setwantsurltome = true, $preventredirect = false) { 02576 global $CFG, $SESSION, $USER, $FULLME, $PAGE, $SITE, $DB, $OUTPUT; 02577 02578 // setup global $COURSE, themes, language and locale 02579 if (!empty($courseorid)) { 02580 if (is_object($courseorid)) { 02581 $course = $courseorid; 02582 } else if ($courseorid == SITEID) { 02583 $course = clone($SITE); 02584 } else { 02585 $course = $DB->get_record('course', array('id' => $courseorid), '*', MUST_EXIST); 02586 } 02587 if ($cm) { 02588 if ($cm->course != $course->id) { 02589 throw new coding_exception('course and cm parameters in require_login() call do not match!!'); 02590 } 02591 // make sure we have a $cm from get_fast_modinfo as this contains activity access details 02592 if (!($cm instanceof cm_info)) { 02593 // note: nearly all pages call get_fast_modinfo anyway and it does not make any 02594 // db queries so this is not really a performance concern, however it is obviously 02595 // better if you use get_fast_modinfo to get the cm before calling this. 02596 $modinfo = get_fast_modinfo($course); 02597 $cm = $modinfo->get_cm($cm->id); 02598 } 02599 $PAGE->set_cm($cm, $course); // set's up global $COURSE 02600 $PAGE->set_pagelayout('incourse'); 02601 } else { 02602 $PAGE->set_course($course); // set's up global $COURSE 02603 } 02604 } else { 02605 // do not touch global $COURSE via $PAGE->set_course(), 02606 // the reasons is we need to be able to call require_login() at any time!! 02607 $course = $SITE; 02608 if ($cm) { 02609 throw new coding_exception('cm parameter in require_login() requires valid course parameter!'); 02610 } 02611 } 02612 02613 // If the user is not even logged in yet then make sure they are 02614 if (!isloggedin()) { 02615 if ($autologinguest and !empty($CFG->guestloginbutton) and !empty($CFG->autologinguests)) { 02616 if (!$guest = get_complete_user_data('id', $CFG->siteguest)) { 02617 // misconfigured site guest, just redirect to login page 02618 redirect(get_login_url()); 02619 exit; // never reached 02620 } 02621 $lang = isset($SESSION->lang) ? $SESSION->lang : $CFG->lang; 02622 complete_user_login($guest); 02623 $USER->autologinguest = true; 02624 $SESSION->lang = $lang; 02625 } else { 02626 //NOTE: $USER->site check was obsoleted by session test cookie, 02627 // $USER->confirmed test is in login/index.php 02628 if ($preventredirect) { 02629 throw new require_login_exception('You are not logged in'); 02630 } 02631 02632 if ($setwantsurltome) { 02633 // TODO: switch to PAGE->url 02634 $SESSION->wantsurl = $FULLME; 02635 } 02636 if (!empty($_SERVER['HTTP_REFERER'])) { 02637 $SESSION->fromurl = $_SERVER['HTTP_REFERER']; 02638 } 02639 redirect(get_login_url()); 02640 exit; // never reached 02641 } 02642 } 02643 02644 // loginas as redirection if needed 02645 if ($course->id != SITEID and session_is_loggedinas()) { 02646 if ($USER->loginascontext->contextlevel == CONTEXT_COURSE) { 02647 if ($USER->loginascontext->instanceid != $course->id) { 02648 print_error('loginasonecourse', '', $CFG->wwwroot.'/course/view.php?id='.$USER->loginascontext->instanceid); 02649 } 02650 } 02651 } 02652 02653 // check whether the user should be changing password (but only if it is REALLY them) 02654 if (get_user_preferences('auth_forcepasswordchange') && !session_is_loggedinas()) { 02655 $userauth = get_auth_plugin($USER->auth); 02656 if ($userauth->can_change_password() and !$preventredirect) { 02657 $SESSION->wantsurl = $FULLME; 02658 if ($changeurl = $userauth->change_password_url()) { 02659 //use plugin custom url 02660 redirect($changeurl); 02661 } else { 02662 //use moodle internal method 02663 if (empty($CFG->loginhttps)) { 02664 redirect($CFG->wwwroot .'/login/change_password.php'); 02665 } else { 02666 $wwwroot = str_replace('http:','https:', $CFG->wwwroot); 02667 redirect($wwwroot .'/login/change_password.php'); 02668 } 02669 } 02670 } else { 02671 print_error('nopasswordchangeforced', 'auth'); 02672 } 02673 } 02674 02675 // Check that the user account is properly set up 02676 if (user_not_fully_set_up($USER)) { 02677 if ($preventredirect) { 02678 throw new require_login_exception('User not fully set-up'); 02679 } 02680 $SESSION->wantsurl = $FULLME; 02681 redirect($CFG->wwwroot .'/user/edit.php?id='. $USER->id .'&course='. SITEID); 02682 } 02683 02684 // Make sure the USER has a sesskey set up. Used for CSRF protection. 02685 sesskey(); 02686 02687 // Do not bother admins with any formalities 02688 if (is_siteadmin()) { 02689 //set accesstime or the user will appear offline which messes up messaging 02690 user_accesstime_log($course->id); 02691 return; 02692 } 02693 02694 // Check that the user has agreed to a site policy if there is one - do not test in case of admins 02695 if (!$USER->policyagreed and !is_siteadmin()) { 02696 if (!empty($CFG->sitepolicy) and !isguestuser()) { 02697 if ($preventredirect) { 02698 throw new require_login_exception('Policy not agreed'); 02699 } 02700 $SESSION->wantsurl = $FULLME; 02701 redirect($CFG->wwwroot .'/user/policy.php'); 02702 } else if (!empty($CFG->sitepolicyguest) and isguestuser()) { 02703 if ($preventredirect) { 02704 throw new require_login_exception('Policy not agreed'); 02705 } 02706 $SESSION->wantsurl = $FULLME; 02707 redirect($CFG->wwwroot .'/user/policy.php'); 02708 } 02709 } 02710 02711 // Fetch the system context, the course context, and prefetch its child contexts 02712 $sysctx = get_context_instance(CONTEXT_SYSTEM); 02713 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST); 02714 if ($cm) { 02715 $cmcontext = get_context_instance(CONTEXT_MODULE, $cm->id, MUST_EXIST); 02716 } else { 02717 $cmcontext = null; 02718 } 02719 02720 // If the site is currently under maintenance, then print a message 02721 if (!empty($CFG->maintenance_enabled) and !has_capability('moodle/site:config', $sysctx)) { 02722 if ($preventredirect) { 02723 throw new require_login_exception('Maintenance in progress'); 02724 } 02725 02726 print_maintenance_message(); 02727 } 02728 02729 // make sure the course itself is not hidden 02730 if ($course->id == SITEID) { 02731 // frontpage can not be hidden 02732 } else { 02733 if (is_role_switched($course->id)) { 02734 // when switching roles ignore the hidden flag - user had to be in course to do the switch 02735 } else { 02736 if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext)) { 02737 // originally there was also test of parent category visibility, 02738 // BUT is was very slow in complex queries involving "my courses" 02739 // now it is also possible to simply hide all courses user is not enrolled in :-) 02740 if ($preventredirect) { 02741 throw new require_login_exception('Course is hidden'); 02742 } 02743 // We need to override the navigation URL as the course won't have 02744 // been added to the navigation and thus the navigation will mess up 02745 // when trying to find it. 02746 navigation_node::override_active_url(new moodle_url('/')); 02747 notice(get_string('coursehidden'), $CFG->wwwroot .'/'); 02748 } 02749 } 02750 } 02751 02752 // is the user enrolled? 02753 if ($course->id == SITEID) { 02754 // everybody is enrolled on the frontpage 02755 02756 } else { 02757 if (session_is_loggedinas()) { 02758 // Make sure the REAL person can access this course first 02759 $realuser = session_get_realuser(); 02760 if (!is_enrolled($coursecontext, $realuser->id, '', true) and !is_viewing($coursecontext, $realuser->id) and !is_siteadmin($realuser->id)) { 02761 if ($preventredirect) { 02762 throw new require_login_exception('Invalid course login-as access'); 02763 } 02764 echo $OUTPUT->header(); 02765 notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/'); 02766 } 02767 } 02768 02769 $access = false; 02770 02771 if (is_role_switched($course->id)) { 02772 // ok, user had to be inside this course before the switch 02773 $access = true; 02774 02775 } else if (is_viewing($coursecontext, $USER)) { 02776 // ok, no need to mess with enrol 02777 $access = true; 02778 02779 } else { 02780 if (isset($USER->enrol['enrolled'][$course->id])) { 02781 if ($USER->enrol['enrolled'][$course->id] > time()) { 02782 $access = true; 02783 if (isset($USER->enrol['tempguest'][$course->id])) { 02784 unset($USER->enrol['tempguest'][$course->id]); 02785 remove_temp_course_roles($coursecontext); 02786 } 02787 } else { 02788 //expired 02789 unset($USER->enrol['enrolled'][$course->id]); 02790 } 02791 } 02792 if (isset($USER->enrol['tempguest'][$course->id])) { 02793 if ($USER->enrol['tempguest'][$course->id] == 0) { 02794 $access = true; 02795 } else if ($USER->enrol['tempguest'][$course->id] > time()) { 02796 $access = true; 02797 } else { 02798 //expired 02799 unset($USER->enrol['tempguest'][$course->id]); 02800 remove_temp_course_roles($coursecontext); 02801 } 02802 } 02803 02804 if ($access) { 02805 // cache ok 02806 } else { 02807 $until = enrol_get_enrolment_end($coursecontext->instanceid, $USER->id); 02808 if ($until !== false) { 02809 // active participants may always access, a timestamp in the future, 0 (always) or false. 02810 if ($until == 0) { 02811 $until = ENROL_MAX_TIMESTAMP; 02812 } 02813 $USER->enrol['enrolled'][$course->id] = $until; 02814 $access = true; 02815 02816 } else { 02817 $instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder, id ASC'); 02818 $enrols = enrol_get_plugins(true); 02819 // first ask all enabled enrol instances in course if they want to auto enrol user 02820 foreach($instances as $instance) { 02821 if (!isset($enrols[$instance->enrol])) { 02822 continue; 02823 } 02824 // Get a duration for the enrolment, a timestamp in the future, 0 (always) or false. 02825 $until = $enrols[$instance->enrol]->try_autoenrol($instance); 02826 if ($until !== false) { 02827 if ($until == 0) { 02828 $until = ENROL_MAX_TIMESTAMP; 02829 } 02830 $USER->enrol['enrolled'][$course->id] = $until; 02831 $access = true; 02832 break; 02833 } 02834 } 02835 // if not enrolled yet try to gain temporary guest access 02836 if (!$access) { 02837 foreach($instances as $instance) { 02838 if (!isset($enrols[$instance->enrol])) { 02839 continue; 02840 } 02841 // Get a duration for the guest access, a timestamp in the future or false. 02842 $until = $enrols[$instance->enrol]->try_guestaccess($instance); 02843 if ($until !== false and $until > time()) { 02844 $USER->enrol['tempguest'][$course->id] = $until; 02845 $access = true; 02846 break; 02847 } 02848 } 02849 } 02850 } 02851 } 02852 } 02853 02854 if (!$access) { 02855 if ($preventredirect) { 02856 throw new require_login_exception('Not enrolled'); 02857 } 02858 $SESSION->wantsurl = $FULLME; 02859 redirect($CFG->wwwroot .'/enrol/index.php?id='. $course->id); 02860 } 02861 } 02862 02863 // Check visibility of activity to current user; includes visible flag, groupmembersonly, 02864 // conditional availability, etc 02865 if ($cm && !$cm->uservisible) { 02866 if ($preventredirect) { 02867 throw new require_login_exception('Activity is hidden'); 02868 } 02869 redirect($CFG->wwwroot, get_string('activityiscurrentlyhidden')); 02870 } 02871 02872 // Finally access granted, update lastaccess times 02873 user_accesstime_log($course->id); 02874 } 02875 02876 02882 function require_logout() { 02883 global $USER; 02884 02885 $params = $USER; 02886 02887 if (isloggedin()) { 02888 add_to_log(SITEID, "user", "logout", "view.php?id=$USER->id&course=".SITEID, $USER->id, 0, $USER->id); 02889 02890 $authsequence = get_enabled_auth_plugins(); // auths, in sequence 02891 foreach($authsequence as $authname) { 02892 $authplugin = get_auth_plugin($authname); 02893 $authplugin->prelogout_hook(); 02894 } 02895 } 02896 02897 events_trigger('user_logout', $params); 02898 session_get_instance()->terminate_current(); 02899 unset($params); 02900 } 02901 02920 function require_course_login($courseorid, $autologinguest = true, $cm = NULL, $setwantsurltome = true, $preventredirect = false) { 02921 global $CFG, $PAGE, $SITE; 02922 $issite = (is_object($courseorid) and $courseorid->id == SITEID) 02923 or (!is_object($courseorid) and $courseorid == SITEID); 02924 if ($issite && !empty($cm) && !($cm instanceof cm_info)) { 02925 // note: nearly all pages call get_fast_modinfo anyway and it does not make any 02926 // db queries so this is not really a performance concern, however it is obviously 02927 // better if you use get_fast_modinfo to get the cm before calling this. 02928 if (is_object($courseorid)) { 02929 $course = $courseorid; 02930 } else { 02931 $course = clone($SITE); 02932 } 02933 $modinfo = get_fast_modinfo($course); 02934 $cm = $modinfo->get_cm($cm->id); 02935 } 02936 if (!empty($CFG->forcelogin)) { 02937 // login required for both SITE and courses 02938 require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect); 02939 02940 } else if ($issite && !empty($cm) and !$cm->uservisible) { 02941 // always login for hidden activities 02942 require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect); 02943 02944 } else if ($issite) { 02945 //login for SITE not required 02946 if ($cm and empty($cm->visible)) { 02947 // hidden activities are not accessible without login 02948 require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect); 02949 } else if ($cm and !empty($CFG->enablegroupmembersonly) and $cm->groupmembersonly) { 02950 // not-logged-in users do not have any group membership 02951 require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect); 02952 } else { 02953 // We still need to instatiate PAGE vars properly so that things 02954 // that rely on it like navigation function correctly. 02955 if (!empty($courseorid)) { 02956 if (is_object($courseorid)) { 02957 $course = $courseorid; 02958 } else { 02959 $course = clone($SITE); 02960 } 02961 if ($cm) { 02962 if ($cm->course != $course->id) { 02963 throw new coding_exception('course and cm parameters in require_course_login() call do not match!!'); 02964 } 02965 $PAGE->set_cm($cm, $course); 02966 $PAGE->set_pagelayout('incourse'); 02967 } else { 02968 $PAGE->set_course($course); 02969 } 02970 } else { 02971 // If $PAGE->course, and hence $PAGE->context, have not already been set 02972 // up properly, set them up now. 02973 $PAGE->set_course($PAGE->course); 02974 } 02975 //TODO: verify conditional activities here 02976 user_accesstime_log(SITEID); 02977 return; 02978 } 02979 02980 } else { 02981 // course login always required 02982 require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect); 02983 } 02984 } 02985 02999 function require_user_key_login($script, $instance=null) { 03000 global $USER, $SESSION, $CFG, $DB; 03001 03002 if (!NO_MOODLE_COOKIES) { 03003 print_error('sessioncookiesdisable'); 03004 } 03005 03007 @session_write_close(); 03008 03009 $keyvalue = required_param('key', PARAM_ALPHANUM); 03010 03011 if (!$key = $DB->get_record('user_private_key', array('script'=>$script, 'value'=>$keyvalue, 'instance'=>$instance))) { 03012 print_error('invalidkey'); 03013 } 03014 03015 if (!empty($key->validuntil) and $key->validuntil < time()) { 03016 print_error('expiredkey'); 03017 } 03018 03019 if ($key->iprestriction) { 03020 $remoteaddr = getremoteaddr(null); 03021 if (empty($remoteaddr) or !address_in_subnet($remoteaddr, $key->iprestriction)) { 03022 print_error('ipmismatch'); 03023 } 03024 } 03025 03026 if (!$user = $DB->get_record('user', array('id'=>$key->userid))) { 03027 print_error('invaliduserid'); 03028 } 03029 03031 enrol_check_plugins($user); 03032 session_set_user($user); 03033 03035 if (!defined('USER_KEY_LOGIN')) { 03036 define('USER_KEY_LOGIN', true); 03037 } 03038 03040 return $key->instance; 03041 } 03042 03054 function create_user_key($script, $userid, $instance=null, $iprestriction=null, $validuntil=null) { 03055 global $DB; 03056 03057 $key = new stdClass(); 03058 $key->script = $script; 03059 $key->userid = $userid; 03060 $key->instance = $instance; 03061 $key->iprestriction = $iprestriction; 03062 $key->validuntil = $validuntil; 03063 $key->timecreated = time(); 03064 03065 $key->value = md5($userid.'_'.time().random_string(40)); // something long and unique 03066 while ($DB->record_exists('user_private_key', array('value'=>$key->value))) { 03067 // must be unique 03068 $key->value = md5($userid.'_'.time().random_string(40)); 03069 } 03070 $DB->insert_record('user_private_key', $key); 03071 return $key->value; 03072 } 03073 03082 function delete_user_key($script,$userid) { 03083 global $DB; 03084 $DB->delete_records('user_private_key', array('script'=>$script, 'userid'=>$userid)); 03085 } 03086 03098 function get_user_key($script, $userid, $instance=null, $iprestriction=null, $validuntil=null) { 03099 global $DB; 03100 03101 if ($key = $DB->get_record('user_private_key', array('script'=>$script, 'userid'=>$userid, 03102 'instance'=>$instance, 'iprestriction'=>$iprestriction, 03103 'validuntil'=>$validuntil))) { 03104 return $key->value; 03105 } else { 03106 return create_user_key($script, $userid, $instance, $iprestriction, $validuntil); 03107 } 03108 } 03109 03110 03119 function update_user_login_times() { 03120 global $USER, $DB; 03121 03122 $user = new stdClass(); 03123 $USER->lastlogin = $user->lastlogin = $USER->currentlogin; 03124 $USER->currentlogin = $user->lastaccess = $user->currentlogin = time(); 03125 03126 $user->id = $USER->id; 03127 03128 $DB->update_record('user', $user); 03129 return true; 03130 } 03131 03138 function user_not_fully_set_up($user) { 03139 if (isguestuser($user)) { 03140 return false; 03141 } 03142 return (empty($user->firstname) or empty($user->lastname) or empty($user->email) or over_bounce_threshold($user)); 03143 } 03144 03153 function over_bounce_threshold($user) { 03154 global $CFG, $DB; 03155 03156 if (empty($CFG->handlebounces)) { 03157 return false; 03158 } 03159 03160 if (empty($user->id)) { 03161 return false; 03162 } 03163 03164 // set sensible defaults 03165 if (empty($CFG->minbounces)) { 03166 $CFG->minbounces = 10; 03167 } 03168 if (empty($CFG->bounceratio)) { 03169 $CFG->bounceratio = .20; 03170 } 03171 $bouncecount = 0; 03172 $sendcount = 0; 03173 if ($bounce = $DB->get_record('user_preferences', array ('userid'=>$user->id, 'name'=>'email_bounce_count'))) { 03174 $bouncecount = $bounce->value; 03175 } 03176 if ($send = $DB->get_record('user_preferences', array('userid'=>$user->id, 'name'=>'email_send_count'))) { 03177 $sendcount = $send->value; 03178 } 03179 return ($bouncecount >= $CFG->minbounces && $bouncecount/$sendcount >= $CFG->bounceratio); 03180 } 03181 03190 function set_send_count($user,$reset=false) { 03191 global $DB; 03192 03193 if (empty($user->id)) { 03194 return; 03195 } 03196 03197 if ($pref = $DB->get_record('user_preferences', array('userid'=>$user->id, 'name'=>'email_send_count'))) { 03198 $pref->value = (!empty($reset)) ? 0 : $pref->value+1; 03199 $DB->update_record('user_preferences', $pref); 03200 } 03201 else if (!empty($reset)) { // if it's not there and we're resetting, don't bother. 03202 // make a new one 03203 $pref = new stdClass(); 03204 $pref->name = 'email_send_count'; 03205 $pref->value = 1; 03206 $pref->userid = $user->id; 03207 $DB->insert_record('user_preferences', $pref, false); 03208 } 03209 } 03210 03218 function set_bounce_count($user,$reset=false) { 03219 global $DB; 03220 03221 if ($pref = $DB->get_record('user_preferences', array('userid'=>$user->id, 'name'=>'email_bounce_count'))) { 03222 $pref->value = (!empty($reset)) ? 0 : $pref->value+1; 03223 $DB->update_record('user_preferences', $pref); 03224 } 03225 else if (!empty($reset)) { // if it's not there and we're resetting, don't bother. 03226 // make a new one 03227 $pref = new stdClass(); 03228 $pref->name = 'email_bounce_count'; 03229 $pref->value = 1; 03230 $pref->userid = $user->id; 03231 $DB->insert_record('user_preferences', $pref, false); 03232 } 03233 } 03234 03240 function update_login_count() { 03241 global $SESSION; 03242 03243 $max_logins = 10; 03244 03245 if (empty($SESSION->logincount)) { 03246 $SESSION->logincount = 1; 03247 } else { 03248 $SESSION->logincount++; 03249 } 03250 03251 if ($SESSION->logincount > $max_logins) { 03252 unset($SESSION->wantsurl); 03253 print_error('errortoomanylogins'); 03254 } 03255 } 03256 03262 function reset_login_count() { 03263 global $SESSION; 03264 03265 $SESSION->logincount = 0; 03266 } 03267 03279 function isediting() { 03280 global $PAGE; 03281 debugging('call to deprecated function isediting(). Please use $PAGE->user_is_editing() instead', DEBUG_DEVELOPER); 03282 return $PAGE->user_is_editing(); 03283 } 03284 03292 function ismoving($courseid) { 03293 global $USER; 03294 03295 if (!empty($USER->activitycopy)) { 03296 return ($USER->activitycopycourse == $courseid); 03297 } 03298 return false; 03299 } 03300 03317 function fullname($user, $override=false) { 03318 global $CFG, $SESSION; 03319 03320 if (!isset($user->firstname) and !isset($user->lastname)) { 03321 return ''; 03322 } 03323 03324 if (!$override) { 03325 if (!empty($CFG->forcefirstname)) { 03326 $user->firstname = $CFG->forcefirstname; 03327 } 03328 if (!empty($CFG->forcelastname)) { 03329 $user->lastname = $CFG->forcelastname; 03330 } 03331 } 03332 03333 if (!empty($SESSION->fullnamedisplay)) { 03334 $CFG->fullnamedisplay = $SESSION->fullnamedisplay; 03335 } 03336 03337 if (!isset($CFG->fullnamedisplay) or $CFG->fullnamedisplay === 'firstname lastname') { 03338 return $user->firstname .' '. $user->lastname; 03339 03340 } else if ($CFG->fullnamedisplay == 'lastname firstname') { 03341 return $user->lastname .' '. $user->firstname; 03342 03343 } else if ($CFG->fullnamedisplay == 'firstname') { 03344 if ($override) { 03345 return get_string('fullnamedisplay', '', $user); 03346 } else { 03347 return $user->firstname; 03348 } 03349 } 03350 03351 return get_string('fullnamedisplay', '', $user); 03352 } 03353 03362 function get_extra_user_fields($context, $already = array()) { 03363 global $CFG; 03364 03365 // Only users with permission get the extra fields 03366 if (!has_capability('moodle/site:viewuseridentity', $context)) { 03367 return array(); 03368 } 03369 03370 // Split showuseridentity on comma 03371 if (empty($CFG->showuseridentity)) { 03372 // Explode gives wrong result with empty string 03373 $extra = array(); 03374 } else { 03375 $extra = explode(',', $CFG->showuseridentity); 03376 } 03377 $renumber = false; 03378 foreach ($extra as $key => $field) { 03379 if (in_array($field, $already)) { 03380 unset($extra[$key]); 03381 $renumber = true; 03382 } 03383 } 03384 if ($renumber) { 03385 // For consistency, if entries are removed from array, renumber it 03386 // so they are numbered as you would expect 03387 $extra = array_merge($extra); 03388 } 03389 return $extra; 03390 } 03391 03404 function get_extra_user_fields_sql($context, $alias='', $prefix='', 03405 $already = array()) { 03406 $fields = get_extra_user_fields($context, $already); 03407 $result = ''; 03408 // Add punctuation for alias 03409 if ($alias !== '') { 03410 $alias .= '.'; 03411 } 03412 foreach ($fields as $field) { 03413 $result .= ', ' . $alias . $field; 03414 if ($prefix) { 03415 $result .= ' AS ' . $prefix . $field; 03416 } 03417 } 03418 return $result; 03419 } 03420 03427 function get_user_field_name($field) { 03428 // Some fields have language strings which are not the same as field name 03429 switch ($field) { 03430 case 'phone1' : return get_string('phone'); 03431 } 03432 // Otherwise just use the same lang string 03433 return get_string($field); 03434 } 03435 03444 function exists_auth_plugin($auth) { 03445 global $CFG; 03446 03447 if (file_exists("{$CFG->dirroot}/auth/$auth/auth.php")) { 03448 return is_readable("{$CFG->dirroot}/auth/$auth/auth.php"); 03449 } 03450 return false; 03451 } 03452 03459 function is_enabled_auth($auth) { 03460 if (empty($auth)) { 03461 return false; 03462 } 03463 03464 $enabled = get_enabled_auth_plugins(); 03465 03466 return in_array($auth, $enabled); 03467 } 03468 03476 function get_auth_plugin($auth) { 03477 global $CFG; 03478 03479 // check the plugin exists first 03480 if (! exists_auth_plugin($auth)) { 03481 print_error('authpluginnotfound', 'debug', '', $auth); 03482 } 03483 03484 // return auth plugin instance 03485 require_once "{$CFG->dirroot}/auth/$auth/auth.php"; 03486 $class = "auth_plugin_$auth"; 03487 return new $class; 03488 } 03489 03496 function get_enabled_auth_plugins($fix=false) { 03497 global $CFG; 03498 03499 $default = array('manual', 'nologin'); 03500 03501 if (empty($CFG->auth)) { 03502 $auths = array(); 03503 } else { 03504 $auths = explode(',', $CFG->auth); 03505 } 03506 03507 if ($fix) { 03508 $auths = array_unique($auths); 03509 foreach($auths as $k=>$authname) { 03510 if (!exists_auth_plugin($authname) or in_array($authname, $default)) { 03511 unset($auths[$k]); 03512 } 03513 } 03514 $newconfig = implode(',', $auths); 03515 if (!isset($CFG->auth) or $newconfig != $CFG->auth) { 03516 set_config('auth', $newconfig); 03517 } 03518 } 03519 03520 return (array_merge($default, $auths)); 03521 } 03522 03530 function is_internal_auth($auth) { 03531 $authplugin = get_auth_plugin($auth); // throws error if bad $auth 03532 return $authplugin->is_internal(); 03533 } 03534 03546 function is_restored_user($username) { 03547 global $CFG, $DB; 03548 03549 return $DB->record_exists('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id, 'password'=>'restored')); 03550 } 03551 03557 function get_user_fieldnames() { 03558 global $DB; 03559 03560 $fieldarray = $DB->get_columns('user'); 03561 unset($fieldarray['id']); 03562 $fieldarray = array_keys($fieldarray); 03563 03564 return $fieldarray; 03565 } 03566 03577 function create_user_record($username, $password, $auth = 'manual') { 03578 global $CFG, $DB; 03579 03580 //just in case check text case 03581 $username = trim(moodle_strtolower($username)); 03582 03583 $authplugin = get_auth_plugin($auth); 03584 03585 $newuser = new stdClass(); 03586 03587 if ($newinfo = $authplugin->get_userinfo($username)) { 03588 $newinfo = truncate_userinfo($newinfo); 03589 foreach ($newinfo as $key => $value){ 03590 $newuser->$key = $value; 03591 } 03592 } 03593 03594 if (!empty($newuser->email)) { 03595 if (email_is_not_allowed($newuser->email)) { 03596 unset($newuser->email); 03597 } 03598 } 03599 03600 if (!isset($newuser->city)) { 03601 $newuser->city = ''; 03602 } 03603 03604 $newuser->auth = $auth; 03605 $newuser->username = $username; 03606 03607 // fix for MDL-8480 03608 // user CFG lang for user if $newuser->lang is empty 03609 // or $user->lang is not an installed language 03610 if (empty($newuser->lang) || !get_string_manager()->translation_exists($newuser->lang)) { 03611 $newuser->lang = $CFG->lang; 03612 } 03613 $newuser->confirmed = 1; 03614 $newuser->lastip = getremoteaddr(); 03615 $newuser->timecreated = time(); 03616 $newuser->timemodified = $newuser->timecreated; 03617 $newuser->mnethostid = $CFG->mnet_localhost_id; 03618 03619 $newuser->id = $DB->insert_record('user', $newuser); 03620 $user = get_complete_user_data('id', $newuser->id); 03621 if (!empty($CFG->{'auth_'.$newuser->auth.'_forcechangepassword'})){ 03622 set_user_preference('auth_forcepasswordchange', 1, $user); 03623 } 03624 update_internal_user_password($user, $password); 03625 03626 // fetch full user record for the event, the complete user data contains too much info 03627 // and we want to be consistent with other places that trigger this event 03628 events_trigger('user_created', $DB->get_record('user', array('id'=>$user->id))); 03629 03630 return $user; 03631 } 03632 03640 function update_user_record($username) { 03641 global $DB, $CFG; 03642 03643 $username = trim(moodle_strtolower($username)); 03644 03645 $oldinfo = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id), '*', MUST_EXIST); 03646 $newuser = array(); 03647 $userauth = get_auth_plugin($oldinfo->auth); 03648 03649 if ($newinfo = $userauth->get_userinfo($username)) { 03650 $newinfo = truncate_userinfo($newinfo); 03651 foreach ($newinfo as $key => $value){ 03652 $key = strtolower($key); 03653 if (!property_exists($oldinfo, $key) or $key === 'username' or $key === 'id' 03654 or $key === 'auth' or $key === 'mnethostid' or $key === 'deleted') { 03655 // unknown or must not be changed 03656 continue; 03657 } 03658 $confval = $userauth->config->{'field_updatelocal_' . $key}; 03659 $lockval = $userauth->config->{'field_lock_' . $key}; 03660 if (empty($confval) || empty($lockval)) { 03661 continue; 03662 } 03663 if ($confval === 'onlogin') { 03664 // MDL-4207 Don't overwrite modified user profile values with 03665 // empty LDAP values when 'unlocked if empty' is set. The purpose 03666 // of the setting 'unlocked if empty' is to allow the user to fill 03667 // in a value for the selected field _if LDAP is giving 03668 // nothing_ for this field. Thus it makes sense to let this value 03669 // stand in until LDAP is giving a value for this field. 03670 if (!(empty($value) && $lockval === 'unlockedifempty')) { 03671 if ((string)$oldinfo->$key !== (string)$value) { 03672 $newuser[$key] = (string)$value; 03673 } 03674 } 03675 } 03676 } 03677 if ($newuser) { 03678 $newuser['id'] = $oldinfo->id; 03679 $newuser['timemodified'] = time(); 03680 $DB->update_record('user', $newuser); 03681 // fetch full user record for the event, the complete user data contains too much info 03682 // and we want to be consistent with other places that trigger this event 03683 events_trigger('user_updated', $DB->get_record('user', array('id'=>$oldinfo->id))); 03684 } 03685 } 03686 03687 return get_complete_user_data('id', $oldinfo->id); 03688 } 03689 03699 function truncate_userinfo($info) { 03700 // define the limits 03701 $limit = array( 03702 'username' => 100, 03703 'idnumber' => 255, 03704 'firstname' => 100, 03705 'lastname' => 100, 03706 'email' => 100, 03707 'icq' => 15, 03708 'phone1' => 20, 03709 'phone2' => 20, 03710 'institution' => 40, 03711 'department' => 30, 03712 'address' => 70, 03713 'city' => 120, 03714 'country' => 2, 03715 'url' => 255, 03716 ); 03717 03718 $textlib = textlib_get_instance(); 03719 // apply where needed 03720 foreach (array_keys($info) as $key) { 03721 if (!empty($limit[$key])) { 03722 $info[$key] = trim($textlib->substr($info[$key],0, $limit[$key])); 03723 } 03724 } 03725 03726 return $info; 03727 } 03728 03738 function delete_user($user) { 03739 global $CFG, $DB; 03740 require_once($CFG->libdir.'/grouplib.php'); 03741 require_once($CFG->libdir.'/gradelib.php'); 03742 require_once($CFG->dirroot.'/message/lib.php'); 03743 require_once($CFG->dirroot.'/tag/lib.php'); 03744 03745 // delete all grades - backup is kept in grade_grades_history table 03746 grade_user_delete($user->id); 03747 03748 //move unread messages from this user to read 03749 message_move_userfrom_unread2read($user->id); 03750 03751 // TODO: remove from cohorts using standard API here 03752 03753 // remove user tags 03754 tag_set('user', $user->id, array()); 03755 03756 // unconditionally unenrol from all courses 03757 enrol_user_delete($user); 03758 03759 // unenrol from all roles in all contexts 03760 role_unassign_all(array('userid'=>$user->id)); // this might be slow but it is really needed - modules might do some extra cleanup! 03761 03762 //now do a brute force cleanup 03763 03764 // remove from all cohorts 03765 $DB->delete_records('cohort_members', array('userid'=>$user->id)); 03766 03767 // remove from all groups 03768 $DB->delete_records('groups_members', array('userid'=>$user->id)); 03769 03770 // brute force unenrol from all courses 03771 $DB->delete_records('user_enrolments', array('userid'=>$user->id)); 03772 03773 // purge user preferences 03774 $DB->delete_records('user_preferences', array('userid'=>$user->id)); 03775 03776 // purge user extra profile info 03777 $DB->delete_records('user_info_data', array('userid'=>$user->id)); 03778 03779 // last course access not necessary either 03780 $DB->delete_records('user_lastaccess', array('userid'=>$user->id)); 03781 03782 // remove all user tokens 03783 $DB->delete_records('external_tokens', array('userid'=>$user->id)); 03784 03785 // unauthorise the user for all services 03786 $DB->delete_records('external_services_users', array('userid'=>$user->id)); 03787 03788 // force logout - may fail if file based sessions used, sorry 03789 session_kill_user($user->id); 03790 03791 // now do a final accesslib cleanup - removes all role assignments in user context and context itself 03792 delete_context(CONTEXT_USER, $user->id); 03793 03794 // workaround for bulk deletes of users with the same email address 03795 $delname = "$user->email.".time(); 03796 while ($DB->record_exists('user', array('username'=>$delname))) { // no need to use mnethostid here 03797 $delname++; 03798 } 03799 03800 // mark internal user record as "deleted" 03801 $updateuser = new stdClass(); 03802 $updateuser->id = $user->id; 03803 $updateuser->deleted = 1; 03804 $updateuser->username = $delname; // Remember it just in case 03805 $updateuser->email = md5($user->username);// Store hash of username, useful importing/restoring users 03806 $updateuser->idnumber = ''; // Clear this field to free it up 03807 $updateuser->timemodified = time(); 03808 03809 $DB->update_record('user', $updateuser); 03810 03811 // We will update the user's timemodified, as it will be passed to the user_deleted event, which 03812 // should know about this updated property persisted to the user's table. 03813 $user->timemodified = $updateuser->timemodified; 03814 03815 // notify auth plugin - do not block the delete even when plugin fails 03816 $authplugin = get_auth_plugin($user->auth); 03817 $authplugin->user_delete($user); 03818 03819 // any plugin that needs to cleanup should register this event 03820 events_trigger('user_deleted', $user); 03821 03822 return true; 03823 } 03824 03832 function guest_user() { 03833 global $CFG, $DB; 03834 03835 if ($newuser = $DB->get_record('user', array('id'=>$CFG->siteguest))) { 03836 $newuser->confirmed = 1; 03837 $newuser->lang = $CFG->lang; 03838 $newuser->lastip = getremoteaddr(); 03839 } 03840 03841 return $newuser; 03842 } 03843 03864 function authenticate_user_login($username, $password) { 03865 global $CFG, $DB; 03866 03867 $authsenabled = get_enabled_auth_plugins(); 03868 03869 if ($user = get_complete_user_data('username', $username, $CFG->mnet_localhost_id)) { 03870 $auth = empty($user->auth) ? 'manual' : $user->auth; // use manual if auth not set 03871 if (!empty($user->suspended)) { 03872 add_to_log(SITEID, 'login', 'error', 'index.php', $username); 03873 error_log('[client '.getremoteaddr()."] $CFG->wwwroot Suspended Login: $username ".$_SERVER['HTTP_USER_AGENT']); 03874 return false; 03875 } 03876 if ($auth=='nologin' or !is_enabled_auth($auth)) { 03877 add_to_log(SITEID, 'login', 'error', 'index.php', $username); 03878 error_log('[client '.getremoteaddr()."] $CFG->wwwroot Disabled Login: $username ".$_SERVER['HTTP_USER_AGENT']); 03879 return false; 03880 } 03881 $auths = array($auth); 03882 03883 } else { 03884 // check if there's a deleted record (cheaply) 03885 if ($DB->get_field('user', 'id', array('username'=>$username, 'deleted'=>1))) { 03886 error_log('[client '.getremoteaddr()."] $CFG->wwwroot Deleted Login: $username ".$_SERVER['HTTP_USER_AGENT']); 03887 return false; 03888 } 03889 03890 // User does not exist 03891 $auths = $authsenabled; 03892 $user = new stdClass(); 03893 $user->id = 0; 03894 } 03895 03896 foreach ($auths as $auth) { 03897 $authplugin = get_auth_plugin($auth); 03898 03899 // on auth fail fall through to the next plugin 03900 if (!$authplugin->user_login($username, $password)) { 03901 continue; 03902 } 03903 03904 // successful authentication 03905 if ($user->id) { // User already exists in database 03906 if (empty($user->auth)) { // For some reason auth isn't set yet 03907 $DB->set_field('user', 'auth', $auth, array('username'=>$username)); 03908 $user->auth = $auth; 03909 } 03910 if (empty($user->firstaccess)) { //prevent firstaccess from remaining 0 for manual account that never required confirmation 03911 $DB->set_field('user','firstaccess', $user->timemodified, array('id' => $user->id)); 03912 $user->firstaccess = $user->timemodified; 03913 } 03914 03915 update_internal_user_password($user, $password); // just in case salt or encoding were changed (magic quotes too one day) 03916 03917 if ($authplugin->is_synchronised_with_external()) { // update user record from external DB 03918 $user = update_user_record($username); 03919 } 03920 } else { 03921 // if user not found and user creation is not disabled, create it 03922 if (empty($CFG->authpreventaccountcreation)) { 03923 $user = create_user_record($username, $password, $auth); 03924 } else { 03925 continue; 03926 } 03927 } 03928 03929 $authplugin->sync_roles($user); 03930 03931 foreach ($authsenabled as $hau) { 03932 $hauth = get_auth_plugin($hau); 03933 $hauth->user_authenticated_hook($user, $username, $password); 03934 } 03935 03936 if (empty($user->id)) { 03937 return false; 03938 } 03939 03940 if (!empty($user->suspended)) { 03941 // just in case some auth plugin suspended account 03942 add_to_log(SITEID, 'login', 'error', 'index.php', $username); 03943 error_log('[client '.getremoteaddr()."] $CFG->wwwroot Suspended Login: $username ".$_SERVER['HTTP_USER_AGENT']); 03944 return false; 03945 } 03946 03947 return $user; 03948 } 03949 03950 // failed if all the plugins have failed 03951 add_to_log(SITEID, 'login', 'error', 'index.php', $username); 03952 if (debugging('', DEBUG_ALL)) { 03953 error_log('[client '.getremoteaddr()."] $CFG->wwwroot Failed Login: $username ".$_SERVER['HTTP_USER_AGENT']); 03954 } 03955 return false; 03956 } 03957 03970 function complete_user_login($user) { 03971 global $CFG, $USER; 03972 03973 // regenerate session id and delete old session, 03974 // this helps prevent session fixation attacks from the same domain 03975 session_regenerate_id(true); 03976 03977 // let enrol plugins deal with new enrolments if necessary 03978 enrol_check_plugins($user); 03979 03980 // check enrolments, load caps and setup $USER object 03981 session_set_user($user); 03982 03983 // reload preferences from DB 03984 unset($USER->preference); 03985 check_user_preferences_loaded($USER); 03986 03987 // update login times 03988 update_user_login_times(); 03989 03990 // extra session prefs init 03991 set_login_session_preferences(); 03992 03993 if (isguestuser()) { 03994 // no need to continue when user is THE guest 03995 return $USER; 03996 } 03997 03999 $userauth = get_auth_plugin($USER->auth); 04000 04002 if (get_user_preferences('auth_forcepasswordchange', false)){ 04003 if ($userauth->can_change_password()) { 04004 if ($changeurl = $userauth->change_password_url()) { 04005 redirect($changeurl); 04006 } else { 04007 redirect($CFG->httpswwwroot.'/login/change_password.php'); 04008 } 04009 } else { 04010 print_error('nopasswordchangeforced', 'auth'); 04011 } 04012 } 04013 return $USER; 04014 } 04015 04024 function validate_internal_user_password($user, $password) { 04025 global $CFG; 04026 04027 if (!isset($CFG->passwordsaltmain)) { 04028 $CFG->passwordsaltmain = ''; 04029 } 04030 04031 $validated = false; 04032 04033 if ($user->password === 'not cached') { 04034 // internal password is not used at all, it can not validate 04035 04036 } else if ($user->password === md5($password.$CFG->passwordsaltmain) 04037 or $user->password === md5($password) 04038 or $user->password === md5(addslashes($password).$CFG->passwordsaltmain) 04039 or $user->password === md5(addslashes($password))) { 04040 // note: we are intentionally using the addslashes() here because we 04041 // need to accept old password hashes of passwords with magic quotes 04042 $validated = true; 04043 04044 } else { 04045 for ($i=1; $i<=20; $i++) { //20 alternative salts should be enough, right? 04046 $alt = 'passwordsaltalt'.$i; 04047 if (!empty($CFG->$alt)) { 04048 if ($user->password === md5($password.$CFG->$alt) or $user->password === md5(addslashes($password).$CFG->$alt)) { 04049 $validated = true; 04050 break; 04051 } 04052 } 04053 } 04054 } 04055 04056 if ($validated) { 04057 // force update of password hash using latest main password salt and encoding if needed 04058 update_internal_user_password($user, $password); 04059 } 04060 04061 return $validated; 04062 } 04063 04070 function hash_internal_user_password($password) { 04071 global $CFG; 04072 04073 if (isset($CFG->passwordsaltmain)) { 04074 return md5($password.$CFG->passwordsaltmain); 04075 } else { 04076 return md5($password); 04077 } 04078 } 04079 04087 function update_internal_user_password($user, $password) { 04088 global $DB; 04089 04090 $authplugin = get_auth_plugin($user->auth); 04091 if ($authplugin->prevent_local_passwords()) { 04092 $hashedpassword = 'not cached'; 04093 } else { 04094 $hashedpassword = hash_internal_user_password($password); 04095 } 04096 04097 if ($user->password !== $hashedpassword) { 04098 $DB->set_field('user', 'password', $hashedpassword, array('id'=>$user->id)); 04099 $user->password = $hashedpassword; 04100 } 04101 04102 return true; 04103 } 04104 04116 function get_complete_user_data($field, $value, $mnethostid = null) { 04117 global $CFG, $DB; 04118 04119 if (!$field || !$value) { 04120 return false; 04121 } 04122 04124 $params = array('fieldval'=>$value); 04125 $constraints = "$field = :fieldval AND deleted <> 1"; 04126 04127 // If we are loading user data based on anything other than id, 04128 // we must also restrict our search based on mnet host. 04129 if ($field != 'id') { 04130 if (empty($mnethostid)) { 04131 // if empty, we restrict to local users 04132 $mnethostid = $CFG->mnet_localhost_id; 04133 } 04134 } 04135 if (!empty($mnethostid)) { 04136 $params['mnethostid'] = $mnethostid; 04137 $constraints .= " AND mnethostid = :mnethostid"; 04138 } 04139 04141 04142 if (! $user = $DB->get_record_select('user', $constraints, $params)) { 04143 return false; 04144 } 04145 04147 04148 // preload preference cache 04149 check_user_preferences_loaded($user); 04150 04151 // load course enrolment related stuff 04152 $user->lastcourseaccess = array(); // during last session 04153 $user->currentcourseaccess = array(); // during current session 04154 if ($lastaccesses = $DB->get_records('user_lastaccess', array('userid'=>$user->id))) { 04155 foreach ($lastaccesses as $lastaccess) { 04156 $user->lastcourseaccess[$lastaccess->courseid] = $lastaccess->timeaccess; 04157 } 04158 } 04159 04160 $sql = "SELECT g.id, g.courseid 04161 FROM {groups} g, {groups_members} gm 04162 WHERE gm.groupid=g.id AND gm.userid=?"; 04163 04164 // this is a special hack to speedup calendar display 04165 $user->groupmember = array(); 04166 if (!isguestuser($user)) { 04167 if ($groups = $DB->get_records_sql($sql, array($user->id))) { 04168 foreach ($groups as $group) { 04169 if (!array_key_exists($group->courseid, $user->groupmember)) { 04170 $user->groupmember[$group->courseid] = array(); 04171 } 04172 $user->groupmember[$group->courseid][$group->id] = $group->id; 04173 } 04174 } 04175 } 04176 04178 $user->profile = array(); 04179 if (!isguestuser($user)) { 04180 require_once($CFG->dirroot.'/user/profile/lib.php'); 04181 profile_load_custom_fields($user); 04182 } 04183 04185 if (!empty($user->description)) { 04186 $user->description = true; // No need to cart all of it around 04187 } 04188 if (isguestuser($user)) { 04189 $user->lang = $CFG->lang; // Guest language always same as site 04190 $user->firstname = get_string('guestuser'); // Name always in current language 04191 $user->lastname = ' '; 04192 } 04193 04194 return $user; 04195 } 04196 04205 function check_password_policy($password, &$errmsg) { 04206 global $CFG; 04207 04208 if (empty($CFG->passwordpolicy)) { 04209 return true; 04210 } 04211 04212 $textlib = textlib_get_instance(); 04213 $errmsg = ''; 04214 if ($textlib->strlen($password) < $CFG->minpasswordlength) { 04215 $errmsg .= '<div>'. get_string('errorminpasswordlength', 'auth', $CFG->minpasswordlength) .'</div>'; 04216 04217 } 04218 if (preg_match_all('/[[:digit:]]/u', $password, $matches) < $CFG->minpassworddigits) { 04219 $errmsg .= '<div>'. get_string('errorminpassworddigits', 'auth', $CFG->minpassworddigits) .'</div>'; 04220 04221 } 04222 if (preg_match_all('/[[:lower:]]/u', $password, $matches) < $CFG->minpasswordlower) { 04223 $errmsg .= '<div>'. get_string('errorminpasswordlower', 'auth', $CFG->minpasswordlower) .'</div>'; 04224 04225 } 04226 if (preg_match_all('/[[:upper:]]/u', $password, $matches) < $CFG->minpasswordupper) { 04227 $errmsg .= '<div>'. get_string('errorminpasswordupper', 'auth', $CFG->minpasswordupper) .'</div>'; 04228 04229 } 04230 if (preg_match_all('/[^[:upper:][:lower:][:digit:]]/u', $password, $matches) < $CFG->minpasswordnonalphanum) { 04231 $errmsg .= '<div>'. get_string('errorminpasswordnonalphanum', 'auth', $CFG->minpasswordnonalphanum) .'</div>'; 04232 } 04233 if (!check_consecutive_identical_characters($password, $CFG->maxconsecutiveidentchars)) { 04234 $errmsg .= '<div>'. get_string('errormaxconsecutiveidentchars', 'auth', $CFG->maxconsecutiveidentchars) .'</div>'; 04235 } 04236 04237 if ($errmsg == '') { 04238 return true; 04239 } else { 04240 return false; 04241 } 04242 } 04243 04244 04252 function set_login_session_preferences() { 04253 global $SESSION, $CFG; 04254 04255 $SESSION->justloggedin = true; 04256 04257 unset($SESSION->lang); 04258 } 04259 04260 04273 function delete_course($courseorid, $showfeedback = true) { 04274 global $DB; 04275 04276 if (is_object($courseorid)) { 04277 $courseid = $courseorid->id; 04278 $course = $courseorid; 04279 } else { 04280 $courseid = $courseorid; 04281 if (!$course = $DB->get_record('course', array('id'=>$courseid))) { 04282 return false; 04283 } 04284 } 04285 $context = get_context_instance(CONTEXT_COURSE, $courseid); 04286 04287 // frontpage course can not be deleted!! 04288 if ($courseid == SITEID) { 04289 return false; 04290 } 04291 04292 // make the course completely empty 04293 remove_course_contents($courseid, $showfeedback); 04294 04295 // delete the course and related context instance 04296 delete_context(CONTEXT_COURSE, $courseid); 04297 04298 // We will update the course's timemodified, as it will be passed to the course_deleted event, 04299 // which should know about this updated property, as this event is meant to pass the full course record 04300 $course->timemodified = time(); 04301 04302 $DB->delete_records("course", array("id"=>$courseid)); 04303 04304 //trigger events 04305 $course->context = $context; // you can not fetch context in the event because it was already deleted 04306 events_trigger('course_deleted', $course); 04307 04308 return true; 04309 } 04310 04330 function remove_course_contents($courseid, $showfeedback = true, array $options = null) { 04331 global $CFG, $DB, $OUTPUT; 04332 require_once($CFG->libdir.'/completionlib.php'); 04333 require_once($CFG->libdir.'/questionlib.php'); 04334 require_once($CFG->libdir.'/gradelib.php'); 04335 require_once($CFG->dirroot.'/group/lib.php'); 04336 require_once($CFG->dirroot.'/tag/coursetagslib.php'); 04337 require_once($CFG->dirroot.'/comment/lib.php'); 04338 require_once($CFG->dirroot.'/rating/lib.php'); 04339 04340 // NOTE: these concatenated strings are suboptimal, but it is just extra info... 04341 $strdeleted = get_string('deleted').' - '; 04342 04343 // Some crazy wishlist of stuff we should skip during purging of course content 04344 $options = (array)$options; 04345 04346 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST); 04347 $coursecontext = context_course::instance($courseid); 04348 $fs = get_file_storage(); 04349 04350 // Delete course completion information, this has to be done before grades and enrols 04351 $cc = new completion_info($course); 04352 $cc->clear_criteria(); 04353 if ($showfeedback) { 04354 echo $OUTPUT->notification($strdeleted.get_string('completion', 'completion'), 'notifysuccess'); 04355 } 04356 04357 // Remove all data from gradebook - this needs to be done before course modules 04358 // because while deleting this information, the system may need to reference 04359 // the course modules that own the grades. 04360 remove_course_grades($courseid, $showfeedback); 04361 remove_grade_letters($coursecontext, $showfeedback); 04362 04363 // Delete course blocks in any all child contexts, 04364 // they may depend on modules so delete them first 04365 $childcontexts = $coursecontext->get_child_contexts(); // returns all subcontexts since 2.2 04366 foreach ($childcontexts as $childcontext) { 04367 blocks_delete_all_for_context($childcontext->id); 04368 } 04369 unset($childcontexts); 04370 blocks_delete_all_for_context($coursecontext->id); 04371 if ($showfeedback) { 04372 echo $OUTPUT->notification($strdeleted.get_string('type_block_plural', 'plugin'), 'notifysuccess'); 04373 } 04374 04375 // Delete every instance of every module, 04376 // this has to be done before deleting of course level stuff 04377 $locations = get_plugin_list('mod'); 04378 foreach ($locations as $modname=>$moddir) { 04379 if ($modname === 'NEWMODULE') { 04380 continue; 04381 } 04382 if ($module = $DB->get_record('modules', array('name'=>$modname))) { 04383 include_once("$moddir/lib.php"); // Shows php warning only if plugin defective 04384 $moddelete = $modname .'_delete_instance'; // Delete everything connected to an instance 04385 $moddeletecourse = $modname .'_delete_course'; // Delete other stray stuff (uncommon) 04386 04387 if ($instances = $DB->get_records($modname, array('course'=>$course->id))) { 04388 foreach ($instances as $instance) { 04389 if ($cm = get_coursemodule_from_instance($modname, $instance->id, $course->id)) { 04391 question_delete_activity($cm, $showfeedback); 04392 } 04393 if (function_exists($moddelete)) { 04394 // This purges all module data in related tables, extra user prefs, settings, etc. 04395 $moddelete($instance->id); 04396 } else { 04397 // NOTE: we should not allow installation of modules with missing delete support! 04398 debugging("Defective module '$modname' detected when deleting course contents: missing function $moddelete()!"); 04399 $DB->delete_records($modname, array('id'=>$instance->id)); 04400 } 04401 04402 if ($cm) { 04403 // Delete cm and its context - orphaned contexts are purged in cron in case of any race condition 04404 context_helper::delete_instance(CONTEXT_MODULE, $cm->id); 04405 $DB->delete_records('course_modules', array('id'=>$cm->id)); 04406 } 04407 } 04408 } 04409 if (function_exists($moddeletecourse)) { 04410 // Execute ptional course cleanup callback 04411 $moddeletecourse($course, $showfeedback); 04412 } 04413 if ($instances and $showfeedback) { 04414 echo $OUTPUT->notification($strdeleted.get_string('pluginname', $modname), 'notifysuccess'); 04415 } 04416 } else { 04417 // Ooops, this module is not properly installed, force-delete it in the next block 04418 } 04419 } 04420 // We have tried to delete everything the nice way - now let's force-delete any remaining module data 04421 $cms = $DB->get_records('course_modules', array('course'=>$course->id)); 04422 foreach ($cms as $cm) { 04423 if ($module = $DB->get_record('modules', array('id'=>$cm->module))) { 04424 try { 04425 $DB->delete_records($module->name, array('id'=>$cm->instance)); 04426 } catch (Exception $e) { 04427 // Ignore weird or missing table problems 04428 } 04429 } 04430 context_helper::delete_instance(CONTEXT_MODULE, $cm->id); 04431 $DB->delete_records('course_modules', array('id'=>$cm->id)); 04432 } 04433 // Remove all data from availability and completion tables that is associated 04434 // with course-modules belonging to this course. Note this is done even if the 04435 // features are not enabled now, in case they were enabled previously 04436 $DB->delete_records_select('course_modules_completion', 04437 'coursemoduleid IN (SELECT id from {course_modules} WHERE course=?)', 04438 array($courseid)); 04439 $DB->delete_records_select('course_modules_availability', 04440 'coursemoduleid IN (SELECT id from {course_modules} WHERE course=?)', 04441 array($courseid)); 04442 if ($showfeedback) { 04443 echo $OUTPUT->notification($strdeleted.get_string('type_mod_plural', 'plugin'), 'notifysuccess'); 04444 } 04445 04446 // Cleanup the rest of plugins 04447 $cleanuplugintypes = array('report', 'coursereport', 'format'); 04448 foreach ($cleanuplugintypes as $type) { 04449 $plugins = get_plugin_list_with_function($type, 'delete_course', 'lib.php'); 04450 foreach ($plugins as $plugin=>$pluginfunction) { 04451 $pluginfunction($course->id, $showfeedback); 04452 } 04453 if ($showfeedback) { 04454 echo $OUTPUT->notification($strdeleted.get_string('type_'.$type.'_plural', 'plugin'), 'notifysuccess'); 04455 } 04456 } 04457 04458 // Delete questions and question categories 04459 question_delete_course($course, $showfeedback); 04460 if ($showfeedback) { 04461 echo $OUTPUT->notification($strdeleted.get_string('questions', 'question'), 'notifysuccess'); 04462 } 04463 04464 // Make sure there are no subcontexts left - all valid blocks and modules should be already gone 04465 $childcontexts = $coursecontext->get_child_contexts(); // returns all subcontexts since 2.2 04466 foreach ($childcontexts as $childcontext) { 04467 $childcontext->delete(); 04468 } 04469 unset($childcontexts); 04470 04471 // Remove all roles and enrolments by default 04472 if (empty($options['keep_roles_and_enrolments'])) { 04473 // this hack is used in restore when deleting contents of existing course 04474 role_unassign_all(array('contextid'=>$coursecontext->id), true); 04475 enrol_course_delete($course); 04476 if ($showfeedback) { 04477 echo $OUTPUT->notification($strdeleted.get_string('type_enrol_plural', 'plugin'), 'notifysuccess'); 04478 } 04479 } 04480 04481 // Delete any groups, removing members and grouping/course links first. 04482 if (empty($options['keep_groups_and_groupings'])) { 04483 groups_delete_groupings($course->id, $showfeedback); 04484 groups_delete_groups($course->id, $showfeedback); 04485 } 04486 04487 // filters be gone! 04488 filter_delete_all_for_context($coursecontext->id); 04489 04490 // die comments! 04491 comment::delete_comments($coursecontext->id); 04492 04493 // ratings are history too 04494 $delopt = new stdclass(); 04495 $delopt->contextid = $coursecontext->id; 04496 $rm = new rating_manager(); 04497 $rm->delete_ratings($delopt); 04498 04499 // Delete course tags 04500 coursetag_delete_course_tags($course->id, $showfeedback); 04501 04502 // Delete calendar events 04503 $DB->delete_records('event', array('courseid'=>$course->id)); 04504 $fs->delete_area_files($coursecontext->id, 'calendar'); 04505 04506 // Delete all related records in other core tables that may have a courseid 04507 // This array stores the tables that need to be cleared, as 04508 // table_name => column_name that contains the course id. 04509 $tablestoclear = array( 04510 'log' => 'course', // Course logs (NOTE: this might be changed in the future) 04511 'backup_courses' => 'courseid', // Scheduled backup stuff 04512 'user_lastaccess' => 'courseid', // User access info 04513 ); 04514 foreach ($tablestoclear as $table => $col) { 04515 $DB->delete_records($table, array($col=>$course->id)); 04516 } 04517 04518 // delete all course backup files 04519 $fs->delete_area_files($coursecontext->id, 'backup'); 04520 04521 // cleanup course record - remove links to deleted stuff 04522 $oldcourse = new stdClass(); 04523 $oldcourse->id = $course->id; 04524 $oldcourse->summary = ''; 04525 $oldcourse->modinfo = NULL; 04526 $oldcourse->legacyfiles = 0; 04527 $oldcourse->enablecompletion = 0; 04528 if (!empty($options['keep_groups_and_groupings'])) { 04529 $oldcourse->defaultgroupingid = 0; 04530 } 04531 $DB->update_record('course', $oldcourse); 04532 04533 // Delete course sections and user selections 04534 $DB->delete_records('course_sections', array('course'=>$course->id)); 04535 $DB->delete_records('course_display', array('course'=>$course->id)); 04536 04537 // delete legacy, section and any other course files 04538 $fs->delete_area_files($coursecontext->id, 'course'); // files from summary and section 04539 04540 // Delete all remaining stuff linked to context such as files, comments, ratings, etc. 04541 if (empty($options['keep_roles_and_enrolments']) and empty($options['keep_groups_and_groupings'])) { 04542 // Easy, do not delete the context itself... 04543 $coursecontext->delete_content(); 04544 04545 } else { 04546 // Hack alert!!!! 04547 // We can not drop all context stuff because it would bork enrolments and roles, 04548 // there might be also files used by enrol plugins... 04549 } 04550 04551 // Delete legacy files - just in case some files are still left there after conversion to new file api, 04552 // also some non-standard unsupported plugins may try to store something there 04553 fulldelete($CFG->dataroot.'/'.$course->id); 04554 04555 // Finally trigger the event 04556 $course->context = $coursecontext; // you can not access context in cron event later after course is deleted 04557 $course->options = $options; // not empty if we used any crazy hack 04558 events_trigger('course_content_removed', $course); 04559 04560 return true; 04561 } 04562 04574 function shift_course_mod_dates($modname, $fields, $timeshift, $courseid) { 04575 global $CFG, $DB; 04576 include_once($CFG->dirroot.'/mod/'.$modname.'/lib.php'); 04577 04578 $return = true; 04579 foreach ($fields as $field) { 04580 $updatesql = "UPDATE {".$modname."} 04581 SET $field = $field + ? 04582 WHERE course=? AND $field<>0 AND $field<>0"; 04583 $return = $DB->execute($updatesql, array($timeshift, $courseid)) && $return; 04584 } 04585 04586 $refreshfunction = $modname.'_refresh_events'; 04587 if (function_exists($refreshfunction)) { 04588 $refreshfunction($courseid); 04589 } 04590 04591 return $return; 04592 } 04593 04601 function reset_course_userdata($data) { 04602 global $CFG, $USER, $DB; 04603 require_once($CFG->libdir.'/gradelib.php'); 04604 require_once($CFG->libdir.'/completionlib.php'); 04605 require_once($CFG->dirroot.'/group/lib.php'); 04606 04607 $data->courseid = $data->id; 04608 $context = get_context_instance(CONTEXT_COURSE, $data->courseid); 04609 04610 // calculate the time shift of dates 04611 if (!empty($data->reset_start_date)) { 04612 // time part of course startdate should be zero 04613 $data->timeshift = $data->reset_start_date - usergetmidnight($data->reset_start_date_old); 04614 } else { 04615 $data->timeshift = 0; 04616 } 04617 04618 // result array: component, item, error 04619 $status = array(); 04620 04621 // start the resetting 04622 $componentstr = get_string('general'); 04623 04624 // move the course start time 04625 if (!empty($data->reset_start_date) and $data->timeshift) { 04626 // change course start data 04627 $DB->set_field('course', 'startdate', $data->reset_start_date, array('id'=>$data->courseid)); 04628 // update all course and group events - do not move activity events 04629 $updatesql = "UPDATE {event} 04630 SET timestart = timestart + ? 04631 WHERE courseid=? AND instance=0"; 04632 $DB->execute($updatesql, array($data->timeshift, $data->courseid)); 04633 04634 $status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false); 04635 } 04636 04637 if (!empty($data->reset_logs)) { 04638 $DB->delete_records('log', array('course'=>$data->courseid)); 04639 $status[] = array('component'=>$componentstr, 'item'=>get_string('deletelogs'), 'error'=>false); 04640 } 04641 04642 if (!empty($data->reset_events)) { 04643 $DB->delete_records('event', array('courseid'=>$data->courseid)); 04644 $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteevents', 'calendar'), 'error'=>false); 04645 } 04646 04647 if (!empty($data->reset_notes)) { 04648 require_once($CFG->dirroot.'/notes/lib.php'); 04649 note_delete_all($data->courseid); 04650 $status[] = array('component'=>$componentstr, 'item'=>get_string('deletenotes', 'notes'), 'error'=>false); 04651 } 04652 04653 if (!empty($data->delete_blog_associations)) { 04654 require_once($CFG->dirroot.'/blog/lib.php'); 04655 blog_remove_associations_for_course($data->courseid); 04656 $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteblogassociations', 'blog'), 'error'=>false); 04657 } 04658 04659 if (!empty($data->reset_course_completion)) { 04660 // Delete course completion information 04661 $course = $DB->get_record('course', array('id'=>$data->courseid)); 04662 $cc = new completion_info($course); 04663 $cc->delete_course_completion_data(); 04664 $status[] = array('component'=>$componentstr, 'item'=>get_string('deletecoursecompletiondata', 'completion'), 'error'=>false); 04665 } 04666 04667 $componentstr = get_string('roles'); 04668 04669 if (!empty($data->reset_roles_overrides)) { 04670 $children = get_child_contexts($context); 04671 foreach ($children as $child) { 04672 $DB->delete_records('role_capabilities', array('contextid'=>$child->id)); 04673 } 04674 $DB->delete_records('role_capabilities', array('contextid'=>$context->id)); 04675 //force refresh for logged in users 04676 mark_context_dirty($context->path); 04677 $status[] = array('component'=>$componentstr, 'item'=>get_string('deletecourseoverrides', 'role'), 'error'=>false); 04678 } 04679 04680 if (!empty($data->reset_roles_local)) { 04681 $children = get_child_contexts($context); 04682 foreach ($children as $child) { 04683 role_unassign_all(array('contextid'=>$child->id)); 04684 } 04685 //force refresh for logged in users 04686 mark_context_dirty($context->path); 04687 $status[] = array('component'=>$componentstr, 'item'=>get_string('deletelocalroles', 'role'), 'error'=>false); 04688 } 04689 04690 // First unenrol users - this cleans some of related user data too, such as forum subscriptions, tracking, etc. 04691 $data->unenrolled = array(); 04692 if (!empty($data->unenrol_users)) { 04693 $plugins = enrol_get_plugins(true); 04694 $instances = enrol_get_instances($data->courseid, true); 04695 foreach ($instances as $key=>$instance) { 04696 if (!isset($plugins[$instance->enrol])) { 04697 unset($instances[$key]); 04698 continue; 04699 } 04700 if (!$plugins[$instance->enrol]->allow_unenrol($instance)) { 04701 unset($instances[$key]); 04702 } 04703 } 04704 04705 $sqlempty = $DB->sql_empty(); 04706 foreach($data->unenrol_users as $withroleid) { 04707 $sql = "SELECT DISTINCT ue.userid, ue.enrolid 04708 FROM {user_enrolments} ue 04709 JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid) 04710 JOIN {context} c ON (c.contextlevel = :courselevel AND c.instanceid = e.courseid) 04711 JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.roleid = :roleid AND ra.userid = ue.userid)"; 04712 $params = array('courseid'=>$data->courseid, 'roleid'=>$withroleid, 'courselevel'=>CONTEXT_COURSE); 04713 04714 $rs = $DB->get_recordset_sql($sql, $params); 04715 foreach ($rs as $ue) { 04716 if (!isset($instances[$ue->enrolid])) { 04717 continue; 04718 } 04719 $plugins[$instances[$ue->enrolid]->enrol]->unenrol_user($instances[$ue->enrolid], $ue->userid); 04720 $data->unenrolled[$ue->userid] = $ue->userid; 04721 } 04722 } 04723 } 04724 if (!empty($data->unenrolled)) { 04725 $status[] = array('component'=>$componentstr, 'item'=>get_string('unenrol', 'enrol').' ('.count($data->unenrolled).')', 'error'=>false); 04726 } 04727 04728 04729 $componentstr = get_string('groups'); 04730 04731 // remove all group members 04732 if (!empty($data->reset_groups_members)) { 04733 groups_delete_group_members($data->courseid); 04734 $status[] = array('component'=>$componentstr, 'item'=>get_string('removegroupsmembers', 'group'), 'error'=>false); 04735 } 04736 04737 // remove all groups 04738 if (!empty($data->reset_groups_remove)) { 04739 groups_delete_groups($data->courseid, false); 04740 $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallgroups', 'group'), 'error'=>false); 04741 } 04742 04743 // remove all grouping members 04744 if (!empty($data->reset_groupings_members)) { 04745 groups_delete_groupings_groups($data->courseid, false); 04746 $status[] = array('component'=>$componentstr, 'item'=>get_string('removegroupingsmembers', 'group'), 'error'=>false); 04747 } 04748 04749 // remove all groupings 04750 if (!empty($data->reset_groupings_remove)) { 04751 groups_delete_groupings($data->courseid, false); 04752 $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallgroupings', 'group'), 'error'=>false); 04753 } 04754 04755 // Look in every instance of every module for data to delete 04756 $unsupported_mods = array(); 04757 if ($allmods = $DB->get_records('modules') ) { 04758 foreach ($allmods as $mod) { 04759 $modname = $mod->name; 04760 if (!$DB->count_records($modname, array('course'=>$data->courseid))) { 04761 continue; // skip mods with no instances 04762 } 04763 $modfile = $CFG->dirroot.'/mod/'. $modname.'/lib.php'; 04764 $moddeleteuserdata = $modname.'_reset_userdata'; // Function to delete user data 04765 if (file_exists($modfile)) { 04766 include_once($modfile); 04767 if (function_exists($moddeleteuserdata)) { 04768 $modstatus = $moddeleteuserdata($data); 04769 if (is_array($modstatus)) { 04770 $status = array_merge($status, $modstatus); 04771 } else { 04772 debugging('Module '.$modname.' returned incorrect staus - must be an array!'); 04773 } 04774 } else { 04775 $unsupported_mods[] = $mod; 04776 } 04777 } else { 04778 debugging('Missing lib.php in '.$modname.' module!'); 04779 } 04780 } 04781 } 04782 04783 // mention unsupported mods 04784 if (!empty($unsupported_mods)) { 04785 foreach($unsupported_mods as $mod) { 04786 $status[] = array('component'=>get_string('modulenameplural', $mod->name), 'item'=>'', 'error'=>get_string('resetnotimplemented')); 04787 } 04788 } 04789 04790 04791 $componentstr = get_string('gradebook', 'grades'); 04792 // reset gradebook 04793 if (!empty($data->reset_gradebook_items)) { 04794 remove_course_grades($data->courseid, false); 04795 grade_grab_course_grades($data->courseid); 04796 grade_regrade_final_grades($data->courseid); 04797 $status[] = array('component'=>$componentstr, 'item'=>get_string('removeallcourseitems', 'grades'), 'error'=>false); 04798 04799 } else if (!empty($data->reset_gradebook_grades)) { 04800 grade_course_reset($data->courseid); 04801 $status[] = array('component'=>$componentstr, 'item'=>get_string('removeallcoursegrades', 'grades'), 'error'=>false); 04802 } 04803 // reset comments 04804 if (!empty($data->reset_comments)) { 04805 require_once($CFG->dirroot.'/comment/lib.php'); 04806 comment::reset_course_page_comments($context); 04807 } 04808 04809 return $status; 04810 } 04811 04819 function generate_email_processing_address($modid,$modargs) { 04820 global $CFG; 04821 04822 $header = $CFG->mailprefix . substr(base64_encode(pack('C',$modid)),0,2).$modargs; 04823 return $header . substr(md5($header.get_site_identifier()),0,16).'@'.$CFG->maildomain; 04824 } 04825 04835 function moodle_process_email($modargs,$body) { 04836 global $DB; 04837 04838 // the first char should be an unencoded letter. We'll take this as an action 04839 switch ($modargs{0}) { 04840 case 'B': { // bounce 04841 list(,$userid) = unpack('V',base64_decode(substr($modargs,1,8))); 04842 if ($user = $DB->get_record("user", array('id'=>$userid), "id,email")) { 04843 // check the half md5 of their email 04844 $md5check = substr(md5($user->email),0,16); 04845 if ($md5check == substr($modargs, -16)) { 04846 set_bounce_count($user); 04847 } 04848 // else maybe they've already changed it? 04849 } 04850 } 04851 break; 04852 // maybe more later? 04853 } 04854 } 04855 04857 04865 function get_mailer($action='get') { 04866 global $CFG; 04867 04868 static $mailer = null; 04869 static $counter = 0; 04870 04871 if (!isset($CFG->smtpmaxbulk)) { 04872 $CFG->smtpmaxbulk = 1; 04873 } 04874 04875 if ($action == 'get') { 04876 $prevkeepalive = false; 04877 04878 if (isset($mailer) and $mailer->Mailer == 'smtp') { 04879 if ($counter < $CFG->smtpmaxbulk and !$mailer->IsError()) { 04880 $counter++; 04881 // reset the mailer 04882 $mailer->Priority = 3; 04883 $mailer->CharSet = 'UTF-8'; // our default 04884 $mailer->ContentType = "text/plain"; 04885 $mailer->Encoding = "8bit"; 04886 $mailer->From = "root@localhost"; 04887 $mailer->FromName = "Root User"; 04888 $mailer->Sender = ""; 04889 $mailer->Subject = ""; 04890 $mailer->Body = ""; 04891 $mailer->AltBody = ""; 04892 $mailer->ConfirmReadingTo = ""; 04893 04894 $mailer->ClearAllRecipients(); 04895 $mailer->ClearReplyTos(); 04896 $mailer->ClearAttachments(); 04897 $mailer->ClearCustomHeaders(); 04898 return $mailer; 04899 } 04900 04901 $prevkeepalive = $mailer->SMTPKeepAlive; 04902 get_mailer('flush'); 04903 } 04904 04905 include_once($CFG->libdir.'/phpmailer/moodle_phpmailer.php'); 04906 $mailer = new moodle_phpmailer(); 04907 04908 $counter = 1; 04909 04910 $mailer->Version = 'Moodle '.$CFG->version; // mailer version 04911 $mailer->PluginDir = $CFG->libdir.'/phpmailer/'; // plugin directory (eg smtp plugin) 04912 $mailer->CharSet = 'UTF-8'; 04913 04914 // some MTAs may do double conversion of LF if CRLF used, CRLF is required line ending in RFC 822bis 04915 if (isset($CFG->mailnewline) and $CFG->mailnewline == 'CRLF') { 04916 $mailer->LE = "\r\n"; 04917 } else { 04918 $mailer->LE = "\n"; 04919 } 04920 04921 if ($CFG->smtphosts == 'qmail') { 04922 $mailer->IsQmail(); // use Qmail system 04923 04924 } else if (empty($CFG->smtphosts)) { 04925 $mailer->IsMail(); // use PHP mail() = sendmail 04926 04927 } else { 04928 $mailer->IsSMTP(); // use SMTP directly 04929 if (!empty($CFG->debugsmtp)) { 04930 $mailer->SMTPDebug = true; 04931 } 04932 $mailer->Host = $CFG->smtphosts; // specify main and backup servers 04933 $mailer->SMTPKeepAlive = $prevkeepalive; // use previous keepalive 04934 04935 if ($CFG->smtpuser) { // Use SMTP authentication 04936 $mailer->SMTPAuth = true; 04937 $mailer->Username = $CFG->smtpuser; 04938 $mailer->Password = $CFG->smtppass; 04939 } 04940 } 04941 04942 return $mailer; 04943 } 04944 04945 $nothing = null; 04946 04947 // keep smtp session open after sending 04948 if ($action == 'buffer') { 04949 if (!empty($CFG->smtpmaxbulk)) { 04950 get_mailer('flush'); 04951 $m = get_mailer(); 04952 if ($m->Mailer == 'smtp') { 04953 $m->SMTPKeepAlive = true; 04954 } 04955 } 04956 return $nothing; 04957 } 04958 04959 // close smtp session, but continue buffering 04960 if ($action == 'flush') { 04961 if (isset($mailer) and $mailer->Mailer == 'smtp') { 04962 if (!empty($mailer->SMTPDebug)) { 04963 echo '<pre>'."\n"; 04964 } 04965 $mailer->SmtpClose(); 04966 if (!empty($mailer->SMTPDebug)) { 04967 echo '</pre>'; 04968 } 04969 } 04970 return $nothing; 04971 } 04972 04973 // close smtp session, do not buffer anymore 04974 if ($action == 'close') { 04975 if (isset($mailer) and $mailer->Mailer == 'smtp') { 04976 get_mailer('flush'); 04977 $mailer->SMTPKeepAlive = false; 04978 } 04979 $mailer = null; // better force new instance 04980 return $nothing; 04981 } 04982 } 04983 05005 function email_to_user($user, $from, $subject, $messagetext, $messagehtml='', $attachment='', $attachname='', $usetrueaddress=true, $replyto='', $replytoname='', $wordwrapwidth=79) { 05006 05007 global $CFG, $FULLME; 05008 05009 if (empty($user) || empty($user->email)) { 05010 mtrace('Error: lib/moodlelib.php email_to_user(): User is null or has no email'); 05011 return false; 05012 } 05013 05014 if (!empty($user->deleted)) { 05015 // do not mail delted users 05016 mtrace('Error: lib/moodlelib.php email_to_user(): User is deleted'); 05017 return false; 05018 } 05019 05020 if (!empty($CFG->noemailever)) { 05021 // hidden setting for development sites, set in config.php if needed 05022 mtrace('Error: lib/moodlelib.php email_to_user(): Not sending email due to noemailever config setting'); 05023 return true; 05024 } 05025 05026 if (!empty($CFG->divertallemailsto)) { 05027 $subject = "[DIVERTED {$user->email}] $subject"; 05028 $user = clone($user); 05029 $user->email = $CFG->divertallemailsto; 05030 } 05031 05032 // skip mail to suspended users 05033 if ((isset($user->auth) && $user->auth=='nologin') or (isset($user->suspended) && $user->suspended)) { 05034 return true; 05035 } 05036 05037 if (!validate_email($user->email)) { 05038 // we can not send emails to invalid addresses - it might create security issue or confuse the mailer 05039 $invalidemail = "User $user->id (".fullname($user).") email ($user->email) is invalid! Not sending."; 05040 error_log($invalidemail); 05041 if (CLI_SCRIPT) { 05042 // do not print this in standard web pages 05043 mtrace($invalidemail); 05044 } 05045 return false; 05046 } 05047 05048 if (over_bounce_threshold($user)) { 05049 $bouncemsg = "User $user->id (".fullname($user).") is over bounce threshold! Not sending."; 05050 error_log($bouncemsg); 05051 mtrace('Error: lib/moodlelib.php email_to_user(): '.$bouncemsg); 05052 return false; 05053 } 05054 05055 // If the user is a remote mnet user, parse the email text for URL to the 05056 // wwwroot and modify the url to direct the user's browser to login at their 05057 // home site (identity provider - idp) before hitting the link itself 05058 if (is_mnet_remote_user($user)) { 05059 require_once($CFG->dirroot.'/mnet/lib.php'); 05060 05061 $jumpurl = mnet_get_idp_jump_url($user); 05062 $callback = partial('mnet_sso_apply_indirection', $jumpurl); 05063 05064 $messagetext = preg_replace_callback("%($CFG->wwwroot[^[:space:]]*)%", 05065 $callback, 05066 $messagetext); 05067 $messagehtml = preg_replace_callback("%href=[\"'`]($CFG->wwwroot[\w_:\?=#&@/;.~-]*)[\"'`]%", 05068 $callback, 05069 $messagehtml); 05070 } 05071 $mail = get_mailer(); 05072 05073 if (!empty($mail->SMTPDebug)) { 05074 echo '<pre>' . "\n"; 05075 } 05076 05077 $temprecipients = array(); 05078 $tempreplyto = array(); 05079 05080 $supportuser = generate_email_supportuser(); 05081 05082 // make up an email address for handling bounces 05083 if (!empty($CFG->handlebounces)) { 05084 $modargs = 'B'.base64_encode(pack('V',$user->id)).substr(md5($user->email),0,16); 05085 $mail->Sender = generate_email_processing_address(0,$modargs); 05086 } else { 05087 $mail->Sender = $supportuser->email; 05088 } 05089 05090 if (is_string($from)) { // So we can pass whatever we want if there is need 05091 $mail->From = $CFG->noreplyaddress; 05092 $mail->FromName = $from; 05093 } else if ($usetrueaddress and $from->maildisplay) { 05094 $mail->From = $from->email; 05095 $mail->FromName = fullname($from); 05096 } else { 05097 $mail->From = $CFG->noreplyaddress; 05098 $mail->FromName = fullname($from); 05099 if (empty($replyto)) { 05100 $tempreplyto[] = array($CFG->noreplyaddress, get_string('noreplyname')); 05101 } 05102 } 05103 05104 if (!empty($replyto)) { 05105 $tempreplyto[] = array($replyto, $replytoname); 05106 } 05107 05108 $mail->Subject = substr($subject, 0, 900); 05109 05110 $temprecipients[] = array($user->email, fullname($user)); 05111 05112 $mail->WordWrap = $wordwrapwidth; // set word wrap 05113 05114 if (!empty($from->customheaders)) { // Add custom headers 05115 if (is_array($from->customheaders)) { 05116 foreach ($from->customheaders as $customheader) { 05117 $mail->AddCustomHeader($customheader); 05118 } 05119 } else { 05120 $mail->AddCustomHeader($from->customheaders); 05121 } 05122 } 05123 05124 if (!empty($from->priority)) { 05125 $mail->Priority = $from->priority; 05126 } 05127 05128 if ($messagehtml && !empty($user->mailformat) && $user->mailformat == 1) { // Don't ever send HTML to users who don't want it 05129 $mail->IsHTML(true); 05130 $mail->Encoding = 'quoted-printable'; // Encoding to use 05131 $mail->Body = $messagehtml; 05132 $mail->AltBody = "\n$messagetext\n"; 05133 } else { 05134 $mail->IsHTML(false); 05135 $mail->Body = "\n$messagetext\n"; 05136 } 05137 05138 if ($attachment && $attachname) { 05139 if (preg_match( "~\\.\\.~" ,$attachment )) { // Security check for ".." in dir path 05140 $temprecipients[] = array($supportuser->email, fullname($supportuser, true)); 05141 $mail->AddStringAttachment('Error in attachment. User attempted to attach a filename with a unsafe name.', 'error.txt', '8bit', 'text/plain'); 05142 } else { 05143 require_once($CFG->libdir.'/filelib.php'); 05144 $mimetype = mimeinfo('type', $attachname); 05145 $mail->AddAttachment($CFG->dataroot .'/'. $attachment, $attachname, 'base64', $mimetype); 05146 } 05147 } 05148 05149 // Check if the email should be sent in an other charset then the default UTF-8 05150 if ((!empty($CFG->sitemailcharset) || !empty($CFG->allowusermailcharset))) { 05151 05152 // use the defined site mail charset or eventually the one preferred by the recipient 05153 $charset = $CFG->sitemailcharset; 05154 if (!empty($CFG->allowusermailcharset)) { 05155 if ($useremailcharset = get_user_preferences('mailcharset', '0', $user->id)) { 05156 $charset = $useremailcharset; 05157 } 05158 } 05159 05160 // convert all the necessary strings if the charset is supported 05161 $charsets = get_list_of_charsets(); 05162 unset($charsets['UTF-8']); 05163 if (in_array($charset, $charsets)) { 05164 $textlib = textlib_get_instance(); 05165 $mail->CharSet = $charset; 05166 $mail->FromName = $textlib->convert($mail->FromName, 'utf-8', strtolower($charset)); 05167 $mail->Subject = $textlib->convert($mail->Subject, 'utf-8', strtolower($charset)); 05168 $mail->Body = $textlib->convert($mail->Body, 'utf-8', strtolower($charset)); 05169 $mail->AltBody = $textlib->convert($mail->AltBody, 'utf-8', strtolower($charset)); 05170 05171 foreach ($temprecipients as $key => $values) { 05172 $temprecipients[$key][1] = $textlib->convert($values[1], 'utf-8', strtolower($charset)); 05173 } 05174 foreach ($tempreplyto as $key => $values) { 05175 $tempreplyto[$key][1] = $textlib->convert($values[1], 'utf-8', strtolower($charset)); 05176 } 05177 } 05178 } 05179 05180 foreach ($temprecipients as $values) { 05181 $mail->AddAddress($values[0], $values[1]); 05182 } 05183 foreach ($tempreplyto as $values) { 05184 $mail->AddReplyTo($values[0], $values[1]); 05185 } 05186 05187 if ($mail->Send()) { 05188 set_send_count($user); 05189 $mail->IsSMTP(); // use SMTP directly 05190 if (!empty($mail->SMTPDebug)) { 05191 echo '</pre>'; 05192 } 05193 return true; 05194 } else { 05195 mtrace('ERROR: '. $mail->ErrorInfo); 05196 add_to_log(SITEID, 'library', 'mailer', $FULLME, 'ERROR: '. $mail->ErrorInfo); 05197 if (!empty($mail->SMTPDebug)) { 05198 echo '</pre>'; 05199 } 05200 return false; 05201 } 05202 } 05203 05210 function generate_email_signoff() { 05211 global $CFG; 05212 05213 $signoff = "\n"; 05214 if (!empty($CFG->supportname)) { 05215 $signoff .= $CFG->supportname."\n"; 05216 } 05217 if (!empty($CFG->supportemail)) { 05218 $signoff .= $CFG->supportemail."\n"; 05219 } 05220 if (!empty($CFG->supportpage)) { 05221 $signoff .= $CFG->supportpage."\n"; 05222 } 05223 return $signoff; 05224 } 05225 05231 function generate_email_supportuser() { 05232 global $CFG; 05233 05234 static $supportuser; 05235 05236 if (!empty($supportuser)) { 05237 return $supportuser; 05238 } 05239 05240 $supportuser = new stdClass(); 05241 $supportuser->email = $CFG->supportemail ? $CFG->supportemail : $CFG->noreplyaddress; 05242 $supportuser->firstname = $CFG->supportname ? $CFG->supportname : get_string('noreplyname'); 05243 $supportuser->lastname = ''; 05244 $supportuser->maildisplay = true; 05245 05246 return $supportuser; 05247 } 05248 05249 05258 function setnew_password_and_mail($user) { 05259 global $CFG, $DB; 05260 05261 $site = get_site(); 05262 05263 $supportuser = generate_email_supportuser(); 05264 05265 $newpassword = generate_password(); 05266 05267 $DB->set_field('user', 'password', hash_internal_user_password($newpassword), array('id'=>$user->id)); 05268 05269 $a = new stdClass(); 05270 $a->firstname = fullname($user, true); 05271 $a->sitename = format_string($site->fullname); 05272 $a->username = $user->username; 05273 $a->newpassword = $newpassword; 05274 $a->link = $CFG->wwwroot .'/login/'; 05275 $a->signoff = generate_email_signoff(); 05276 05277 $message = get_string('newusernewpasswordtext', '', $a); 05278 05279 $subject = format_string($site->fullname) .': '. get_string('newusernewpasswordsubj'); 05280 05281 //directly email rather than using the messaging system to ensure its not routed to a popup or jabber 05282 return email_to_user($user, $supportuser, $subject, $message); 05283 05284 } 05285 05292 function reset_password_and_mail($user) { 05293 global $CFG; 05294 05295 $site = get_site(); 05296 $supportuser = generate_email_supportuser(); 05297 05298 $userauth = get_auth_plugin($user->auth); 05299 if (!$userauth->can_reset_password() or !is_enabled_auth($user->auth)) { 05300 trigger_error("Attempt to reset user password for user $user->username with Auth $user->auth."); 05301 return false; 05302 } 05303 05304 $newpassword = generate_password(); 05305 05306 if (!$userauth->user_update_password($user, $newpassword)) { 05307 print_error("cannotsetpassword"); 05308 } 05309 05310 $a = new stdClass(); 05311 $a->firstname = $user->firstname; 05312 $a->lastname = $user->lastname; 05313 $a->sitename = format_string($site->fullname); 05314 $a->username = $user->username; 05315 $a->newpassword = $newpassword; 05316 $a->link = $CFG->httpswwwroot .'/login/change_password.php'; 05317 $a->signoff = generate_email_signoff(); 05318 05319 $message = get_string('newpasswordtext', '', $a); 05320 05321 $subject = format_string($site->fullname) .': '. get_string('changedpassword'); 05322 05323 unset_user_preference('create_password', $user); // prevent cron from generating the password 05324 05325 //directly email rather than using the messaging system to ensure its not routed to a popup or jabber 05326 return email_to_user($user, $supportuser, $subject, $message); 05327 05328 } 05329 05337 function send_confirmation_email($user) { 05338 global $CFG; 05339 05340 $site = get_site(); 05341 $supportuser = generate_email_supportuser(); 05342 05343 $data = new stdClass(); 05344 $data->firstname = fullname($user); 05345 $data->sitename = format_string($site->fullname); 05346 $data->admin = generate_email_signoff(); 05347 05348 $subject = get_string('emailconfirmationsubject', '', format_string($site->fullname)); 05349 05350 $data->link = $CFG->wwwroot .'/login/confirm.php?data='. $user->secret .'/'. urlencode($user->username); 05351 $message = get_string('emailconfirmation', '', $data); 05352 $messagehtml = text_to_html(get_string('emailconfirmation', '', $data), false, false, true); 05353 05354 $user->mailformat = 1; // Always send HTML version as well 05355 05356 //directly email rather than using the messaging system to ensure its not routed to a popup or jabber 05357 return email_to_user($user, $supportuser, $subject, $message, $messagehtml); 05358 05359 } 05360 05368 function send_password_change_confirmation_email($user) { 05369 global $CFG; 05370 05371 $site = get_site(); 05372 $supportuser = generate_email_supportuser(); 05373 05374 $data = new stdClass(); 05375 $data->firstname = $user->firstname; 05376 $data->lastname = $user->lastname; 05377 $data->sitename = format_string($site->fullname); 05378 $data->link = $CFG->httpswwwroot .'/login/forgot_password.php?p='. $user->secret .'&s='. urlencode($user->username); 05379 $data->admin = generate_email_signoff(); 05380 05381 $message = get_string('emailpasswordconfirmation', '', $data); 05382 $subject = get_string('emailpasswordconfirmationsubject', '', format_string($site->fullname)); 05383 05384 //directly email rather than using the messaging system to ensure its not routed to a popup or jabber 05385 return email_to_user($user, $supportuser, $subject, $message); 05386 05387 } 05388 05396 function send_password_change_info($user) { 05397 global $CFG; 05398 05399 $site = get_site(); 05400 $supportuser = generate_email_supportuser(); 05401 $systemcontext = get_context_instance(CONTEXT_SYSTEM); 05402 05403 $data = new stdClass(); 05404 $data->firstname = $user->firstname; 05405 $data->lastname = $user->lastname; 05406 $data->sitename = format_string($site->fullname); 05407 $data->admin = generate_email_signoff(); 05408 05409 $userauth = get_auth_plugin($user->auth); 05410 05411 if (!is_enabled_auth($user->auth) or $user->auth == 'nologin') { 05412 $message = get_string('emailpasswordchangeinfodisabled', '', $data); 05413 $subject = get_string('emailpasswordchangeinfosubject', '', format_string($site->fullname)); 05414 //directly email rather than using the messaging system to ensure its not routed to a popup or jabber 05415 return email_to_user($user, $supportuser, $subject, $message); 05416 } 05417 05418 if ($userauth->can_change_password() and $userauth->change_password_url()) { 05419 // we have some external url for password changing 05420 $data->link .= $userauth->change_password_url(); 05421 05422 } else { 05423 //no way to change password, sorry 05424 $data->link = ''; 05425 } 05426 05427 if (!empty($data->link) and has_capability('moodle/user:changeownpassword', $systemcontext, $user->id)) { 05428 $message = get_string('emailpasswordchangeinfo', '', $data); 05429 $subject = get_string('emailpasswordchangeinfosubject', '', format_string($site->fullname)); 05430 } else { 05431 $message = get_string('emailpasswordchangeinfofail', '', $data); 05432 $subject = get_string('emailpasswordchangeinfosubject', '', format_string($site->fullname)); 05433 } 05434 05435 //directly email rather than using the messaging system to ensure its not routed to a popup or jabber 05436 return email_to_user($user, $supportuser, $subject, $message); 05437 05438 } 05439 05448 function email_is_not_allowed($email) { 05449 global $CFG; 05450 05451 if (!empty($CFG->allowemailaddresses)) { 05452 $allowed = explode(' ', $CFG->allowemailaddresses); 05453 foreach ($allowed as $allowedpattern) { 05454 $allowedpattern = trim($allowedpattern); 05455 if (!$allowedpattern) { 05456 continue; 05457 } 05458 if (strpos($allowedpattern, '.') === 0) { 05459 if (strpos(strrev($email), strrev($allowedpattern)) === 0) { 05460 // subdomains are in a form ".example.com" - matches "xxx@anything.example.com" 05461 return false; 05462 } 05463 05464 } else if (strpos(strrev($email), strrev('@'.$allowedpattern)) === 0) { // Match! (bug 5250) 05465 return false; 05466 } 05467 } 05468 return get_string('emailonlyallowed', '', $CFG->allowemailaddresses); 05469 05470 } else if (!empty($CFG->denyemailaddresses)) { 05471 $denied = explode(' ', $CFG->denyemailaddresses); 05472 foreach ($denied as $deniedpattern) { 05473 $deniedpattern = trim($deniedpattern); 05474 if (!$deniedpattern) { 05475 continue; 05476 } 05477 if (strpos($deniedpattern, '.') === 0) { 05478 if (strpos(strrev($email), strrev($deniedpattern)) === 0) { 05479 // subdomains are in a form ".example.com" - matches "xxx@anything.example.com" 05480 return get_string('emailnotallowed', '', $CFG->denyemailaddresses); 05481 } 05482 05483 } else if (strpos(strrev($email), strrev('@'.$deniedpattern)) === 0) { // Match! (bug 5250) 05484 return get_string('emailnotallowed', '', $CFG->denyemailaddresses); 05485 } 05486 } 05487 } 05488 05489 return false; 05490 } 05491 05493 05499 function get_file_storage() { 05500 global $CFG; 05501 05502 static $fs = null; 05503 05504 if ($fs) { 05505 return $fs; 05506 } 05507 05508 require_once("$CFG->libdir/filelib.php"); 05509 05510 if (isset($CFG->filedir)) { 05511 $filedir = $CFG->filedir; 05512 } else { 05513 $filedir = $CFG->dataroot.'/filedir'; 05514 } 05515 05516 if (isset($CFG->trashdir)) { 05517 $trashdirdir = $CFG->trashdir; 05518 } else { 05519 $trashdirdir = $CFG->dataroot.'/trashdir'; 05520 } 05521 05522 $fs = new file_storage($filedir, $trashdirdir, "$CFG->tempdir/filestorage", $CFG->directorypermissions, $CFG->filepermissions); 05523 05524 return $fs; 05525 } 05526 05532 function get_file_browser() { 05533 global $CFG; 05534 05535 static $fb = null; 05536 05537 if ($fb) { 05538 return $fb; 05539 } 05540 05541 require_once("$CFG->libdir/filelib.php"); 05542 05543 $fb = new file_browser(); 05544 05545 return $fb; 05546 } 05547 05554 function get_file_packer($mimetype='application/zip') { 05555 global $CFG; 05556 05557 static $fp = array();; 05558 05559 if (isset($fp[$mimetype])) { 05560 return $fp[$mimetype]; 05561 } 05562 05563 switch ($mimetype) { 05564 case 'application/zip': 05565 case 'application/vnd.moodle.backup': 05566 $classname = 'zip_packer'; 05567 break; 05568 case 'application/x-tar': 05569 // $classname = 'tar_packer'; 05570 // break; 05571 default: 05572 return false; 05573 } 05574 05575 require_once("$CFG->libdir/filestorage/$classname.php"); 05576 $fp[$mimetype] = new $classname(); 05577 05578 return $fp[$mimetype]; 05579 } 05580 05587 function valid_uploaded_file($newfile) { 05588 if (empty($newfile)) { 05589 return ''; 05590 } 05591 if (is_uploaded_file($newfile['tmp_name']) and $newfile['size'] > 0) { 05592 return $newfile['tmp_name']; 05593 } else { 05594 return ''; 05595 } 05596 } 05597 05621 function get_max_upload_file_size($sitebytes=0, $coursebytes=0, $modulebytes=0) { 05622 05623 if (! $filesize = ini_get('upload_max_filesize')) { 05624 $filesize = '5M'; 05625 } 05626 $minimumsize = get_real_size($filesize); 05627 05628 if ($postsize = ini_get('post_max_size')) { 05629 $postsize = get_real_size($postsize); 05630 if ($postsize < $minimumsize) { 05631 $minimumsize = $postsize; 05632 } 05633 } 05634 05635 if ($sitebytes and $sitebytes < $minimumsize) { 05636 $minimumsize = $sitebytes; 05637 } 05638 05639 if ($coursebytes and $coursebytes < $minimumsize) { 05640 $minimumsize = $coursebytes; 05641 } 05642 05643 if ($modulebytes and $modulebytes < $minimumsize) { 05644 $minimumsize = $modulebytes; 05645 } 05646 05647 return $minimumsize; 05648 } 05649 05666 function get_max_upload_sizes($sitebytes=0, $coursebytes=0, $modulebytes=0) { 05667 global $CFG; 05668 05669 if (!$maxsize = get_max_upload_file_size($sitebytes, $coursebytes, $modulebytes)) { 05670 return array(); 05671 } 05672 05673 $filesize[intval($maxsize)] = display_size($maxsize); 05674 05675 $sizelist = array(10240, 51200, 102400, 512000, 1048576, 2097152, 05676 5242880, 10485760, 20971520, 52428800, 104857600); 05677 05678 // Allow maxbytes to be selected if it falls outside the above boundaries 05679 if (isset($CFG->maxbytes) && !in_array(get_real_size($CFG->maxbytes), $sizelist)) { 05680 // note: get_real_size() is used in order to prevent problems with invalid values 05681 $sizelist[] = get_real_size($CFG->maxbytes); 05682 } 05683 05684 foreach ($sizelist as $sizebytes) { 05685 if ($sizebytes < $maxsize) { 05686 $filesize[intval($sizebytes)] = display_size($sizebytes); 05687 } 05688 } 05689 05690 krsort($filesize, SORT_NUMERIC); 05691 05692 return $filesize; 05693 } 05694 05713 function get_directory_list($rootdir, $excludefiles='', $descend=true, $getdirs=false, $getfiles=true) { 05714 05715 $dirs = array(); 05716 05717 if (!$getdirs and !$getfiles) { // Nothing to show 05718 return $dirs; 05719 } 05720 05721 if (!is_dir($rootdir)) { // Must be a directory 05722 return $dirs; 05723 } 05724 05725 if (!$dir = opendir($rootdir)) { // Can't open it for some reason 05726 return $dirs; 05727 } 05728 05729 if (!is_array($excludefiles)) { 05730 $excludefiles = array($excludefiles); 05731 } 05732 05733 while (false !== ($file = readdir($dir))) { 05734 $firstchar = substr($file, 0, 1); 05735 if ($firstchar == '.' or $file == 'CVS' or in_array($file, $excludefiles)) { 05736 continue; 05737 } 05738 $fullfile = $rootdir .'/'. $file; 05739 if (filetype($fullfile) == 'dir') { 05740 if ($getdirs) { 05741 $dirs[] = $file; 05742 } 05743 if ($descend) { 05744 $subdirs = get_directory_list($fullfile, $excludefiles, $descend, $getdirs, $getfiles); 05745 foreach ($subdirs as $subdir) { 05746 $dirs[] = $file .'/'. $subdir; 05747 } 05748 } 05749 } else if ($getfiles) { 05750 $dirs[] = $file; 05751 } 05752 } 05753 closedir($dir); 05754 05755 asort($dirs); 05756 05757 return $dirs; 05758 } 05759 05760 05770 function get_directory_size($rootdir, $excludefile='') { 05771 global $CFG; 05772 05773 // do it this way if we can, it's much faster 05774 if (!empty($CFG->pathtodu) && is_executable(trim($CFG->pathtodu))) { 05775 $command = trim($CFG->pathtodu).' -sk '.escapeshellarg($rootdir); 05776 $output = null; 05777 $return = null; 05778 exec($command,$output,$return); 05779 if (is_array($output)) { 05780 return get_real_size(intval($output[0]).'k'); // we told it to return k. 05781 } 05782 } 05783 05784 if (!is_dir($rootdir)) { // Must be a directory 05785 return 0; 05786 } 05787 05788 if (!$dir = @opendir($rootdir)) { // Can't open it for some reason 05789 return 0; 05790 } 05791 05792 $size = 0; 05793 05794 while (false !== ($file = readdir($dir))) { 05795 $firstchar = substr($file, 0, 1); 05796 if ($firstchar == '.' or $file == 'CVS' or $file == $excludefile) { 05797 continue; 05798 } 05799 $fullfile = $rootdir .'/'. $file; 05800 if (filetype($fullfile) == 'dir') { 05801 $size += get_directory_size($fullfile, $excludefile); 05802 } else { 05803 $size += filesize($fullfile); 05804 } 05805 } 05806 closedir($dir); 05807 05808 return $size; 05809 } 05810 05823 function display_size($size) { 05824 05825 static $gb, $mb, $kb, $b; 05826 05827 if (empty($gb)) { 05828 $gb = get_string('sizegb'); 05829 $mb = get_string('sizemb'); 05830 $kb = get_string('sizekb'); 05831 $b = get_string('sizeb'); 05832 } 05833 05834 if ($size >= 1073741824) { 05835 $size = round($size / 1073741824 * 10) / 10 . $gb; 05836 } else if ($size >= 1048576) { 05837 $size = round($size / 1048576 * 10) / 10 . $mb; 05838 } else if ($size >= 1024) { 05839 $size = round($size / 1024 * 10) / 10 . $kb; 05840 } else { 05841 $size = intval($size) .' '. $b; // file sizes over 2GB can not work in 32bit PHP anyway 05842 } 05843 return $size; 05844 } 05845 05854 function clean_filename($string) { 05855 return clean_param($string, PARAM_FILE); 05856 } 05857 05858 05860 05866 function current_language() { 05867 global $CFG, $USER, $SESSION, $COURSE; 05868 05869 if (!empty($COURSE->id) and $COURSE->id != SITEID and !empty($COURSE->lang)) { // Course language can override all other settings for this page 05870 $return = $COURSE->lang; 05871 05872 } else if (!empty($SESSION->lang)) { // Session language can override other settings 05873 $return = $SESSION->lang; 05874 05875 } else if (!empty($USER->lang)) { 05876 $return = $USER->lang; 05877 05878 } else if (isset($CFG->lang)) { 05879 $return = $CFG->lang; 05880 05881 } else { 05882 $return = 'en'; 05883 } 05884 05885 $return = str_replace('_utf8', '', $return); // Just in case this slipped in from somewhere by accident 05886 05887 return $return; 05888 } 05889 05898 function get_parent_language($lang=null) { 05899 global $COURSE, $SESSION; 05900 05901 //let's hack around the current language 05902 if (!empty($lang)) { 05903 $old_course_lang = empty($COURSE->lang) ? '' : $COURSE->lang; 05904 $old_session_lang = empty($SESSION->lang) ? '' : $SESSION->lang; 05905 $COURSE->lang = ''; 05906 $SESSION->lang = $lang; 05907 } 05908 05909 $parentlang = get_string('parentlanguage', 'langconfig'); 05910 if ($parentlang === 'en') { 05911 $parentlang = ''; 05912 } 05913 05914 //let's hack around the current language 05915 if (!empty($lang)) { 05916 $COURSE->lang = $old_course_lang; 05917 $SESSION->lang = $old_session_lang; 05918 } 05919 05920 return $parentlang; 05921 } 05922 05932 function get_string_manager($forcereload=false) { 05933 global $CFG; 05934 05935 static $singleton = null; 05936 05937 if ($forcereload) { 05938 $singleton = null; 05939 } 05940 if ($singleton === null) { 05941 if (empty($CFG->early_install_lang)) { 05942 05943 if (empty($CFG->langcacheroot)) { 05944 $langcacheroot = $CFG->cachedir . '/lang'; 05945 } else { 05946 $langcacheroot = $CFG->langcacheroot; 05947 } 05948 05949 if (empty($CFG->langlist)) { 05950 $translist = array(); 05951 } else { 05952 $translist = explode(',', $CFG->langlist); 05953 } 05954 05955 if (empty($CFG->langmenucachefile)) { 05956 $langmenucache = $CFG->cachedir . '/languages'; 05957 } else { 05958 $langmenucache = $CFG->langmenucachefile; 05959 } 05960 05961 $singleton = new core_string_manager($CFG->langotherroot, $CFG->langlocalroot, $langcacheroot, 05962 !empty($CFG->langstringcache), $translist, $langmenucache); 05963 05964 } else { 05965 $singleton = new install_string_manager(); 05966 } 05967 } 05968 05969 return $singleton; 05970 } 05971 05972 05981 interface string_manager { 05992 public function get_string($identifier, $component = '', $a = NULL, $lang = NULL); 05993 06006 public function string_exists($identifier, $component); 06007 06014 public function get_list_of_countries($returnall = false, $lang = NULL); 06015 06024 public function get_list_of_languages($lang = NULL, $standard = 'iso6392'); 06025 06033 public function translation_exists($lang, $includeall = true); 06034 06040 public function get_list_of_translations($returnall = false); 06041 06048 public function get_list_of_currencies($lang = NULL); 06049 06058 public function load_component_strings($component, $lang, $disablecache=false, $disablelocal=false); 06059 06063 public function reset_caches(); 06064 } 06065 06066 06074 class core_string_manager implements string_manager { 06076 protected $otherroot; 06078 protected $localroot; 06080 protected $cacheroot; 06082 protected $cache = array(); 06084 protected $countgetstring = 0; 06086 protected $countmemcache = 0; 06088 protected $countdiskcache = 0; 06090 protected $usediskcache; 06091 /* @var array limit list of translations */ 06092 protected $translist; 06094 protected $menucache; 06095 06106 public function __construct($otherroot, $localroot, $cacheroot, $usediskcache, $translist, $menucache) { 06107 $this->otherroot = $otherroot; 06108 $this->localroot = $localroot; 06109 $this->cacheroot = $cacheroot; 06110 $this->usediskcache = $usediskcache; 06111 $this->translist = $translist; 06112 $this->menucache = $menucache; 06113 } 06114 06120 public function get_language_dependencies($lang) { 06121 if ($lang === 'en') { 06122 return array(); 06123 } 06124 if (!file_exists("$this->otherroot/$lang/langconfig.php")) { 06125 return array(); 06126 } 06127 $string = array(); 06128 include("$this->otherroot/$lang/langconfig.php"); 06129 06130 if (empty($string['parentlanguage'])) { 06131 return array($lang); 06132 } else { 06133 $parentlang = $string['parentlanguage']; 06134 unset($string); 06135 return array_merge($this->get_language_dependencies($parentlang), array($lang)); 06136 } 06137 } 06138 06147 public function load_component_strings($component, $lang, $disablecache=false, $disablelocal=false) { 06148 global $CFG; 06149 06150 list($plugintype, $pluginname) = normalize_component($component); 06151 if ($plugintype == 'core' and is_null($pluginname)) { 06152 $component = 'core'; 06153 } else { 06154 $component = $plugintype . '_' . $pluginname; 06155 } 06156 06157 if (!$disablecache) { 06158 // try in-memory cache first 06159 if (isset($this->cache[$lang][$component])) { 06160 $this->countmemcache++; 06161 return $this->cache[$lang][$component]; 06162 } 06163 06164 // try on-disk cache then 06165 if ($this->usediskcache and file_exists($this->cacheroot . "/$lang/$component.php")) { 06166 $this->countdiskcache++; 06167 include($this->cacheroot . "/$lang/$component.php"); 06168 return $this->cache[$lang][$component]; 06169 } 06170 } 06171 06172 // no cache found - let us merge all possible sources of the strings 06173 if ($plugintype === 'core') { 06174 $file = $pluginname; 06175 if ($file === null) { 06176 $file = 'moodle'; 06177 } 06178 $string = array(); 06179 // first load english pack 06180 if (!file_exists("$CFG->dirroot/lang/en/$file.php")) { 06181 return array(); 06182 } 06183 include("$CFG->dirroot/lang/en/$file.php"); 06184 $originalkeys = array_keys($string); 06185 $originalkeys = array_flip($originalkeys); 06186 06187 // and then corresponding local if present and allowed 06188 if (!$disablelocal and file_exists("$this->localroot/en_local/$file.php")) { 06189 include("$this->localroot/en_local/$file.php"); 06190 } 06191 // now loop through all langs in correct order 06192 $deps = $this->get_language_dependencies($lang); 06193 foreach ($deps as $dep) { 06194 // the main lang string location 06195 if (file_exists("$this->otherroot/$dep/$file.php")) { 06196 include("$this->otherroot/$dep/$file.php"); 06197 } 06198 if (!$disablelocal and file_exists("$this->localroot/{$dep}_local/$file.php")) { 06199 include("$this->localroot/{$dep}_local/$file.php"); 06200 } 06201 } 06202 06203 } else { 06204 if (!$location = get_plugin_directory($plugintype, $pluginname) or !is_dir($location)) { 06205 return array(); 06206 } 06207 if ($plugintype === 'mod') { 06208 // bloody mod hack 06209 $file = $pluginname; 06210 } else { 06211 $file = $plugintype . '_' . $pluginname; 06212 } 06213 $string = array(); 06214 // first load English pack 06215 if (!file_exists("$location/lang/en/$file.php")) { 06216 //English pack does not exist, so do not try to load anything else 06217 return array(); 06218 } 06219 include("$location/lang/en/$file.php"); 06220 $originalkeys = array_keys($string); 06221 $originalkeys = array_flip($originalkeys); 06222 // and then corresponding local english if present 06223 if (!$disablelocal and file_exists("$this->localroot/en_local/$file.php")) { 06224 include("$this->localroot/en_local/$file.php"); 06225 } 06226 06227 // now loop through all langs in correct order 06228 $deps = $this->get_language_dependencies($lang); 06229 foreach ($deps as $dep) { 06230 // legacy location - used by contrib only 06231 if (file_exists("$location/lang/$dep/$file.php")) { 06232 include("$location/lang/$dep/$file.php"); 06233 } 06234 // the main lang string location 06235 if (file_exists("$this->otherroot/$dep/$file.php")) { 06236 include("$this->otherroot/$dep/$file.php"); 06237 } 06238 // local customisations 06239 if (!$disablelocal and file_exists("$this->localroot/{$dep}_local/$file.php")) { 06240 include("$this->localroot/{$dep}_local/$file.php"); 06241 } 06242 } 06243 } 06244 06245 // we do not want any extra strings from other languages - everything must be in en lang pack 06246 $string = array_intersect_key($string, $originalkeys); 06247 06248 // now we have a list of strings from all possible sources. put it into both in-memory and on-disk 06249 // caches so we do not need to do all this merging and dependencies resolving again 06250 $this->cache[$lang][$component] = $string; 06251 if ($this->usediskcache) { 06252 check_dir_exists("$this->cacheroot/$lang"); 06253 file_put_contents("$this->cacheroot/$lang/$component.php", "<?php \$this->cache['$lang']['$component'] = ".var_export($string, true).";"); 06254 } 06255 return $string; 06256 } 06257 06270 public function string_exists($identifier, $component) { 06271 $identifier = clean_param($identifier, PARAM_STRINGID); 06272 if (empty($identifier)) { 06273 return false; 06274 } 06275 $lang = current_language(); 06276 $string = $this->load_component_strings($component, $lang); 06277 return isset($string[$identifier]); 06278 } 06279 06290 public function get_string($identifier, $component = '', $a = NULL, $lang = NULL) { 06291 $this->countgetstring++; 06292 // there are very many uses of these time formating strings without the 'langconfig' component, 06293 // it would not be reasonable to expect that all of them would be converted during 2.0 migration 06294 static $langconfigstrs = array( 06295 'strftimedate' => 1, 06296 'strftimedatefullshort' => 1, 06297 'strftimedateshort' => 1, 06298 'strftimedatetime' => 1, 06299 'strftimedatetimeshort' => 1, 06300 'strftimedaydate' => 1, 06301 'strftimedaydatetime' => 1, 06302 'strftimedayshort' => 1, 06303 'strftimedaytime' => 1, 06304 'strftimemonthyear' => 1, 06305 'strftimerecent' => 1, 06306 'strftimerecentfull' => 1, 06307 'strftimetime' => 1); 06308 06309 if (empty($component)) { 06310 if (isset($langconfigstrs[$identifier])) { 06311 $component = 'langconfig'; 06312 } else { 06313 $component = 'moodle'; 06314 } 06315 } 06316 06317 if ($lang === NULL) { 06318 $lang = current_language(); 06319 } 06320 06321 $string = $this->load_component_strings($component, $lang); 06322 06323 if (!isset($string[$identifier])) { 06324 if ($component === 'pix' or $component === 'core_pix') { 06325 // this component contains only alt tags for emoticons, 06326 // not all of them are supposed to be defined 06327 return ''; 06328 } 06329 if ($identifier === 'parentlanguage' and ($component === 'langconfig' or $component === 'core_langconfig')) { 06330 // parentlanguage is a special string, undefined means use English if not defined 06331 return 'en'; 06332 } 06333 if ($this->usediskcache) { 06334 // maybe the on-disk cache is dirty - let the last attempt be to find the string in original sources 06335 $string = $this->load_component_strings($component, $lang, true); 06336 } 06337 if (!isset($string[$identifier])) { 06338 // the string is still missing - should be fixed by developer 06339 list($plugintype, $pluginname) = normalize_component($component); 06340 if ($plugintype == 'core') { 06341 $file = "lang/en/{$component}.php"; 06342 } else if ($plugintype == 'mod') { 06343 $file = "mod/{$pluginname}/lang/en/{$pluginname}.php"; 06344 } else { 06345 $path = get_plugin_directory($plugintype, $pluginname); 06346 $file = "{$path}/lang/en/{$plugintype}_{$pluginname}.php"; 06347 } 06348 debugging("Invalid get_string() identifier: '{$identifier}' or component '{$component}'. " . 06349 "Perhaps you are missing \$string['{$identifier}'] = ''; in {$file}?", DEBUG_DEVELOPER); 06350 return "[[$identifier]]"; 06351 } 06352 } 06353 06354 $string = $string[$identifier]; 06355 06356 if ($a !== NULL) { 06357 if (is_object($a) or is_array($a)) { 06358 $a = (array)$a; 06359 $search = array(); 06360 $replace = array(); 06361 foreach ($a as $key=>$value) { 06362 if (is_int($key)) { 06363 // we do not support numeric keys - sorry! 06364 continue; 06365 } 06366 if (is_object($value) or is_array($value)) { 06367 // we support just string as value 06368 continue; 06369 } 06370 $search[] = '{$a->'.$key.'}'; 06371 $replace[] = (string)$value; 06372 } 06373 if ($search) { 06374 $string = str_replace($search, $replace, $string); 06375 } 06376 } else { 06377 $string = str_replace('{$a}', (string)$a, $string); 06378 } 06379 } 06380 06381 return $string; 06382 } 06383 06388 public function get_performance_summary() { 06389 return array(array( 06390 'langcountgetstring' => $this->countgetstring, 06391 'langcountmemcache' => $this->countmemcache, 06392 'langcountdiskcache' => $this->countdiskcache, 06393 ), array( 06394 'langcountgetstring' => 'get_string calls', 06395 'langcountmemcache' => 'strings mem cache hits', 06396 'langcountdiskcache' => 'strings disk cache hits', 06397 )); 06398 } 06399 06407 public function get_list_of_countries($returnall = false, $lang = NULL) { 06408 global $CFG; 06409 06410 if ($lang === NULL) { 06411 $lang = current_language(); 06412 } 06413 06414 $countries = $this->load_component_strings('core_countries', $lang); 06415 collatorlib::asort($countries); 06416 if (!$returnall and !empty($CFG->allcountrycodes)) { 06417 $enabled = explode(',', $CFG->allcountrycodes); 06418 $return = array(); 06419 foreach ($enabled as $c) { 06420 if (isset($countries[$c])) { 06421 $return[$c] = $countries[$c]; 06422 } 06423 } 06424 return $return; 06425 } 06426 06427 return $countries; 06428 } 06429 06439 public function get_list_of_languages($lang = NULL, $standard = 'iso6391') { 06440 if ($lang === NULL) { 06441 $lang = current_language(); 06442 } 06443 06444 if ($standard === 'iso6392') { 06445 $langs = $this->load_component_strings('core_iso6392', $lang); 06446 ksort($langs); 06447 return $langs; 06448 06449 } else if ($standard === 'iso6391') { 06450 $langs2 = $this->load_component_strings('core_iso6392', $lang); 06451 static $mapping = array('aar' => 'aa', 'abk' => 'ab', 'afr' => 'af', 'aka' => 'ak', 'sqi' => 'sq', 'amh' => 'am', 'ara' => 'ar', 'arg' => 'an', 'hye' => 'hy', 06452 'asm' => 'as', 'ava' => 'av', 'ave' => 'ae', 'aym' => 'ay', 'aze' => 'az', 'bak' => 'ba', 'bam' => 'bm', 'eus' => 'eu', 'bel' => 'be', 'ben' => 'bn', 'bih' => 'bh', 06453 'bis' => 'bi', 'bos' => 'bs', 'bre' => 'br', 'bul' => 'bg', 'mya' => 'my', 'cat' => 'ca', 'cha' => 'ch', 'che' => 'ce', 'zho' => 'zh', 'chu' => 'cu', 'chv' => 'cv', 06454 'cor' => 'kw', 'cos' => 'co', 'cre' => 'cr', 'ces' => 'cs', 'dan' => 'da', 'div' => 'dv', 'nld' => 'nl', 'dzo' => 'dz', 'eng' => 'en', 'epo' => 'eo', 'est' => 'et', 06455 'ewe' => 'ee', 'fao' => 'fo', 'fij' => 'fj', 'fin' => 'fi', 'fra' => 'fr', 'fry' => 'fy', 'ful' => 'ff', 'kat' => 'ka', 'deu' => 'de', 'gla' => 'gd', 'gle' => 'ga', 06456 'glg' => 'gl', 'glv' => 'gv', 'ell' => 'el', 'grn' => 'gn', 'guj' => 'gu', 'hat' => 'ht', 'hau' => 'ha', 'heb' => 'he', 'her' => 'hz', 'hin' => 'hi', 'hmo' => 'ho', 06457 'hrv' => 'hr', 'hun' => 'hu', 'ibo' => 'ig', 'isl' => 'is', 'ido' => 'io', 'iii' => 'ii', 'iku' => 'iu', 'ile' => 'ie', 'ina' => 'ia', 'ind' => 'id', 'ipk' => 'ik', 06458 'ita' => 'it', 'jav' => 'jv', 'jpn' => 'ja', 'kal' => 'kl', 'kan' => 'kn', 'kas' => 'ks', 'kau' => 'kr', 'kaz' => 'kk', 'khm' => 'km', 'kik' => 'ki', 'kin' => 'rw', 06459 'kir' => 'ky', 'kom' => 'kv', 'kon' => 'kg', 'kor' => 'ko', 'kua' => 'kj', 'kur' => 'ku', 'lao' => 'lo', 'lat' => 'la', 'lav' => 'lv', 'lim' => 'li', 'lin' => 'ln', 06460 'lit' => 'lt', 'ltz' => 'lb', 'lub' => 'lu', 'lug' => 'lg', 'mkd' => 'mk', 'mah' => 'mh', 'mal' => 'ml', 'mri' => 'mi', 'mar' => 'mr', 'msa' => 'ms', 'mlg' => 'mg', 06461 'mlt' => 'mt', 'mon' => 'mn', 'nau' => 'na', 'nav' => 'nv', 'nbl' => 'nr', 'nde' => 'nd', 'ndo' => 'ng', 'nep' => 'ne', 'nno' => 'nn', 'nob' => 'nb', 'nor' => 'no', 06462 'nya' => 'ny', 'oci' => 'oc', 'oji' => 'oj', 'ori' => 'or', 'orm' => 'om', 'oss' => 'os', 'pan' => 'pa', 'fas' => 'fa', 'pli' => 'pi', 'pol' => 'pl', 'por' => 'pt', 06463 'pus' => 'ps', 'que' => 'qu', 'roh' => 'rm', 'ron' => 'ro', 'run' => 'rn', 'rus' => 'ru', 'sag' => 'sg', 'san' => 'sa', 'sin' => 'si', 'slk' => 'sk', 'slv' => 'sl', 06464 'sme' => 'se', 'smo' => 'sm', 'sna' => 'sn', 'snd' => 'sd', 'som' => 'so', 'sot' => 'st', 'spa' => 'es', 'srd' => 'sc', 'srp' => 'sr', 'ssw' => 'ss', 'sun' => 'su', 06465 'swa' => 'sw', 'swe' => 'sv', 'tah' => 'ty', 'tam' => 'ta', 'tat' => 'tt', 'tel' => 'te', 'tgk' => 'tg', 'tgl' => 'tl', 'tha' => 'th', 'bod' => 'bo', 'tir' => 'ti', 06466 'ton' => 'to', 'tsn' => 'tn', 'tso' => 'ts', 'tuk' => 'tk', 'tur' => 'tr', 'twi' => 'tw', 'uig' => 'ug', 'ukr' => 'uk', 'urd' => 'ur', 'uzb' => 'uz', 'ven' => 've', 06467 'vie' => 'vi', 'vol' => 'vo', 'cym' => 'cy', 'wln' => 'wa', 'wol' => 'wo', 'xho' => 'xh', 'yid' => 'yi', 'yor' => 'yo', 'zha' => 'za', 'zul' => 'zu'); 06468 $langs1 = array(); 06469 foreach ($mapping as $c2=>$c1) { 06470 $langs1[$c1] = $langs2[$c2]; 06471 } 06472 ksort($langs1); 06473 return $langs1; 06474 06475 } else { 06476 debugging('Unsupported $standard parameter in get_list_of_languages() method: '.$standard); 06477 } 06478 06479 return array(); 06480 } 06481 06489 public function translation_exists($lang, $includeall = true) { 06490 06491 if (strpos($lang, '_local') !== false) { 06492 // _local packs are not real translations 06493 return false; 06494 } 06495 if (!$includeall and !empty($this->translist)) { 06496 if (!in_array($lang, $this->translist)) { 06497 return false; 06498 } 06499 } 06500 if ($lang === 'en') { 06501 // part of distribution 06502 return true; 06503 } 06504 return file_exists("$this->otherroot/$lang/langconfig.php"); 06505 } 06506 06512 public function get_list_of_translations($returnall = false) { 06513 global $CFG; 06514 06515 $languages = array(); 06516 06517 if (!empty($CFG->langcache) and is_readable($this->menucache)) { 06518 // try to re-use the cached list of all available languages 06519 $cachedlist = json_decode(file_get_contents($this->menucache), true); 06520 06521 if (is_array($cachedlist) and !empty($cachedlist)) { 06522 // the cache file is restored correctly 06523 06524 if (!$returnall and !empty($this->translist)) { 06525 // return just enabled translations 06526 foreach ($cachedlist as $langcode => $langname) { 06527 if (in_array($langcode, $this->translist)) { 06528 $languages[$langcode] = $langname; 06529 } 06530 } 06531 return $languages; 06532 06533 } else { 06534 // return all translations 06535 return $cachedlist; 06536 } 06537 } 06538 } 06539 06540 // the cached list of languages is not available, let us populate the list 06541 06542 if (!$returnall and !empty($this->translist)) { 06543 // return only some translations 06544 foreach ($this->translist as $lang) { 06545 $lang = trim($lang); //Just trim spaces to be a bit more permissive 06546 if (strstr($lang, '_local') !== false) { 06547 continue; 06548 } 06549 if (strstr($lang, '_utf8') !== false) { 06550 continue; 06551 } 06552 if ($lang !== 'en' and !file_exists("$this->otherroot/$lang/langconfig.php")) { 06553 // some broken or missing lang - can not switch to it anyway 06554 continue; 06555 } 06556 $string = $this->load_component_strings('langconfig', $lang); 06557 if (!empty($string['thislanguage'])) { 06558 $languages[$lang] = $string['thislanguage'].' ('. $lang .')'; 06559 } 06560 unset($string); 06561 } 06562 06563 } else { 06564 // return all languages available in system 06565 $langdirs = get_list_of_plugins('', '', $this->otherroot); 06566 06567 $langdirs = array_merge($langdirs, array("$CFG->dirroot/lang/en"=>'en')); 06568 // Sort all 06569 06570 // Loop through all langs and get info 06571 foreach ($langdirs as $lang) { 06572 if (strstr($lang, '_local') !== false) { 06573 continue; 06574 } 06575 if (strstr($lang, '_utf8') !== false) { 06576 continue; 06577 } 06578 $string = $this->load_component_strings('langconfig', $lang); 06579 if (!empty($string['thislanguage'])) { 06580 $languages[$lang] = $string['thislanguage'].' ('. $lang .')'; 06581 } 06582 unset($string); 06583 } 06584 06585 if (!empty($CFG->langcache) and !empty($this->menucache)) { 06586 // cache the list so that it can be used next time 06587 collatorlib::asort($languages); 06588 check_dir_exists(dirname($this->menucache), true, true); 06589 file_put_contents($this->menucache, json_encode($languages)); 06590 } 06591 } 06592 06593 collatorlib::asort($languages); 06594 06595 return $languages; 06596 } 06597 06604 public function get_list_of_currencies($lang = NULL) { 06605 if ($lang === NULL) { 06606 $lang = current_language(); 06607 } 06608 06609 $currencies = $this->load_component_strings('core_currencies', $lang); 06610 asort($currencies); 06611 06612 return $currencies; 06613 } 06614 06618 public function reset_caches() { 06619 global $CFG; 06620 require_once("$CFG->libdir/filelib.php"); 06621 06622 // clear the on-disk disk with aggregated string files 06623 fulldelete($this->cacheroot); 06624 06625 // clear the in-memory cache of loaded strings 06626 $this->cache = array(); 06627 06628 // clear the cache containing the list of available translations 06629 // and re-populate it again 06630 fulldelete($this->menucache); 06631 $this->get_list_of_translations(true); 06632 } 06633 } 06634 06635 06645 class install_string_manager implements string_manager { 06647 protected $installroot; 06648 06652 public function __construct() { 06653 global $CFG; 06654 $this->installroot = "$CFG->dirroot/install/lang"; 06655 } 06656 06665 public function load_component_strings($component, $lang, $disablecache=false, $disablelocal=false) { 06666 // not needed in installer 06667 return array(); 06668 } 06669 06682 public function string_exists($identifier, $component) { 06683 $identifier = clean_param($identifier, PARAM_STRINGID); 06684 if (empty($identifier)) { 06685 return false; 06686 } 06687 // simple old style hack ;) 06688 $str = get_string($identifier, $component); 06689 return (strpos($str, '[[') === false); 06690 } 06691 06702 public function get_string($identifier, $component = '', $a = NULL, $lang = NULL) { 06703 if (!$component) { 06704 $component = 'moodle'; 06705 } 06706 06707 if ($lang === NULL) { 06708 $lang = current_language(); 06709 } 06710 06711 //get parent lang 06712 $parent = ''; 06713 if ($lang !== 'en' and $identifier !== 'parentlanguage' and $component !== 'langconfig') { 06714 if (file_exists("$this->installroot/$lang/langconfig.php")) { 06715 $string = array(); 06716 include("$this->installroot/$lang/langconfig.php"); 06717 if (isset($string['parentlanguage'])) { 06718 $parent = $string['parentlanguage']; 06719 } 06720 unset($string); 06721 } 06722 } 06723 06724 // include en string first 06725 if (!file_exists("$this->installroot/en/$component.php")) { 06726 return "[[$identifier]]"; 06727 } 06728 $string = array(); 06729 include("$this->installroot/en/$component.php"); 06730 06731 // now override en with parent if defined 06732 if ($parent and $parent !== 'en' and file_exists("$this->installroot/$parent/$component.php")) { 06733 include("$this->installroot/$parent/$component.php"); 06734 } 06735 06736 // finally override with requested language 06737 if ($lang !== 'en' and file_exists("$this->installroot/$lang/$component.php")) { 06738 include("$this->installroot/$lang/$component.php"); 06739 } 06740 06741 if (!isset($string[$identifier])) { 06742 return "[[$identifier]]"; 06743 } 06744 06745 $string = $string[$identifier]; 06746 06747 if ($a !== NULL) { 06748 if (is_object($a) or is_array($a)) { 06749 $a = (array)$a; 06750 $search = array(); 06751 $replace = array(); 06752 foreach ($a as $key=>$value) { 06753 if (is_int($key)) { 06754 // we do not support numeric keys - sorry! 06755 continue; 06756 } 06757 $search[] = '{$a->'.$key.'}'; 06758 $replace[] = (string)$value; 06759 } 06760 if ($search) { 06761 $string = str_replace($search, $replace, $string); 06762 } 06763 } else { 06764 $string = str_replace('{$a}', (string)$a, $string); 06765 } 06766 } 06767 06768 return $string; 06769 } 06770 06778 public function get_list_of_countries($returnall = false, $lang = NULL) { 06779 //not used in installer 06780 return array(); 06781 } 06782 06791 public function get_list_of_languages($lang = NULL, $standard = 'iso6392') { 06792 //not used in installer 06793 return array(); 06794 } 06795 06803 public function translation_exists($lang, $includeall = true) { 06804 return file_exists($this->installroot.'/'.$lang.'/langconfig.php'); 06805 } 06806 06812 public function get_list_of_translations($returnall = false) { 06813 // return all is ignored here - we need to know all langs in installer 06814 $languages = array(); 06815 // Get raw list of lang directories 06816 $langdirs = get_list_of_plugins('install/lang'); 06817 asort($langdirs); 06818 // Get some info from each lang 06819 foreach ($langdirs as $lang) { 06820 if (file_exists($this->installroot.'/'.$lang.'/langconfig.php')) { 06821 $string = array(); 06822 include($this->installroot.'/'.$lang.'/langconfig.php'); 06823 if (!empty($string['thislanguage'])) { 06824 $languages[$lang] = $string['thislanguage'].' ('.$lang.')'; 06825 } 06826 } 06827 } 06828 // Return array 06829 return $languages; 06830 } 06831 06838 public function get_list_of_currencies($lang = NULL) { 06839 // not used in installer 06840 return array(); 06841 } 06842 06846 public function reset_caches() {} 06847 } 06848 06849 06904 function get_string($identifier, $component = '', $a = NULL) { 06905 global $CFG; 06906 06907 $identifier = clean_param($identifier, PARAM_STRINGID); 06908 if (empty($identifier)) { 06909 throw new coding_exception('Invalid string identifier. Most probably some illegal character is part of the string identifier. Please fix your get_string() call and string definition'); 06910 } 06911 06912 if (func_num_args() > 3) { 06913 debugging('extralocations parameter in get_string() is not supported any more, please use standard lang locations only.'); 06914 } 06915 06916 if (strpos($component, '/') !== false) { 06917 debugging('The module name you passed to get_string is the deprecated format ' . 06918 'like mod/mymod or block/myblock. The correct form looks like mymod, or block_myblock.' , DEBUG_DEVELOPER); 06919 $componentpath = explode('/', $component); 06920 06921 switch ($componentpath[0]) { 06922 case 'mod': 06923 $component = $componentpath[1]; 06924 break; 06925 case 'blocks': 06926 case 'block': 06927 $component = 'block_'.$componentpath[1]; 06928 break; 06929 case 'enrol': 06930 $component = 'enrol_'.$componentpath[1]; 06931 break; 06932 case 'format': 06933 $component = 'format_'.$componentpath[1]; 06934 break; 06935 case 'grade': 06936 $component = 'grade'.$componentpath[1].'_'.$componentpath[2]; 06937 break; 06938 } 06939 } 06940 06941 $result = get_string_manager()->get_string($identifier, $component, $a); 06942 06943 // Debugging feature lets you display string identifier and component 06944 if (isset($CFG->debugstringids) && $CFG->debugstringids && optional_param('strings', 0, PARAM_INT)) { 06945 $result .= ' {' . $identifier . '/' . $component . '}'; 06946 } 06947 return $result; 06948 } 06949 06957 function get_strings($array, $component = '') { 06958 $string = new stdClass; 06959 foreach ($array as $item) { 06960 $string->$item = get_string($item, $component); 06961 } 06962 return $string; 06963 } 06964 06988 function print_string($identifier, $component = '', $a = NULL) { 06989 echo get_string($identifier, $component, $a); 06990 } 06991 07000 function get_list_of_charsets() { 07001 07002 $charsets = array( 07003 'EUC-JP' => 'EUC-JP', 07004 'ISO-2022-JP'=> 'ISO-2022-JP', 07005 'ISO-8859-1' => 'ISO-8859-1', 07006 'SHIFT-JIS' => 'SHIFT-JIS', 07007 'GB2312' => 'GB2312', 07008 'GB18030' => 'GB18030', // gb18030 not supported by typo and mbstring 07009 'UTF-8' => 'UTF-8'); 07010 07011 asort($charsets); 07012 07013 return $charsets; 07014 } 07015 07021 function get_list_of_themes() { 07022 global $CFG; 07023 07024 $themes = array(); 07025 07026 if (!empty($CFG->themelist)) { // use admin's list of themes 07027 $themelist = explode(',', $CFG->themelist); 07028 } else { 07029 $themelist = array_keys(get_plugin_list("theme")); 07030 } 07031 07032 foreach ($themelist as $key => $themename) { 07033 $theme = theme_config::load($themename); 07034 $themes[$themename] = $theme; 07035 } 07036 07037 collatorlib::asort_objects_by_method($themes, 'get_theme_name'); 07038 07039 return $themes; 07040 } 07041 07049 function get_list_of_timezones() { 07050 global $CFG, $DB; 07051 07052 static $timezones; 07053 07054 if (!empty($timezones)) { // This function has been called recently 07055 return $timezones; 07056 } 07057 07058 $timezones = array(); 07059 07060 if ($rawtimezones = $DB->get_records_sql("SELECT MAX(id), name FROM {timezone} GROUP BY name")) { 07061 foreach($rawtimezones as $timezone) { 07062 if (!empty($timezone->name)) { 07063 if (get_string_manager()->string_exists(strtolower($timezone->name), 'timezones')) { 07064 $timezones[$timezone->name] = get_string(strtolower($timezone->name), 'timezones'); 07065 } else { 07066 $timezones[$timezone->name] = $timezone->name; 07067 } 07068 if (substr($timezones[$timezone->name], 0, 1) == '[') { // No translation found 07069 $timezones[$timezone->name] = $timezone->name; 07070 } 07071 } 07072 } 07073 } 07074 07075 asort($timezones); 07076 07077 for ($i = -13; $i <= 13; $i += .5) { 07078 $tzstring = 'UTC'; 07079 if ($i < 0) { 07080 $timezones[sprintf("%.1f", $i)] = $tzstring . $i; 07081 } else if ($i > 0) { 07082 $timezones[sprintf("%.1f", $i)] = $tzstring . '+' . $i; 07083 } else { 07084 $timezones[sprintf("%.1f", $i)] = $tzstring; 07085 } 07086 } 07087 07088 return $timezones; 07089 } 07090 07096 function get_emoticon_manager() { 07097 static $singleton = null; 07098 07099 if (is_null($singleton)) { 07100 $singleton = new emoticon_manager(); 07101 } 07102 07103 return $singleton; 07104 } 07105 07116 class emoticon_manager { 07117 07123 public function get_emoticons() { 07124 global $CFG; 07125 07126 if (empty($CFG->emoticons)) { 07127 return array(); 07128 } 07129 07130 $emoticons = $this->decode_stored_config($CFG->emoticons); 07131 07132 if (!is_array($emoticons)) { 07133 // something is wrong with the format of stored setting 07134 debugging('Invalid format of emoticons setting, please resave the emoticons settings form', DEBUG_NORMAL); 07135 return array(); 07136 } 07137 07138 return $emoticons; 07139 } 07140 07148 public function prepare_renderable_emoticon(stdClass $emoticon, array $attributes = array()) { 07149 $stringmanager = get_string_manager(); 07150 if ($stringmanager->string_exists($emoticon->altidentifier, $emoticon->altcomponent)) { 07151 $alt = get_string($emoticon->altidentifier, $emoticon->altcomponent); 07152 } else { 07153 $alt = s($emoticon->text); 07154 } 07155 return new pix_emoticon($emoticon->imagename, $alt, $emoticon->imagecomponent, $attributes); 07156 } 07157 07165 public function encode_stored_config(array $emoticons) { 07166 return json_encode($emoticons); 07167 } 07168 07176 public function decode_stored_config($encoded) { 07177 $decoded = json_decode($encoded); 07178 if (!is_array($decoded)) { 07179 return null; 07180 } 07181 return $decoded; 07182 } 07183 07189 public function default_emoticons() { 07190 return array( 07191 $this->prepare_emoticon_object(":-)", 's/smiley', 'smiley'), 07192 $this->prepare_emoticon_object(":)", 's/smiley', 'smiley'), 07193 $this->prepare_emoticon_object(":-D", 's/biggrin', 'biggrin'), 07194 $this->prepare_emoticon_object(";-)", 's/wink', 'wink'), 07195 $this->prepare_emoticon_object(":-/", 's/mixed', 'mixed'), 07196 $this->prepare_emoticon_object("V-.", 's/thoughtful', 'thoughtful'), 07197 $this->prepare_emoticon_object(":-P", 's/tongueout', 'tongueout'), 07198 $this->prepare_emoticon_object(":-p", 's/tongueout', 'tongueout'), 07199 $this->prepare_emoticon_object("B-)", 's/cool', 'cool'), 07200 $this->prepare_emoticon_object("^-)", 's/approve', 'approve'), 07201 $this->prepare_emoticon_object("8-)", 's/wideeyes', 'wideeyes'), 07202 $this->prepare_emoticon_object(":o)", 's/clown', 'clown'), 07203 $this->prepare_emoticon_object(":-(", 's/sad', 'sad'), 07204 $this->prepare_emoticon_object(":(", 's/sad', 'sad'), 07205 $this->prepare_emoticon_object("8-.", 's/shy', 'shy'), 07206 $this->prepare_emoticon_object(":-I", 's/blush', 'blush'), 07207 $this->prepare_emoticon_object(":-X", 's/kiss', 'kiss'), 07208 $this->prepare_emoticon_object("8-o", 's/surprise', 'surprise'), 07209 $this->prepare_emoticon_object("P-|", 's/blackeye', 'blackeye'), 07210 $this->prepare_emoticon_object("8-[", 's/angry', 'angry'), 07211 $this->prepare_emoticon_object("(grr)", 's/angry', 'angry'), 07212 $this->prepare_emoticon_object("xx-P", 's/dead', 'dead'), 07213 $this->prepare_emoticon_object("|-.", 's/sleepy', 'sleepy'), 07214 $this->prepare_emoticon_object("}-]", 's/evil', 'evil'), 07215 $this->prepare_emoticon_object("(h)", 's/heart', 'heart'), 07216 $this->prepare_emoticon_object("(heart)", 's/heart', 'heart'), 07217 $this->prepare_emoticon_object("(y)", 's/yes', 'yes', 'core'), 07218 $this->prepare_emoticon_object("(n)", 's/no', 'no', 'core'), 07219 $this->prepare_emoticon_object("(martin)", 's/martin', 'martin'), 07220 $this->prepare_emoticon_object("( )", 's/egg', 'egg'), 07221 ); 07222 } 07223 07234 protected function prepare_emoticon_object($text, $imagename, $altidentifier = null, $altcomponent = 'core_pix', $imagecomponent = 'core') { 07235 return (object)array( 07236 'text' => $text, 07237 'imagename' => $imagename, 07238 'imagecomponent' => $imagecomponent, 07239 'altidentifier' => $altidentifier, 07240 'altcomponent' => $altcomponent, 07241 ); 07242 } 07243 } 07244 07246 07255 function rc4encrypt($data) { 07256 $password = get_site_identifier(); 07257 return endecrypt($password, $data, ''); 07258 } 07259 07268 function rc4decrypt($data) { 07269 $password = get_site_identifier(); 07270 return endecrypt($password, $data, 'de'); 07271 } 07272 07283 function endecrypt ($pwd, $data, $case) { 07284 07285 if ($case == 'de') { 07286 $data = urldecode($data); 07287 } 07288 07289 $key[] = ''; 07290 $box[] = ''; 07291 $temp_swap = ''; 07292 $pwd_length = 0; 07293 07294 $pwd_length = strlen($pwd); 07295 07296 for ($i = 0; $i <= 255; $i++) { 07297 $key[$i] = ord(substr($pwd, ($i % $pwd_length), 1)); 07298 $box[$i] = $i; 07299 } 07300 07301 $x = 0; 07302 07303 for ($i = 0; $i <= 255; $i++) { 07304 $x = ($x + $box[$i] + $key[$i]) % 256; 07305 $temp_swap = $box[$i]; 07306 $box[$i] = $box[$x]; 07307 $box[$x] = $temp_swap; 07308 } 07309 07310 $temp = ''; 07311 $k = ''; 07312 07313 $cipherby = ''; 07314 $cipher = ''; 07315 07316 $a = 0; 07317 $j = 0; 07318 07319 for ($i = 0; $i < strlen($data); $i++) { 07320 $a = ($a + 1) % 256; 07321 $j = ($j + $box[$a]) % 256; 07322 $temp = $box[$a]; 07323 $box[$a] = $box[$j]; 07324 $box[$j] = $temp; 07325 $k = $box[(($box[$a] + $box[$j]) % 256)]; 07326 $cipherby = ord(substr($data, $i, 1)) ^ $k; 07327 $cipher .= chr($cipherby); 07328 } 07329 07330 if ($case == 'de') { 07331 $cipher = urldecode(urlencode($cipher)); 07332 } else { 07333 $cipher = urlencode($cipher); 07334 } 07335 07336 return $cipher; 07337 } 07338 07340 07348 function get_plugin_directory($plugintype, $name) { 07349 global $CFG; 07350 07351 if ($plugintype === '') { 07352 $plugintype = 'mod'; 07353 } 07354 07355 $types = get_plugin_types(true); 07356 if (!array_key_exists($plugintype, $types)) { 07357 return NULL; 07358 } 07359 $name = clean_param($name, PARAM_SAFEDIR); // just in case ;-) 07360 07361 if (!empty($CFG->themedir) and $plugintype === 'theme') { 07362 if (!is_dir($types['theme'] . '/' . $name)) { 07363 // ok, so the theme is supposed to be in the $CFG->themedir 07364 return $CFG->themedir . '/' . $name; 07365 } 07366 } 07367 07368 return $types[$plugintype].'/'.$name; 07369 } 07370 07378 function get_component_directory($component) { 07379 global $CFG; 07380 /* 07381 $simpletest = false; 07382 if (strpos($component, 'simpletest_') === 0) { 07383 $subdir = substr($component, strlen('simpletest_')); 07384 //TODO: this looks borked, where is it used actually? 07385 return $subdir; 07386 } 07387 */ 07388 list($type, $plugin) = normalize_component($component); 07389 07390 if ($type === 'core') { 07391 if ($plugin === NULL ) { 07392 $path = $CFG->libdir; 07393 } else { 07394 $subsystems = get_core_subsystems(); 07395 if (isset($subsystems[$plugin])) { 07396 $path = $CFG->dirroot.'/'.$subsystems[$plugin]; 07397 } else { 07398 $path = NULL; 07399 } 07400 } 07401 07402 } else { 07403 $path = get_plugin_directory($type, $plugin); 07404 } 07405 07406 return $path; 07407 } 07408 07414 function normalize_component($component) { 07415 if ($component === 'moodle' or $component === 'core') { 07416 $type = 'core'; 07417 $plugin = NULL; 07418 07419 } else if (strpos($component, '_') === false) { 07420 $subsystems = get_core_subsystems(); 07421 if (array_key_exists($component, $subsystems)) { 07422 $type = 'core'; 07423 $plugin = $component; 07424 } else { 07425 // everything else is a module 07426 $type = 'mod'; 07427 $plugin = $component; 07428 } 07429 07430 } else { 07431 list($type, $plugin) = explode('_', $component, 2); 07432 $plugintypes = get_plugin_types(false); 07433 if ($type !== 'core' and !array_key_exists($type, $plugintypes)) { 07434 $type = 'mod'; 07435 $plugin = $component; 07436 } 07437 } 07438 07439 return array($type, $plugin); 07440 } 07441 07456 function get_core_subsystems() { 07457 global $CFG; 07458 07459 static $info = null; 07460 07461 if (!$info) { 07462 $info = array( 07463 'access' => NULL, 07464 'admin' => $CFG->admin, 07465 'auth' => 'auth', 07466 'backup' => 'backup/util/ui', 07467 'block' => 'blocks', 07468 'blog' => 'blog', 07469 'bulkusers' => NULL, 07470 'calendar' => 'calendar', 07471 'cohort' => 'cohort', 07472 'condition' => NULL, 07473 'completion' => NULL, 07474 'countries' => NULL, 07475 'course' => 'course', 07476 'currencies' => NULL, 07477 'dbtransfer' => NULL, 07478 'debug' => NULL, 07479 'dock' => NULL, 07480 'editor' => 'lib/editor', 07481 'edufields' => NULL, 07482 'enrol' => 'enrol', 07483 'error' => NULL, 07484 'filepicker' => NULL, 07485 'files' => 'files', 07486 'filters' => NULL, 07487 'fonts' => NULL, 07488 'form' => 'lib/form', 07489 'grades' => 'grade', 07490 'grading' => 'grade/grading', 07491 'group' => 'group', 07492 'help' => NULL, 07493 'hub' => NULL, 07494 'imscc' => NULL, 07495 'install' => NULL, 07496 'iso6392' => NULL, 07497 'langconfig' => NULL, 07498 'license' => NULL, 07499 'mathslib' => NULL, 07500 'message' => 'message', 07501 'mimetypes' => NULL, 07502 'mnet' => 'mnet', 07503 'moodle.org' => NULL, // the dot is nasty, watch out! should be renamed to moodleorg 07504 'my' => 'my', 07505 'notes' => 'notes', 07506 'pagetype' => NULL, 07507 'pix' => NULL, 07508 'plagiarism' => 'plagiarism', 07509 'plugin' => NULL, 07510 'portfolio' => 'portfolio', 07511 'publish' => 'course/publish', 07512 'question' => 'question', 07513 'rating' => 'rating', 07514 'register' => 'admin/registration', //TODO: this is wrong, unfortunately we would need to modify hub code to pass around the correct url 07515 'repository' => 'repository', 07516 'rss' => 'rss', 07517 'role' => $CFG->admin.'/role', 07518 'search' => 'search', 07519 'table' => NULL, 07520 'tag' => 'tag', 07521 'timezones' => NULL, 07522 'user' => 'user', 07523 'userkey' => NULL, 07524 'webservice' => 'webservice', 07525 ); 07526 } 07527 07528 return $info; 07529 } 07530 07536 function get_plugin_types($fullpaths=true) { 07537 global $CFG; 07538 07539 static $info = null; 07540 static $fullinfo = null; 07541 07542 if (!$info) { 07543 $info = array('qtype' => 'question/type', 07544 'mod' => 'mod', 07545 'auth' => 'auth', 07546 'enrol' => 'enrol', 07547 'message' => 'message/output', 07548 'block' => 'blocks', 07549 'filter' => 'filter', 07550 'editor' => 'lib/editor', 07551 'format' => 'course/format', 07552 'profilefield' => 'user/profile/field', 07553 'report' => 'report', 07554 'coursereport' => 'course/report', // must be after system reports 07555 'gradeexport' => 'grade/export', 07556 'gradeimport' => 'grade/import', 07557 'gradereport' => 'grade/report', 07558 'gradingform' => 'grade/grading/form', 07559 'mnetservice' => 'mnet/service', 07560 'webservice' => 'webservice', 07561 'repository' => 'repository', 07562 'portfolio' => 'portfolio', 07563 'qbehaviour' => 'question/behaviour', 07564 'qformat' => 'question/format', 07565 'plagiarism' => 'plagiarism', 07566 'tool' => $CFG->admin.'/tool', 07567 'theme' => 'theme', // this is a bit hacky, themes may be in $CFG->themedir too 07568 ); 07569 07570 $mods = get_plugin_list('mod'); 07571 foreach ($mods as $mod => $moddir) { 07572 if (file_exists("$moddir/db/subplugins.php")) { 07573 $subplugins = array(); 07574 include("$moddir/db/subplugins.php"); 07575 foreach ($subplugins as $subtype=>$dir) { 07576 $info[$subtype] = $dir; 07577 } 07578 } 07579 } 07580 07581 // local is always last! 07582 $info['local'] = 'local'; 07583 07584 $fullinfo = array(); 07585 foreach ($info as $type => $dir) { 07586 $fullinfo[$type] = $CFG->dirroot.'/'.$dir; 07587 } 07588 } 07589 07590 return ($fullpaths ? $fullinfo : $info); 07591 } 07592 07598 function get_plugin_list($plugintype) { 07599 global $CFG; 07600 07601 $ignored = array('CVS', '_vti_cnf', 'simpletest', 'db', 'yui', 'phpunit'); 07602 if ($plugintype == 'auth') { 07603 // Historically we have had an auth plugin called 'db', so allow a special case. 07604 $key = array_search('db', $ignored); 07605 if ($key !== false) { 07606 unset($ignored[$key]); 07607 } 07608 } 07609 07610 if ($plugintype === '') { 07611 $plugintype = 'mod'; 07612 } 07613 07614 $fulldirs = array(); 07615 07616 if ($plugintype === 'mod') { 07617 // mod is an exception because we have to call this function from get_plugin_types() 07618 $fulldirs[] = $CFG->dirroot.'/mod'; 07619 07620 } else if ($plugintype === 'theme') { 07621 $fulldirs[] = $CFG->dirroot.'/theme'; 07622 // themes are special because they may be stored also in separate directory 07623 if (!empty($CFG->themedir) and file_exists($CFG->themedir) and is_dir($CFG->themedir) ) { 07624 $fulldirs[] = $CFG->themedir; 07625 } 07626 07627 } else { 07628 $types = get_plugin_types(true); 07629 if (!array_key_exists($plugintype, $types)) { 07630 return array(); 07631 } 07632 $fulldir = $types[$plugintype]; 07633 if (!file_exists($fulldir)) { 07634 return array(); 07635 } 07636 $fulldirs[] = $fulldir; 07637 } 07638 07639 $result = array(); 07640 07641 foreach ($fulldirs as $fulldir) { 07642 if (!is_dir($fulldir)) { 07643 continue; 07644 } 07645 $items = new DirectoryIterator($fulldir); 07646 foreach ($items as $item) { 07647 if ($item->isDot() or !$item->isDir()) { 07648 continue; 07649 } 07650 $pluginname = $item->getFilename(); 07651 if (in_array($pluginname, $ignored)) { 07652 continue; 07653 } 07654 $pluginname = clean_param($pluginname, PARAM_PLUGIN); 07655 if (empty($pluginname)) { 07656 // better ignore plugins with problematic names here 07657 continue; 07658 } 07659 $result[$pluginname] = $fulldir.'/'.$pluginname; 07660 unset($item); 07661 } 07662 unset($items); 07663 } 07664 07665 //TODO: implement better sorting once we migrated all plugin names to 'pluginname', ksort does not work for unicode, that is why we have to sort by the dir name, not the strings! 07666 ksort($result); 07667 return $result; 07668 } 07669 07679 function get_plugin_list_with_file($plugintype, $file, $include = false) { 07680 global $CFG; // Necessary in case it is referenced by include()d PHP scripts. 07681 07682 $plugins = array(); 07683 07684 foreach(get_plugin_list($plugintype) as $plugin => $dir) { 07685 $path = $dir . '/' . $file; 07686 if (file_exists($path)) { 07687 if ($include) { 07688 include_once($path); 07689 } 07690 $plugins[$plugin] = $path; 07691 } 07692 } 07693 07694 return $plugins; 07695 } 07696 07710 function get_plugin_list_with_function($plugintype, $function, $file = 'lib.php') { 07711 $pluginfunctions = array(); 07712 foreach (get_plugin_list_with_file($plugintype, $file, true) as $plugin => $notused) { 07713 $fullfunction = $plugintype . '_' . $plugin . '_' . $function; 07714 07715 if (function_exists($fullfunction)) { 07716 // Function exists with standard name. Store, indexed by 07717 // frankenstyle name of plugin 07718 $pluginfunctions[$plugintype . '_' . $plugin] = $fullfunction; 07719 07720 } else if ($plugintype === 'mod') { 07721 // For modules, we also allow plugin without full frankenstyle 07722 // but just starting with the module name 07723 $shortfunction = $plugin . '_' . $function; 07724 if (function_exists($shortfunction)) { 07725 $pluginfunctions[$plugintype . '_' . $plugin] = $shortfunction; 07726 } 07727 } 07728 } 07729 return $pluginfunctions; 07730 } 07731 07745 function get_plugin_list_with_class($plugintype, $class, $file) { 07746 if ($class) { 07747 $suffix = '_' . $class; 07748 } else { 07749 $suffix = ''; 07750 } 07751 07752 $pluginclasses = array(); 07753 foreach (get_plugin_list_with_file($plugintype, $file, true) as $plugin => $notused) { 07754 $classname = $plugintype . '_' . $plugin . $suffix; 07755 if (class_exists($classname)) { 07756 $pluginclasses[$plugintype . '_' . $plugin] = $classname; 07757 } 07758 } 07759 07760 return $pluginclasses; 07761 } 07762 07776 function get_list_of_plugins($directory='mod', $exclude='', $basedir='') { 07777 global $CFG; 07778 07779 $plugins = array(); 07780 07781 if (empty($basedir)) { 07782 $basedir = $CFG->dirroot .'/'. $directory; 07783 07784 } else { 07785 $basedir = $basedir .'/'. $directory; 07786 } 07787 07788 if (file_exists($basedir) && filetype($basedir) == 'dir') { 07789 $dirhandle = opendir($basedir); 07790 while (false !== ($dir = readdir($dirhandle))) { 07791 $firstchar = substr($dir, 0, 1); 07792 if ($firstchar === '.' or $dir === 'CVS' or $dir === '_vti_cnf' or $dir === 'simpletest' or $dir === 'yui' or $dir === 'phpunit' or $dir === $exclude) { 07793 continue; 07794 } 07795 if (filetype($basedir .'/'. $dir) != 'dir') { 07796 continue; 07797 } 07798 $plugins[] = $dir; 07799 } 07800 closedir($dirhandle); 07801 } 07802 if ($plugins) { 07803 asort($plugins); 07804 } 07805 return $plugins; 07806 } 07807 07821 function plugin_callback($type, $name, $feature, $action, $params = null, $default = null) { 07822 return component_callback($type . '_' . $name, $feature . '_' . $action, (array) $params, $default); 07823 } 07824 07834 function component_callback($component, $function, array $params = array(), $default = null) { 07835 global $CFG; // this is needed for require_once() below 07836 07837 $cleancomponent = clean_param($component, PARAM_COMPONENT); 07838 if (empty($cleancomponent)) { 07839 throw new coding_exception('Invalid component used in plugin/component_callback():' . $component); 07840 } 07841 $component = $cleancomponent; 07842 07843 list($type, $name) = normalize_component($component); 07844 $component = $type . '_' . $name; 07845 07846 $oldfunction = $name.'_'.$function; 07847 $function = $component.'_'.$function; 07848 07849 $dir = get_component_directory($component); 07850 if (empty($dir)) { 07851 throw new coding_exception('Invalid component used in plugin/component_callback():' . $component); 07852 } 07853 07854 // Load library and look for function 07855 if (file_exists($dir.'/lib.php')) { 07856 require_once($dir.'/lib.php'); 07857 } 07858 07859 if (!function_exists($function) and function_exists($oldfunction)) { 07860 if ($type !== 'mod' and $type !== 'core') { 07861 debugging("Please use new function name $function instead of legacy $oldfunction"); 07862 } 07863 $function = $oldfunction; 07864 } 07865 07866 if (function_exists($function)) { 07867 // Function exists, so just return function result 07868 $ret = call_user_func_array($function, $params); 07869 if (is_null($ret)) { 07870 return $default; 07871 } else { 07872 return $ret; 07873 } 07874 } 07875 return $default; 07876 } 07877 07888 function plugin_supports($type, $name, $feature, $default = NULL) { 07889 global $CFG; 07890 07891 if ($type === 'mod' and $name === 'NEWMODULE') { 07892 //somebody forgot to rename the module template 07893 return false; 07894 } 07895 07896 $component = clean_param($type . '_' . $name, PARAM_COMPONENT); 07897 if (empty($component)) { 07898 throw new coding_exception('Invalid component used in plugin_supports():' . $type . '_' . $name); 07899 } 07900 07901 $function = null; 07902 07903 if ($type === 'mod') { 07904 // we need this special case because we support subplugins in modules, 07905 // otherwise it would end up in infinite loop 07906 if (file_exists("$CFG->dirroot/mod/$name/lib.php")) { 07907 include_once("$CFG->dirroot/mod/$name/lib.php"); 07908 $function = $component.'_supports'; 07909 if (!function_exists($function)) { 07910 // legacy non-frankenstyle function name 07911 $function = $name.'_supports'; 07912 } 07913 } else { 07914 // invalid module 07915 } 07916 07917 } else { 07918 if (!$path = get_plugin_directory($type, $name)) { 07919 // non existent plugin type 07920 return false; 07921 } 07922 if (file_exists("$path/lib.php")) { 07923 include_once("$path/lib.php"); 07924 $function = $component.'_supports'; 07925 } 07926 } 07927 07928 if ($function and function_exists($function)) { 07929 $supports = $function($feature); 07930 if (is_null($supports)) { 07931 // plugin does not know - use default 07932 return $default; 07933 } else { 07934 return $supports; 07935 } 07936 } 07937 07938 //plugin does not care, so use default 07939 return $default; 07940 } 07941 07950 function check_php_version($version='5.2.4') { 07951 return (version_compare(phpversion(), $version) >= 0); 07952 } 07953 07964 function check_browser_operating_system($brand) { 07965 if (empty($_SERVER['HTTP_USER_AGENT'])) { 07966 return false; 07967 } 07968 07969 if (preg_match("/$brand/i", $_SERVER['HTTP_USER_AGENT'])) { 07970 return true; 07971 } 07972 07973 return false; 07974 } 07975 07985 function check_browser_version($brand, $version = null) { 07986 if (empty($_SERVER['HTTP_USER_AGENT'])) { 07987 return false; 07988 } 07989 07990 $agent = $_SERVER['HTTP_USER_AGENT']; 07991 07992 switch ($brand) { 07993 07994 case 'Camino': 07995 if (strpos($agent, 'Camino') === false) { 07996 return false; 07997 } 07998 if (empty($version)) { 07999 return true; // no version specified 08000 } 08001 if (preg_match("/Camino\/([0-9\.]+)/i", $agent, $match)) { 08002 if (version_compare($match[1], $version) >= 0) { 08003 return true; 08004 } 08005 } 08006 break; 08007 08008 08009 case 'Firefox': 08010 if (strpos($agent, 'Iceweasel') === false and strpos($agent, 'Firefox') === false) { 08011 return false; 08012 } 08013 if (empty($version)) { 08014 return true; // no version specified 08015 } 08016 if (preg_match("/(Iceweasel|Firefox)\/([0-9\.]+)/i", $agent, $match)) { 08017 if (version_compare($match[2], $version) >= 0) { 08018 return true; 08019 } 08020 } 08021 break; 08022 08023 08024 case 'Gecko': 08025 if (empty($version) and substr_count($agent, 'Camino')) { 08026 // MacOS X Camino support 08027 $version = 20041110; 08028 } 08029 08030 // the proper string - Gecko/CCYYMMDD Vendor/Version 08031 // Faster version and work-a-round No IDN problem. 08032 if (preg_match("/Gecko\/([0-9]+)/i", $agent, $match)) { 08033 if ($match[1] > $version) { 08034 return true; 08035 } 08036 } 08037 break; 08038 08039 08040 case 'MSIE': 08041 if (strpos($agent, 'Opera') !== false) { // Reject Opera 08042 return false; 08043 } 08044 // in case of IE we have to deal with BC of the version parameter 08045 if (is_null($version)) { 08046 $version = 5.5; // anything older is not considered a browser at all! 08047 } 08048 08049 //see: http://www.useragentstring.com/pages/Internet%20Explorer/ 08050 if (preg_match("/MSIE ([0-9\.]+)/", $agent, $match)) { 08051 if (version_compare($match[1], $version) >= 0) { 08052 return true; 08053 } 08054 } 08055 break; 08056 08057 08058 case 'Opera': 08059 if (strpos($agent, 'Opera') === false) { 08060 return false; 08061 } 08062 if (empty($version)) { 08063 return true; // no version specified 08064 } 08065 if (preg_match("/Opera\/([0-9\.]+)/i", $agent, $match)) { 08066 if (version_compare($match[1], $version) >= 0) { 08067 return true; 08068 } 08069 } 08070 break; 08071 08072 08073 case 'WebKit': 08074 if (strpos($agent, 'AppleWebKit') === false) { 08075 return false; 08076 } 08077 if (empty($version)) { 08078 return true; // no version specified 08079 } 08080 if (preg_match("/AppleWebKit\/([0-9]+)/i", $agent, $match)) { 08081 if (version_compare($match[1], $version) >= 0) { 08082 return true; 08083 } 08084 } 08085 break; 08086 08087 08088 case 'Safari': 08089 if (strpos($agent, 'AppleWebKit') === false) { 08090 return false; 08091 } 08092 // Look for AppleWebKit, excluding strings with OmniWeb, Shiira and SymbianOS and any other mobile devices 08093 if (strpos($agent, 'OmniWeb')) { // Reject OmniWeb 08094 return false; 08095 } 08096 if (strpos($agent, 'Shiira')) { // Reject Shiira 08097 return false; 08098 } 08099 if (strpos($agent, 'SymbianOS')) { // Reject SymbianOS 08100 return false; 08101 } 08102 if (strpos($agent, 'Android')) { // Reject Androids too 08103 return false; 08104 } 08105 if (strpos($agent, 'iPhone') or strpos($agent, 'iPad') or strpos($agent, 'iPod')) { 08106 // No Apple mobile devices here - editor does not work, course ajax is not touch compatible, etc. 08107 return false; 08108 } 08109 if (strpos($agent, 'Chrome')) { // Reject chrome browsers - it needs to be tested explicitly 08110 return false; 08111 } 08112 08113 if (empty($version)) { 08114 return true; // no version specified 08115 } 08116 if (preg_match("/AppleWebKit\/([0-9]+)/i", $agent, $match)) { 08117 if (version_compare($match[1], $version) >= 0) { 08118 return true; 08119 } 08120 } 08121 break; 08122 08123 08124 case 'Chrome': 08125 if (strpos($agent, 'Chrome') === false) { 08126 return false; 08127 } 08128 if (empty($version)) { 08129 return true; // no version specified 08130 } 08131 if (preg_match("/Chrome\/(.*)[ ]+/i", $agent, $match)) { 08132 if (version_compare($match[1], $version) >= 0) { 08133 return true; 08134 } 08135 } 08136 break; 08137 08138 08139 case 'Safari iOS': 08140 if (strpos($agent, 'AppleWebKit') === false or strpos($agent, 'Safari') === false) { 08141 return false; 08142 } 08143 if (!strpos($agent, 'iPhone') and !strpos($agent, 'iPad') and !strpos($agent, 'iPod')) { 08144 return false; 08145 } 08146 if (empty($version)) { 08147 return true; // no version specified 08148 } 08149 if (preg_match("/AppleWebKit\/([0-9]+)/i", $agent, $match)) { 08150 if (version_compare($match[1], $version) >= 0) { 08151 return true; 08152 } 08153 } 08154 break; 08155 08156 08157 case 'WebKit Android': 08158 if (strpos($agent, 'Linux; U; Android') === false) { 08159 return false; 08160 } 08161 if (empty($version)) { 08162 return true; // no version specified 08163 } 08164 if (preg_match("/AppleWebKit\/([0-9]+)/i", $agent, $match)) { 08165 if (version_compare($match[1], $version) >= 0) { 08166 return true; 08167 } 08168 } 08169 break; 08170 08171 } 08172 08173 return false; 08174 } 08175 08183 function get_device_type() { 08184 global $CFG; 08185 08186 if (empty($CFG->enabledevicedetection) || empty($_SERVER['HTTP_USER_AGENT'])) { 08187 return 'default'; 08188 } 08189 08190 $useragent = $_SERVER['HTTP_USER_AGENT']; 08191 08192 if (!empty($CFG->devicedetectregex)) { 08193 $regexes = json_decode($CFG->devicedetectregex); 08194 08195 foreach ($regexes as $value=>$regex) { 08196 if (preg_match($regex, $useragent)) { 08197 return $value; 08198 } 08199 } 08200 } 08201 08202 //mobile detection PHP direct copy from open source detectmobilebrowser.com 08203 $phonesregex = '/android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i'; 08204 $modelsregex = '/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i'; 08205 if (preg_match($phonesregex,$useragent) || preg_match($modelsregex,substr($useragent, 0, 4))){ 08206 return 'mobile'; 08207 } 08208 08209 $tabletregex = '/Tablet browser|iPad|iProd|GT-P1000|GT-I9000|SHW-M180S|SGH-T849|SCH-I800|Build\/ERE27|sholest/i'; 08210 if (preg_match($tabletregex, $useragent)) { 08211 return 'tablet'; 08212 } 08213 08214 // Safe way to check for IE6 and not get false positives for some IE 7/8 users 08215 if (substr($_SERVER['HTTP_USER_AGENT'], 0, 34) === 'Mozilla/4.0 (compatible; MSIE 6.0;') { 08216 return 'legacy'; 08217 } 08218 08219 return 'default'; 08220 } 08221 08228 function get_device_type_list($incusertypes = true) { 08229 global $CFG; 08230 08231 $types = array('default', 'legacy', 'mobile', 'tablet'); 08232 08233 if ($incusertypes && !empty($CFG->devicedetectregex)) { 08234 $regexes = json_decode($CFG->devicedetectregex); 08235 08236 foreach ($regexes as $value => $regex) { 08237 $types[] = $value; 08238 } 08239 } 08240 08241 return $types; 08242 } 08243 08250 function get_selected_theme_for_device_type($devicetype = null) { 08251 global $CFG; 08252 08253 if (empty($devicetype)) { 08254 $devicetype = get_user_device_type(); 08255 } 08256 08257 $themevarname = get_device_cfg_var_name($devicetype); 08258 if (empty($CFG->$themevarname)) { 08259 return false; 08260 } 08261 08262 return $CFG->$themevarname; 08263 } 08264 08271 function get_device_cfg_var_name($devicetype = null) { 08272 if ($devicetype == 'default' || empty($devicetype)) { 08273 return 'theme'; 08274 } 08275 08276 return 'theme' . $devicetype; 08277 } 08278 08286 function set_user_device_type($newdevice) { 08287 global $USER; 08288 08289 $devicetype = get_device_type(); 08290 $devicetypes = get_device_type_list(); 08291 08292 if ($newdevice == $devicetype) { 08293 unset_user_preference('switchdevice'.$devicetype); 08294 } else if (in_array($newdevice, $devicetypes)) { 08295 set_user_preference('switchdevice'.$devicetype, $newdevice); 08296 } 08297 } 08298 08305 function get_user_device_type() { 08306 $device = get_device_type(); 08307 $switched = get_user_preferences('switchdevice'.$device, false); 08308 if ($switched != false) { 08309 return $switched; 08310 } 08311 return $device; 08312 } 08313 08320 function get_browser_version_classes() { 08321 $classes = array(); 08322 08323 if (check_browser_version("MSIE", "0")) { 08324 $classes[] = 'ie'; 08325 if (check_browser_version("MSIE", 9)) { 08326 $classes[] = 'ie9'; 08327 } else if (check_browser_version("MSIE", 8)) { 08328 $classes[] = 'ie8'; 08329 } elseif (check_browser_version("MSIE", 7)) { 08330 $classes[] = 'ie7'; 08331 } elseif (check_browser_version("MSIE", 6)) { 08332 $classes[] = 'ie6'; 08333 } 08334 08335 } else if (check_browser_version("Firefox") || check_browser_version("Gecko") || check_browser_version("Camino")) { 08336 $classes[] = 'gecko'; 08337 if (preg_match('/rv\:([1-2])\.([0-9])/', $_SERVER['HTTP_USER_AGENT'], $matches)) { 08338 $classes[] = "gecko{$matches[1]}{$matches[2]}"; 08339 } 08340 08341 } else if (check_browser_version("WebKit")) { 08342 $classes[] = 'safari'; 08343 if (check_browser_version("Safari iOS")) { 08344 $classes[] = 'ios'; 08345 08346 } else if (check_browser_version("WebKit Android")) { 08347 $classes[] = 'android'; 08348 } 08349 08350 } else if (check_browser_version("Opera")) { 08351 $classes[] = 'opera'; 08352 08353 } 08354 08355 return $classes; 08356 } 08357 08363 function can_use_rotated_text() { 08364 global $USER; 08365 return ajaxenabled(array('Firefox' => 2.0)) && !$USER->screenreader;; 08366 } 08367 08373 function check_gd_version() { 08374 $gdversion = 0; 08375 08376 if (function_exists('gd_info')){ 08377 $gd_info = gd_info(); 08378 if (substr_count($gd_info['GD Version'], '2.')) { 08379 $gdversion = 2; 08380 } else if (substr_count($gd_info['GD Version'], '1.')) { 08381 $gdversion = 1; 08382 } 08383 08384 } else { 08385 ob_start(); 08386 phpinfo(INFO_MODULES); 08387 $phpinfo = ob_get_contents(); 08388 ob_end_clean(); 08389 08390 $phpinfo = explode("\n", $phpinfo); 08391 08392 08393 foreach ($phpinfo as $text) { 08394 $parts = explode('</td>', $text); 08395 foreach ($parts as $key => $val) { 08396 $parts[$key] = trim(strip_tags($val)); 08397 } 08398 if ($parts[0] == 'GD Version') { 08399 if (substr_count($parts[1], '2.0')) { 08400 $parts[1] = '2.0'; 08401 } 08402 $gdversion = intval($parts[1]); 08403 } 08404 } 08405 } 08406 08407 return $gdversion; // 1, 2 or 0 08408 } 08409 08420 function moodle_needs_upgrading() { 08421 global $CFG, $DB, $OUTPUT; 08422 08423 if (empty($CFG->version)) { 08424 return true; 08425 } 08426 08427 // main versio nfirst 08428 $version = null; 08429 include($CFG->dirroot.'/version.php'); // defines $version and upgrades 08430 if ($version > $CFG->version) { 08431 return true; 08432 } 08433 08434 // modules 08435 $mods = get_plugin_list('mod'); 08436 $installed = $DB->get_records('modules', array(), '', 'name, version'); 08437 foreach ($mods as $mod => $fullmod) { 08438 if ($mod === 'NEWMODULE') { // Someone has unzipped the template, ignore it 08439 continue; 08440 } 08441 $module = new stdClass(); 08442 if (!is_readable($fullmod.'/version.php')) { 08443 continue; 08444 } 08445 include($fullmod.'/version.php'); // defines $module with version etc 08446 if (empty($installed[$mod])) { 08447 return true; 08448 } else if ($module->version > $installed[$mod]->version) { 08449 return true; 08450 } 08451 } 08452 unset($installed); 08453 08454 // blocks 08455 $blocks = get_plugin_list('block'); 08456 $installed = $DB->get_records('block', array(), '', 'name, version'); 08457 require_once($CFG->dirroot.'/blocks/moodleblock.class.php'); 08458 foreach ($blocks as $blockname=>$fullblock) { 08459 if ($blockname === 'NEWBLOCK') { // Someone has unzipped the template, ignore it 08460 continue; 08461 } 08462 if (!is_readable($fullblock.'/version.php')) { 08463 continue; 08464 } 08465 $plugin = new stdClass(); 08466 $plugin->version = NULL; 08467 include($fullblock.'/version.php'); 08468 if (empty($installed[$blockname])) { 08469 return true; 08470 } else if ($plugin->version > $installed[$blockname]->version) { 08471 return true; 08472 } 08473 } 08474 unset($installed); 08475 08476 // now the rest of plugins 08477 $plugintypes = get_plugin_types(); 08478 unset($plugintypes['mod']); 08479 unset($plugintypes['block']); 08480 foreach ($plugintypes as $type=>$unused) { 08481 $plugs = get_plugin_list($type); 08482 foreach ($plugs as $plug=>$fullplug) { 08483 $component = $type.'_'.$plug; 08484 if (!is_readable($fullplug.'/version.php')) { 08485 continue; 08486 } 08487 $plugin = new stdClass(); 08488 include($fullplug.'/version.php'); // defines $plugin with version etc 08489 $installedversion = get_config($component, 'version'); 08490 if (empty($installedversion)) { // new installation 08491 return true; 08492 } else if ($installedversion < $plugin->version) { // upgrade 08493 return true; 08494 } 08495 } 08496 } 08497 08498 return false; 08499 } 08500 08510 function upgrade_set_timeout($max_execution_time=300) { 08511 global $CFG; 08512 08513 if (!isset($CFG->upgraderunning) or $CFG->upgraderunning < time()) { 08514 $upgraderunning = get_config(null, 'upgraderunning'); 08515 } else { 08516 $upgraderunning = $CFG->upgraderunning; 08517 } 08518 08519 if (!$upgraderunning) { 08520 // upgrade not running or aborted 08521 print_error('upgradetimedout', 'admin', "$CFG->wwwroot/$CFG->admin/"); 08522 die; 08523 } 08524 08525 if ($max_execution_time < 60) { 08526 // protection against 0 here 08527 $max_execution_time = 60; 08528 } 08529 08530 $expected_end = time() + $max_execution_time; 08531 08532 if ($expected_end < $upgraderunning + 10 and $expected_end > $upgraderunning - 10) { 08533 // no need to store new end, it is nearly the same ;-) 08534 return; 08535 } 08536 08537 set_time_limit($max_execution_time); 08538 set_config('upgraderunning', $expected_end); // keep upgrade locked until this time 08539 } 08540 08542 08551 function moodle_setlocale($locale='') { 08552 global $CFG; 08553 08554 static $currentlocale = ''; // last locale caching 08555 08556 $oldlocale = $currentlocale; 08557 08559 if ($CFG->ostype == 'WINDOWS') { 08560 $stringtofetch = 'localewin'; 08561 } else { 08562 $stringtofetch = 'locale'; 08563 } 08564 08566 if (!empty($locale)) { 08567 $currentlocale = $locale; 08568 } else if (!empty($CFG->locale)) { // override locale for all language packs 08569 $currentlocale = $CFG->locale; 08570 } else { 08571 $currentlocale = get_string($stringtofetch, 'langconfig'); 08572 } 08573 08575 if ($oldlocale == $currentlocale) { 08576 return; 08577 } 08578 08582 08584 $monetary= setlocale (LC_MONETARY, 0); 08585 $numeric = setlocale (LC_NUMERIC, 0); 08586 $ctype = setlocale (LC_CTYPE, 0); 08587 if ($CFG->ostype != 'WINDOWS') { 08588 $messages= setlocale (LC_MESSAGES, 0); 08589 } 08591 setlocale (LC_ALL, $currentlocale); 08593 setlocale (LC_MONETARY, $monetary); 08594 setlocale (LC_NUMERIC, $numeric); 08595 if ($CFG->ostype != 'WINDOWS') { 08596 setlocale (LC_MESSAGES, $messages); 08597 } 08598 if ($currentlocale == 'tr_TR' or $currentlocale == 'tr_TR.UTF-8') { // To workaround a well-known PHP problem with Turkish letter Ii 08599 setlocale (LC_CTYPE, $ctype); 08600 } 08601 } 08602 08613 function moodle_strtolower ($string, $encoding='') { 08614 08615 //If not specified use utf8 08616 if (empty($encoding)) { 08617 $encoding = 'UTF-8'; 08618 } 08619 //Use text services 08620 $textlib = textlib_get_instance(); 08621 08622 return $textlib->strtolower($string, $encoding); 08623 } 08624 08633 function count_words($string) { 08634 $string = strip_tags($string); 08635 return count(preg_split("/\w\b/", $string)) - 1; 08636 } 08637 08645 function count_letters($string) { 08647 $textlib = textlib_get_instance(); 08648 08649 $string = strip_tags($string); // Tags are out now 08650 $string = preg_replace('/[[:space:]]*/','',$string); //Whitespace are out now 08651 08652 return $textlib->strlen($string); 08653 } 08654 08661 function random_string ($length=15) { 08662 $pool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 08663 $pool .= 'abcdefghijklmnopqrstuvwxyz'; 08664 $pool .= '0123456789'; 08665 $poollen = strlen($pool); 08666 mt_srand ((double) microtime() * 1000000); 08667 $string = ''; 08668 for ($i = 0; $i < $length; $i++) { 08669 $string .= substr($pool, (mt_rand()%($poollen)), 1); 08670 } 08671 return $string; 08672 } 08673 08683 function complex_random_string($length=null) { 08684 $pool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 08685 $pool .= '`~!@#%^&*()_+-=[];,./<>?:{} '; 08686 $poollen = strlen($pool); 08687 mt_srand ((double) microtime() * 1000000); 08688 if ($length===null) { 08689 $length = floor(rand(24,32)); 08690 } 08691 $string = ''; 08692 for ($i = 0; $i < $length; $i++) { 08693 $string .= $pool[(mt_rand()%$poollen)]; 08694 } 08695 return $string; 08696 } 08697 08709 function shorten_text($text, $ideal=30, $exact = false, $ending='...') { 08710 08711 global $CFG; 08712 08713 // if the plain text is shorter than the maximum length, return the whole text 08714 if (strlen(preg_replace('/<.*?>/', '', $text)) <= $ideal) { 08715 return $text; 08716 } 08717 08718 // Splits on HTML tags. Each open/close/empty tag will be the first thing 08719 // and only tag in its 'line' 08720 preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER); 08721 08722 $total_length = strlen($ending); 08723 $truncate = ''; 08724 08725 // This array stores information about open and close tags and their position 08726 // in the truncated string. Each item in the array is an object with fields 08727 // ->open (true if open), ->tag (tag name in lower case), and ->pos 08728 // (byte position in truncated text) 08729 $tagdetails = array(); 08730 08731 foreach ($lines as $line_matchings) { 08732 // if there is any html-tag in this line, handle it and add it (uncounted) to the output 08733 if (!empty($line_matchings[1])) { 08734 // if it's an "empty element" with or without xhtml-conform closing slash (f.e. <br/>) 08735 if (preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $line_matchings[1])) { 08736 // do nothing 08737 // if tag is a closing tag (f.e. </b>) 08738 } else if (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) { 08739 // record closing tag 08740 $tagdetails[] = (object)array('open'=>false, 08741 'tag'=>strtolower($tag_matchings[1]), 'pos'=>strlen($truncate)); 08742 // if tag is an opening tag (f.e. <b>) 08743 } else if (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) { 08744 // record opening tag 08745 $tagdetails[] = (object)array('open'=>true, 08746 'tag'=>strtolower($tag_matchings[1]), 'pos'=>strlen($truncate)); 08747 } 08748 // add html-tag to $truncate'd text 08749 $truncate .= $line_matchings[1]; 08750 } 08751 08752 // calculate the length of the plain text part of the line; handle entities as one character 08753 $content_length = strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $line_matchings[2])); 08754 if ($total_length+$content_length > $ideal) { 08755 // the number of characters which are left 08756 $left = $ideal - $total_length; 08757 $entities_length = 0; 08758 // search for html entities 08759 if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $line_matchings[2], $entities, PREG_OFFSET_CAPTURE)) { 08760 // calculate the real length of all entities in the legal range 08761 foreach ($entities[0] as $entity) { 08762 if ($entity[1]+1-$entities_length <= $left) { 08763 $left--; 08764 $entities_length += strlen($entity[0]); 08765 } else { 08766 // no more characters left 08767 break; 08768 } 08769 } 08770 } 08771 $truncate .= substr($line_matchings[2], 0, $left+$entities_length); 08772 // maximum length is reached, so get off the loop 08773 break; 08774 } else { 08775 $truncate .= $line_matchings[2]; 08776 $total_length += $content_length; 08777 } 08778 08779 // if the maximum length is reached, get off the loop 08780 if($total_length >= $ideal) { 08781 break; 08782 } 08783 } 08784 08785 // if the words shouldn't be cut in the middle... 08786 if (!$exact) { 08787 // ...search the last occurence of a space... 08788 for ($k=strlen($truncate);$k>0;$k--) { 08789 if (!empty($truncate[$k]) && ($char = $truncate[$k])) { 08790 if ($char == '.' or $char == ' ') { 08791 $breakpos = $k+1; 08792 break; 08793 } else if (ord($char) >= 0xE0) { // Chinese/Japanese/Korean text 08794 $breakpos = $k; // can be truncated at any UTF-8 08795 break; // character boundary. 08796 } 08797 } 08798 } 08799 08800 if (isset($breakpos)) { 08801 // ...and cut the text in this position 08802 $truncate = substr($truncate, 0, $breakpos); 08803 } 08804 } 08805 08806 // add the defined ending to the text 08807 $truncate .= $ending; 08808 08809 // Now calculate the list of open html tags based on the truncate position 08810 $open_tags = array(); 08811 foreach ($tagdetails as $taginfo) { 08812 if(isset($breakpos) && $taginfo->pos >= $breakpos) { 08813 // Don't include tags after we made the break! 08814 break; 08815 } 08816 if($taginfo->open) { 08817 // add tag to the beginning of $open_tags list 08818 array_unshift($open_tags, $taginfo->tag); 08819 } else { 08820 $pos = array_search($taginfo->tag, array_reverse($open_tags, true)); // can have multiple exact same open tags, close the last one 08821 if ($pos !== false) { 08822 unset($open_tags[$pos]); 08823 } 08824 } 08825 } 08826 08827 // close all unclosed html-tags 08828 foreach ($open_tags as $tag) { 08829 $truncate .= '</' . $tag . '>'; 08830 } 08831 08832 return $truncate; 08833 } 08834 08835 08847 function getweek ($startdate, $thedate) { 08848 if ($thedate < $startdate) { // error 08849 return 0; 08850 } 08851 08852 return floor(($thedate - $startdate) / WEEKSECS) + 1; 08853 } 08854 08865 function generate_password($maxlen=10) { 08866 global $CFG; 08867 08868 if (empty($CFG->passwordpolicy)) { 08869 $fillers = PASSWORD_DIGITS; 08870 $wordlist = file($CFG->wordlist); 08871 $word1 = trim($wordlist[rand(0, count($wordlist) - 1)]); 08872 $word2 = trim($wordlist[rand(0, count($wordlist) - 1)]); 08873 $filler1 = $fillers[rand(0, strlen($fillers) - 1)]; 08874 $password = $word1 . $filler1 . $word2; 08875 } else { 08876 $minlen = !empty($CFG->minpasswordlength) ? $CFG->minpasswordlength : 0; 08877 $digits = $CFG->minpassworddigits; 08878 $lower = $CFG->minpasswordlower; 08879 $upper = $CFG->minpasswordupper; 08880 $nonalphanum = $CFG->minpasswordnonalphanum; 08881 $total = $lower + $upper + $digits + $nonalphanum; 08882 // minlength should be the greater one of the two ( $minlen and $total ) 08883 $minlen = $minlen < $total ? $total : $minlen; 08884 // maxlen can never be smaller than minlen 08885 $maxlen = $minlen > $maxlen ? $minlen : $maxlen; 08886 $additional = $maxlen - $total; 08887 08888 // Make sure we have enough characters to fulfill 08889 // complexity requirements 08890 $passworddigits = PASSWORD_DIGITS; 08891 while ($digits > strlen($passworddigits)) { 08892 $passworddigits .= PASSWORD_DIGITS; 08893 } 08894 $passwordlower = PASSWORD_LOWER; 08895 while ($lower > strlen($passwordlower)) { 08896 $passwordlower .= PASSWORD_LOWER; 08897 } 08898 $passwordupper = PASSWORD_UPPER; 08899 while ($upper > strlen($passwordupper)) { 08900 $passwordupper .= PASSWORD_UPPER; 08901 } 08902 $passwordnonalphanum = PASSWORD_NONALPHANUM; 08903 while ($nonalphanum > strlen($passwordnonalphanum)) { 08904 $passwordnonalphanum .= PASSWORD_NONALPHANUM; 08905 } 08906 08907 // Now mix and shuffle it all 08908 $password = str_shuffle (substr(str_shuffle ($passwordlower), 0, $lower) . 08909 substr(str_shuffle ($passwordupper), 0, $upper) . 08910 substr(str_shuffle ($passworddigits), 0, $digits) . 08911 substr(str_shuffle ($passwordnonalphanum), 0 , $nonalphanum) . 08912 substr(str_shuffle ($passwordlower . 08913 $passwordupper . 08914 $passworddigits . 08915 $passwordnonalphanum), 0 , $additional)); 08916 } 08917 08918 return substr ($password, 0, $maxlen); 08919 } 08920 08930 function format_float($float, $decimalpoints=1, $localized=true) { 08931 if (is_null($float)) { 08932 return ''; 08933 } 08934 if ($localized) { 08935 return number_format($float, $decimalpoints, get_string('decsep', 'langconfig'), ''); 08936 } else { 08937 return number_format($float, $decimalpoints, '.', ''); 08938 } 08939 } 08940 08948 function unformat_float($locale_float) { 08949 $locale_float = trim($locale_float); 08950 08951 if ($locale_float == '') { 08952 return null; 08953 } 08954 08955 $locale_float = str_replace(' ', '', $locale_float); // no spaces - those might be used as thousand separators 08956 08957 return (float)str_replace(get_string('decsep', 'langconfig'), '.', $locale_float); 08958 } 08959 08967 function swapshuffle($array) { 08968 08969 srand ((double) microtime() * 10000000); 08970 $last = count($array) - 1; 08971 for ($i=0;$i<=$last;$i++) { 08972 $from = rand(0,$last); 08973 $curr = $array[$i]; 08974 $array[$i] = $array[$from]; 08975 $array[$from] = $curr; 08976 } 08977 return $array; 08978 } 08979 08986 function swapshuffle_assoc($array) { 08987 08988 $newarray = array(); 08989 $newkeys = swapshuffle(array_keys($array)); 08990 08991 foreach ($newkeys as $newkey) { 08992 $newarray[$newkey] = $array[$newkey]; 08993 } 08994 return $newarray; 08995 } 08996 09008 function draw_rand_array($array, $draws) { 09009 srand ((double) microtime() * 10000000); 09010 09011 $return = array(); 09012 09013 $last = count($array); 09014 09015 if ($draws > $last) { 09016 $draws = $last; 09017 } 09018 09019 while ($draws > 0) { 09020 $last--; 09021 09022 $keys = array_keys($array); 09023 $rand = rand(0, $last); 09024 09025 $return[$keys[$rand]] = $array[$keys[$rand]]; 09026 unset($array[$keys[$rand]]); 09027 09028 $draws--; 09029 } 09030 09031 return $return; 09032 } 09033 09041 function microtime_diff($a, $b) { 09042 list($a_dec, $a_sec) = explode(' ', $a); 09043 list($b_dec, $b_sec) = explode(' ', $b); 09044 return $b_sec - $a_sec + $b_dec - $a_dec; 09045 } 09046 09055 function make_menu_from_list($list, $separator=',') { 09056 09057 $array = array_reverse(explode($separator, $list), true); 09058 foreach ($array as $key => $item) { 09059 $outarray[$key+1] = trim($item); 09060 } 09061 return $outarray; 09062 } 09063 09077 function make_grades_menu($gradingtype) { 09078 global $DB; 09079 09080 $grades = array(); 09081 if ($gradingtype < 0) { 09082 if ($scale = $DB->get_record('scale', array('id'=> (-$gradingtype)))) { 09083 return make_menu_from_list($scale->scale); 09084 } 09085 } else if ($gradingtype > 0) { 09086 for ($i=$gradingtype; $i>=0; $i--) { 09087 $grades[$i] = $i .' / '. $gradingtype; 09088 } 09089 return $grades; 09090 } 09091 return $grades; 09092 } 09093 09106 function course_scale_used($courseid, $scaleid) { 09107 global $CFG, $DB; 09108 09109 $return = 0; 09110 09111 if (!empty($scaleid)) { 09112 if ($cms = get_course_mods($courseid)) { 09113 foreach ($cms as $cm) { 09114 //Check cm->name/lib.php exists 09115 if (file_exists($CFG->dirroot.'/mod/'.$cm->modname.'/lib.php')) { 09116 include_once($CFG->dirroot.'/mod/'.$cm->modname.'/lib.php'); 09117 $function_name = $cm->modname.'_scale_used'; 09118 if (function_exists($function_name)) { 09119 if ($function_name($cm->instance,$scaleid)) { 09120 $return++; 09121 } 09122 } 09123 } 09124 } 09125 } 09126 09127 // check if any course grade item makes use of the scale 09128 $return += $DB->count_records('grade_items', array('courseid'=>$courseid, 'scaleid'=>$scaleid)); 09129 09130 // check if any outcome in the course makes use of the scale 09131 $return += $DB->count_records_sql("SELECT COUNT('x') 09132 FROM {grade_outcomes_courses} goc, 09133 {grade_outcomes} go 09134 WHERE go.id = goc.outcomeid 09135 AND go.scaleid = ? AND goc.courseid = ?", 09136 array($scaleid, $courseid)); 09137 } 09138 return $return; 09139 } 09140 09149 function site_scale_used($scaleid, &$courses) { 09150 $return = 0; 09151 09152 if (!is_array($courses) || count($courses) == 0) { 09153 $courses = get_courses("all",false,"c.id,c.shortname"); 09154 } 09155 09156 if (!empty($scaleid)) { 09157 if (is_array($courses) && count($courses) > 0) { 09158 foreach ($courses as $course) { 09159 $return += course_scale_used($course->id,$scaleid); 09160 } 09161 } 09162 } 09163 return $return; 09164 } 09165 09175 function make_unique_id_code($extra='') { 09176 09177 $hostname = 'unknownhost'; 09178 if (!empty($_SERVER['HTTP_HOST'])) { 09179 $hostname = $_SERVER['HTTP_HOST']; 09180 } else if (!empty($_ENV['HTTP_HOST'])) { 09181 $hostname = $_ENV['HTTP_HOST']; 09182 } else if (!empty($_SERVER['SERVER_NAME'])) { 09183 $hostname = $_SERVER['SERVER_NAME']; 09184 } else if (!empty($_ENV['SERVER_NAME'])) { 09185 $hostname = $_ENV['SERVER_NAME']; 09186 } 09187 09188 $date = gmdate("ymdHis"); 09189 09190 $random = random_string(6); 09191 09192 if ($extra) { 09193 return $hostname .'+'. $date .'+'. $random .'+'. $extra; 09194 } else { 09195 return $hostname .'+'. $date .'+'. $random; 09196 } 09197 } 09198 09199 09215 function address_in_subnet($addr, $subnetstr) { 09216 09217 if ($addr == '0.0.0.0') { 09218 return false; 09219 } 09220 $subnets = explode(',', $subnetstr); 09221 $found = false; 09222 $addr = trim($addr); 09223 $addr = cleanremoteaddr($addr, false); // normalise 09224 if ($addr === null) { 09225 return false; 09226 } 09227 $addrparts = explode(':', $addr); 09228 09229 $ipv6 = strpos($addr, ':'); 09230 09231 foreach ($subnets as $subnet) { 09232 $subnet = trim($subnet); 09233 if ($subnet === '') { 09234 continue; 09235 } 09236 09237 if (strpos($subnet, '/') !== false) { 09239 list($ip, $mask) = explode('/', $subnet); 09240 $mask = trim($mask); 09241 if (!is_number($mask)) { 09242 continue; // incorect mask number, eh? 09243 } 09244 $ip = cleanremoteaddr($ip, false); // normalise 09245 if ($ip === null) { 09246 continue; 09247 } 09248 if (strpos($ip, ':') !== false) { 09249 // IPv6 09250 if (!$ipv6) { 09251 continue; 09252 } 09253 if ($mask > 128 or $mask < 0) { 09254 continue; // nonsense 09255 } 09256 if ($mask == 0) { 09257 return true; // any address 09258 } 09259 if ($mask == 128) { 09260 if ($ip === $addr) { 09261 return true; 09262 } 09263 continue; 09264 } 09265 $ipparts = explode(':', $ip); 09266 $modulo = $mask % 16; 09267 $ipnet = array_slice($ipparts, 0, ($mask-$modulo)/16); 09268 $addrnet = array_slice($addrparts, 0, ($mask-$modulo)/16); 09269 if (implode(':', $ipnet) === implode(':', $addrnet)) { 09270 if ($modulo == 0) { 09271 return true; 09272 } 09273 $pos = ($mask-$modulo)/16; 09274 $ipnet = hexdec($ipparts[$pos]); 09275 $addrnet = hexdec($addrparts[$pos]); 09276 $mask = 0xffff << (16 - $modulo); 09277 if (($addrnet & $mask) == ($ipnet & $mask)) { 09278 return true; 09279 } 09280 } 09281 09282 } else { 09283 // IPv4 09284 if ($ipv6) { 09285 continue; 09286 } 09287 if ($mask > 32 or $mask < 0) { 09288 continue; // nonsense 09289 } 09290 if ($mask == 0) { 09291 return true; 09292 } 09293 if ($mask == 32) { 09294 if ($ip === $addr) { 09295 return true; 09296 } 09297 continue; 09298 } 09299 $mask = 0xffffffff << (32 - $mask); 09300 if (((ip2long($addr) & $mask) == (ip2long($ip) & $mask))) { 09301 return true; 09302 } 09303 } 09304 09305 } else if (strpos($subnet, '-') !== false) { 09307 $parts = explode('-', $subnet); 09308 if (count($parts) != 2) { 09309 continue; 09310 } 09311 09312 if (strpos($subnet, ':') !== false) { 09313 // IPv6 09314 if (!$ipv6) { 09315 continue; 09316 } 09317 $ipstart = cleanremoteaddr(trim($parts[0]), false); // normalise 09318 if ($ipstart === null) { 09319 continue; 09320 } 09321 $ipparts = explode(':', $ipstart); 09322 $start = hexdec(array_pop($ipparts)); 09323 $ipparts[] = trim($parts[1]); 09324 $ipend = cleanremoteaddr(implode(':', $ipparts), false); // normalise 09325 if ($ipend === null) { 09326 continue; 09327 } 09328 $ipparts[7] = ''; 09329 $ipnet = implode(':', $ipparts); 09330 if (strpos($addr, $ipnet) !== 0) { 09331 continue; 09332 } 09333 $ipparts = explode(':', $ipend); 09334 $end = hexdec($ipparts[7]); 09335 09336 $addrend = hexdec($addrparts[7]); 09337 09338 if (($addrend >= $start) and ($addrend <= $end)) { 09339 return true; 09340 } 09341 09342 } else { 09343 // IPv4 09344 if ($ipv6) { 09345 continue; 09346 } 09347 $ipstart = cleanremoteaddr(trim($parts[0]), false); // normalise 09348 if ($ipstart === null) { 09349 continue; 09350 } 09351 $ipparts = explode('.', $ipstart); 09352 $ipparts[3] = trim($parts[1]); 09353 $ipend = cleanremoteaddr(implode('.', $ipparts), false); // normalise 09354 if ($ipend === null) { 09355 continue; 09356 } 09357 09358 if ((ip2long($addr) >= ip2long($ipstart)) and (ip2long($addr) <= ip2long($ipend))) { 09359 return true; 09360 } 09361 } 09362 09363 } else { 09365 if (strpos($subnet, ':') !== false) { 09366 // IPv6 09367 if (!$ipv6) { 09368 continue; 09369 } 09370 $parts = explode(':', $subnet); 09371 $count = count($parts); 09372 if ($parts[$count-1] === '') { 09373 unset($parts[$count-1]); // trim trailing : 09374 $count--; 09375 $subnet = implode('.', $parts); 09376 } 09377 $isip = cleanremoteaddr($subnet, false); // normalise 09378 if ($isip !== null) { 09379 if ($isip === $addr) { 09380 return true; 09381 } 09382 continue; 09383 } else if ($count > 8) { 09384 continue; 09385 } 09386 $zeros = array_fill(0, 8-$count, '0'); 09387 $subnet = $subnet.':'.implode(':', $zeros).'/'.($count*16); 09388 if (address_in_subnet($addr, $subnet)) { 09389 return true; 09390 } 09391 09392 } else { 09393 // IPv4 09394 if ($ipv6) { 09395 continue; 09396 } 09397 $parts = explode('.', $subnet); 09398 $count = count($parts); 09399 if ($parts[$count-1] === '') { 09400 unset($parts[$count-1]); // trim trailing . 09401 $count--; 09402 $subnet = implode('.', $parts); 09403 } 09404 if ($count == 4) { 09405 $subnet = cleanremoteaddr($subnet, false); // normalise 09406 if ($subnet === $addr) { 09407 return true; 09408 } 09409 continue; 09410 } else if ($count > 4) { 09411 continue; 09412 } 09413 $zeros = array_fill(0, 4-$count, '0'); 09414 $subnet = $subnet.'.'.implode('.', $zeros).'/'.($count*8); 09415 if (address_in_subnet($addr, $subnet)) { 09416 return true; 09417 } 09418 } 09419 } 09420 } 09421 09422 return false; 09423 } 09424 09434 function mtrace($string, $eol="\n", $sleep=0) { 09435 09436 if (defined('STDOUT')) { 09437 fwrite(STDOUT, $string.$eol); 09438 } else { 09439 echo $string . $eol; 09440 } 09441 09442 flush(); 09443 09444 //delay to keep message on user's screen in case of subsequent redirect 09445 if ($sleep) { 09446 sleep($sleep); 09447 } 09448 } 09449 09456 function cleardoubleslashes ($path) { 09457 return preg_replace('/(\/|\\\){1,}/','/',$path); 09458 } 09459 09466 function remoteip_in_list($list){ 09467 $inlist = false; 09468 $client_ip = getremoteaddr(null); 09469 09470 if(!$client_ip){ 09471 // ensure access on cli 09472 return true; 09473 } 09474 09475 $list = explode("\n", $list); 09476 foreach($list as $subnet) { 09477 $subnet = trim($subnet); 09478 if (address_in_subnet($client_ip, $subnet)) { 09479 $inlist = true; 09480 break; 09481 } 09482 } 09483 return $inlist; 09484 } 09485 09493 function getremoteaddr($default='0.0.0.0') { 09494 global $CFG; 09495 09496 if (empty($CFG->getremoteaddrconf)) { 09497 // This will happen, for example, before just after the upgrade, as the 09498 // user is redirected to the admin screen. 09499 $variablestoskip = 0; 09500 } else { 09501 $variablestoskip = $CFG->getremoteaddrconf; 09502 } 09503 if (!($variablestoskip & GETREMOTEADDR_SKIP_HTTP_CLIENT_IP)) { 09504 if (!empty($_SERVER['HTTP_CLIENT_IP'])) { 09505 $address = cleanremoteaddr($_SERVER['HTTP_CLIENT_IP']); 09506 return $address ? $address : $default; 09507 } 09508 } 09509 if (!($variablestoskip & GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR)) { 09510 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { 09511 $address = cleanremoteaddr($_SERVER['HTTP_X_FORWARDED_FOR']); 09512 return $address ? $address : $default; 09513 } 09514 } 09515 if (!empty($_SERVER['REMOTE_ADDR'])) { 09516 $address = cleanremoteaddr($_SERVER['REMOTE_ADDR']); 09517 return $address ? $address : $default; 09518 } else { 09519 return $default; 09520 } 09521 } 09522 09531 function cleanremoteaddr($addr, $compress=false) { 09532 $addr = trim($addr); 09533 09534 //TODO: maybe add a separate function is_addr_public() or something like this 09535 09536 if (strpos($addr, ':') !== false) { 09537 // can be only IPv6 09538 $parts = explode(':', $addr); 09539 $count = count($parts); 09540 09541 if (strpos($parts[$count-1], '.') !== false) { 09542 //legacy ipv4 notation 09543 $last = array_pop($parts); 09544 $ipv4 = cleanremoteaddr($last, true); 09545 if ($ipv4 === null) { 09546 return null; 09547 } 09548 $bits = explode('.', $ipv4); 09549 $parts[] = dechex($bits[0]).dechex($bits[1]); 09550 $parts[] = dechex($bits[2]).dechex($bits[3]); 09551 $count = count($parts); 09552 $addr = implode(':', $parts); 09553 } 09554 09555 if ($count < 3 or $count > 8) { 09556 return null; // severly malformed 09557 } 09558 09559 if ($count != 8) { 09560 if (strpos($addr, '::') === false) { 09561 return null; // malformed 09562 } 09563 // uncompress :: 09564 $insertat = array_search('', $parts, true); 09565 $missing = array_fill(0, 1 + 8 - $count, '0'); 09566 array_splice($parts, $insertat, 1, $missing); 09567 foreach ($parts as $key=>$part) { 09568 if ($part === '') { 09569 $parts[$key] = '0'; 09570 } 09571 } 09572 } 09573 09574 $adr = implode(':', $parts); 09575 if (!preg_match('/^([0-9a-f]{1,4})(:[0-9a-f]{1,4})*$/i', $adr)) { 09576 return null; // incorrect format - sorry 09577 } 09578 09579 // normalise 0s and case 09580 $parts = array_map('hexdec', $parts); 09581 $parts = array_map('dechex', $parts); 09582 09583 $result = implode(':', $parts); 09584 09585 if (!$compress) { 09586 return $result; 09587 } 09588 09589 if ($result === '0:0:0:0:0:0:0:0') { 09590 return '::'; // all addresses 09591 } 09592 09593 $compressed = preg_replace('/(:0)+:0$/', '::', $result, 1); 09594 if ($compressed !== $result) { 09595 return $compressed; 09596 } 09597 09598 $compressed = preg_replace('/^(0:){2,7}/', '::', $result, 1); 09599 if ($compressed !== $result) { 09600 return $compressed; 09601 } 09602 09603 $compressed = preg_replace('/(:0){2,6}:/', '::', $result, 1); 09604 if ($compressed !== $result) { 09605 return $compressed; 09606 } 09607 09608 return $result; 09609 } 09610 09611 // first get all things that look like IPv4 addresses 09612 $parts = array(); 09613 if (!preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $addr, $parts)) { 09614 return null; 09615 } 09616 unset($parts[0]); 09617 09618 foreach ($parts as $key=>$match) { 09619 if ($match > 255) { 09620 return null; 09621 } 09622 $parts[$key] = (int)$match; // normalise 0s 09623 } 09624 09625 return implode('.', $parts); 09626 } 09627 09635 function fullclone($thing) { 09636 return unserialize(serialize($thing)); 09637 } 09638 09639 09647 function moodle_request_shutdown() { 09648 global $CFG; 09649 09650 // help apache server if possible 09651 $apachereleasemem = false; 09652 if (function_exists('apache_child_terminate') && function_exists('memory_get_usage') 09653 && ini_get_bool('child_terminate')) { 09654 09655 $limit = (empty($CFG->apachemaxmem) ? 64*1024*1024 : $CFG->apachemaxmem); //64MB default 09656 if (memory_get_usage() > get_real_size($limit)) { 09657 $apachereleasemem = $limit; 09658 @apache_child_terminate(); 09659 } 09660 } 09661 09662 // deal with perf logging 09663 if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) { 09664 if ($apachereleasemem) { 09665 error_log('Mem usage over '.$apachereleasemem.': marking Apache child for reaping.'); 09666 } 09667 if (defined('MDL_PERFTOLOG')) { 09668 $perf = get_performance_info(); 09669 error_log("PERF: " . $perf['txt']); 09670 } 09671 if (defined('MDL_PERFINC')) { 09672 $inc = get_included_files(); 09673 $ts = 0; 09674 foreach($inc as $f) { 09675 if (preg_match(':^/:', $f)) { 09676 $fs = filesize($f); 09677 $ts += $fs; 09678 $hfs = display_size($fs); 09679 error_log(substr($f,strlen($CFG->dirroot)) . " size: $fs ($hfs)" 09680 , NULL, NULL, 0); 09681 } else { 09682 error_log($f , NULL, NULL, 0); 09683 } 09684 } 09685 if ($ts > 0 ) { 09686 $hts = display_size($ts); 09687 error_log("Total size of files included: $ts ($hts)"); 09688 } 09689 } 09690 } 09691 } 09692 09700 function message_popup_window() { 09701 global $USER, $DB, $PAGE, $CFG, $SITE; 09702 09703 if (!$PAGE->get_popup_notification_allowed() || empty($CFG->messaging)) { 09704 return; 09705 } 09706 09707 if (!isloggedin() || isguestuser()) { 09708 return; 09709 } 09710 09711 if (!isset($USER->message_lastpopup)) { 09712 $USER->message_lastpopup = 0; 09713 } else if ($USER->message_lastpopup > (time()-120)) { 09714 //dont run the query to check whether to display a popup if its been run in the last 2 minutes 09715 return; 09716 } 09717 09718 //a quick query to check whether the user has new messages 09719 $messagecount = $DB->count_records('message', array('useridto' => $USER->id)); 09720 if ($messagecount<1) { 09721 return; 09722 } 09723 09724 //got unread messages so now do another query that joins with the user table 09725 $messagesql = "SELECT m.id, m.smallmessage, m.fullmessageformat, m.notification, u.firstname, u.lastname 09726 FROM {message} m 09727 JOIN {message_working} mw ON m.id=mw.unreadmessageid 09728 JOIN {message_processors} p ON mw.processorid=p.id 09729 JOIN {user} u ON m.useridfrom=u.id 09730 WHERE m.useridto = :userid 09731 AND p.name='popup'"; 09732 09733 //if the user was last notified over an hour ago we can renotify them of old messages 09734 //so don't worry about when the new message was sent 09735 $lastnotifiedlongago = $USER->message_lastpopup < (time()-3600); 09736 if (!$lastnotifiedlongago) { 09737 $messagesql .= 'AND m.timecreated > :lastpopuptime'; 09738 } 09739 09740 $message_users = $DB->get_records_sql($messagesql, array('userid'=>$USER->id, 'lastpopuptime'=>$USER->message_lastpopup)); 09741 09742 //if we have new messages to notify the user about 09743 if (!empty($message_users)) { 09744 09745 $strmessages = ''; 09746 if (count($message_users)>1) { 09747 $strmessages = get_string('unreadnewmessages', 'message', count($message_users)); 09748 } else { 09749 $message_users = reset($message_users); 09750 09751 //show who the message is from if its not a notification 09752 if (!$message_users->notification) { 09753 $strmessages = get_string('unreadnewmessage', 'message', fullname($message_users) ); 09754 } 09755 09756 //try to display the small version of the message 09757 $smallmessage = null; 09758 if (!empty($message_users->smallmessage)) { 09759 //display the first 200 chars of the message in the popup 09760 $textlib = textlib_get_instance(); 09761 $smallmessage = null; 09762 if ($textlib->strlen($message_users->smallmessage) > 200) { 09763 $smallmessage = $textlib->substr($message_users->smallmessage,0,200).'...'; 09764 } else { 09765 $smallmessage = $message_users->smallmessage; 09766 } 09767 09768 //prevent html symbols being displayed 09769 if ($message_users->fullmessageformat == FORMAT_HTML) { 09770 $smallmessage = html_to_text($smallmessage); 09771 } else { 09772 $smallmessage = s($smallmessage); 09773 } 09774 } else if ($message_users->notification) { 09775 //its a notification with no smallmessage so just say they have a notification 09776 $smallmessage = get_string('unreadnewnotification', 'message'); 09777 } 09778 if (!empty($smallmessage)) { 09779 $strmessages .= '<div id="usermessage">'.s($smallmessage).'</div>'; 09780 } 09781 } 09782 09783 $strgomessage = get_string('gotomessages', 'message'); 09784 $strstaymessage = get_string('ignore','admin'); 09785 09786 $url = $CFG->wwwroot.'/message/index.php'; 09787 $content = html_writer::start_tag('div', array('id'=>'newmessageoverlay','class'=>'mdl-align')). 09788 html_writer::start_tag('div', array('id'=>'newmessagetext')). 09789 $strmessages. 09790 html_writer::end_tag('div'). 09791 09792 html_writer::start_tag('div', array('id'=>'newmessagelinks')). 09793 html_writer::link($url, $strgomessage, array('id'=>'notificationyes')).' '. 09794 html_writer::link('', $strstaymessage, array('id'=>'notificationno')). 09795 html_writer::end_tag('div'); 09796 html_writer::end_tag('div'); 09797 09798 $PAGE->requires->js_init_call('M.core_message.init_notification', array('', $content, $url)); 09799 09800 $USER->message_lastpopup = time(); 09801 } 09802 } 09803 09813 function bounded_number($min, $value, $max) { 09814 if($value < $min) { 09815 return $min; 09816 } 09817 if($value > $max) { 09818 return $max; 09819 } 09820 return $value; 09821 } 09822 09829 function array_is_nested($array) { 09830 foreach ($array as $value) { 09831 if (is_array($value)) { 09832 return true; 09833 } 09834 } 09835 return false; 09836 } 09837 09849 function get_performance_info() { 09850 global $CFG, $PERF, $DB, $PAGE; 09851 09852 $info = array(); 09853 $info['html'] = ''; // holds userfriendly HTML representation 09854 $info['txt'] = me() . ' '; // holds log-friendly representation 09855 09856 $info['realtime'] = microtime_diff($PERF->starttime, microtime()); 09857 09858 $info['html'] .= '<span class="timeused">'.$info['realtime'].' secs</span> '; 09859 $info['txt'] .= 'time: '.$info['realtime'].'s '; 09860 09861 if (function_exists('memory_get_usage')) { 09862 $info['memory_total'] = memory_get_usage(); 09863 $info['memory_growth'] = memory_get_usage() - $PERF->startmemory; 09864 $info['html'] .= '<span class="memoryused">RAM: '.display_size($info['memory_total']).'</span> '; 09865 $info['txt'] .= 'memory_total: '.$info['memory_total'].'B (' . display_size($info['memory_total']).') memory_growth: '.$info['memory_growth'].'B ('.display_size($info['memory_growth']).') '; 09866 } 09867 09868 if (function_exists('memory_get_peak_usage')) { 09869 $info['memory_peak'] = memory_get_peak_usage(); 09870 $info['html'] .= '<span class="memoryused">RAM peak: '.display_size($info['memory_peak']).'</span> '; 09871 $info['txt'] .= 'memory_peak: '.$info['memory_peak'].'B (' . display_size($info['memory_peak']).') '; 09872 } 09873 09874 $inc = get_included_files(); 09875 //error_log(print_r($inc,1)); 09876 $info['includecount'] = count($inc); 09877 $info['html'] .= '<span class="included">Included '.$info['includecount'].' files</span> '; 09878 $info['txt'] .= 'includecount: '.$info['includecount'].' '; 09879 09880 $filtermanager = filter_manager::instance(); 09881 if (method_exists($filtermanager, 'get_performance_summary')) { 09882 list($filterinfo, $nicenames) = $filtermanager->get_performance_summary(); 09883 $info = array_merge($filterinfo, $info); 09884 foreach ($filterinfo as $key => $value) { 09885 $info['html'] .= "<span class='$key'>$nicenames[$key]: $value </span> "; 09886 $info['txt'] .= "$key: $value "; 09887 } 09888 } 09889 09890 $stringmanager = get_string_manager(); 09891 if (method_exists($stringmanager, 'get_performance_summary')) { 09892 list($filterinfo, $nicenames) = $stringmanager->get_performance_summary(); 09893 $info = array_merge($filterinfo, $info); 09894 foreach ($filterinfo as $key => $value) { 09895 $info['html'] .= "<span class='$key'>$nicenames[$key]: $value </span> "; 09896 $info['txt'] .= "$key: $value "; 09897 } 09898 } 09899 09900 $jsmodules = $PAGE->requires->get_loaded_modules(); 09901 if ($jsmodules) { 09902 $yuicount = 0; 09903 $othercount = 0; 09904 $details = ''; 09905 foreach ($jsmodules as $module => $backtraces) { 09906 if (strpos($module, 'yui') === 0) { 09907 $yuicount += 1; 09908 } else { 09909 $othercount += 1; 09910 } 09911 $details .= "<div class='yui-module'><p>$module</p>"; 09912 foreach ($backtraces as $backtrace) { 09913 $details .= "<div class='backtrace'>$backtrace</div>"; 09914 } 09915 $details .= '</div>'; 09916 } 09917 $info['html'] .= "<span class='includedyuimodules'>Included YUI modules: $yuicount</span> "; 09918 $info['txt'] .= "includedyuimodules: $yuicount "; 09919 $info['html'] .= "<span class='includedjsmodules'>Other JavaScript modules: $othercount</span> "; 09920 $info['txt'] .= "includedjsmodules: $othercount "; 09921 // Slightly odd to output the details in a display: none div. The point 09922 // Is that it takes a lot of space, and if you care you can reveal it 09923 // using firebug. 09924 $info['html'] .= '<div id="yui-module-debug" class="notifytiny">'.$details.'</div>'; 09925 } 09926 09927 if (!empty($PERF->logwrites)) { 09928 $info['logwrites'] = $PERF->logwrites; 09929 $info['html'] .= '<span class="logwrites">Log DB writes '.$info['logwrites'].'</span> '; 09930 $info['txt'] .= 'logwrites: '.$info['logwrites'].' '; 09931 } 09932 09933 $info['dbqueries'] = $DB->perf_get_reads().'/'.($DB->perf_get_writes() - $PERF->logwrites); 09934 $info['html'] .= '<span class="dbqueries">DB reads/writes: '.$info['dbqueries'].'</span> '; 09935 $info['txt'] .= 'db reads/writes: '.$info['dbqueries'].' '; 09936 09937 if (function_exists('posix_times')) { 09938 $ptimes = posix_times(); 09939 if (is_array($ptimes)) { 09940 foreach ($ptimes as $key => $val) { 09941 $info[$key] = $ptimes[$key] - $PERF->startposixtimes[$key]; 09942 } 09943 $info['html'] .= "<span class=\"posixtimes\">ticks: $info[ticks] user: $info[utime] sys: $info[stime] cuser: $info[cutime] csys: $info[cstime]</span> "; 09944 $info['txt'] .= "ticks: $info[ticks] user: $info[utime] sys: $info[stime] cuser: $info[cutime] csys: $info[cstime] "; 09945 } 09946 } 09947 09948 // Grab the load average for the last minute 09949 // /proc will only work under some linux configurations 09950 // while uptime is there under MacOSX/Darwin and other unices 09951 if (is_readable('/proc/loadavg') && $loadavg = @file('/proc/loadavg')) { 09952 list($server_load) = explode(' ', $loadavg[0]); 09953 unset($loadavg); 09954 } else if ( function_exists('is_executable') && is_executable('/usr/bin/uptime') && $loadavg = `/usr/bin/uptime` ) { 09955 if (preg_match('/load averages?: (\d+[\.,:]\d+)/', $loadavg, $matches)) { 09956 $server_load = $matches[1]; 09957 } else { 09958 trigger_error('Could not parse uptime output!'); 09959 } 09960 } 09961 if (!empty($server_load)) { 09962 $info['serverload'] = $server_load; 09963 $info['html'] .= '<span class="serverload">Load average: '.$info['serverload'].'</span> '; 09964 $info['txt'] .= "serverload: {$info['serverload']} "; 09965 } 09966 09967 // Display size of session if session started 09968 if (session_id()) { 09969 $info['sessionsize'] = display_size(strlen(session_encode())); 09970 $info['html'] .= '<span class="sessionsize">Session: ' . $info['sessionsize'] . '</span> '; 09971 $info['txt'] .= "Session: {$info['sessionsize']} "; 09972 } 09973 09974 /* if (isset($rcache->hits) && isset($rcache->misses)) { 09975 $info['rcachehits'] = $rcache->hits; 09976 $info['rcachemisses'] = $rcache->misses; 09977 $info['html'] .= '<span class="rcache">Record cache hit/miss ratio : '. 09978 "{$rcache->hits}/{$rcache->misses}</span> "; 09979 $info['txt'] .= 'rcache: '. 09980 "{$rcache->hits}/{$rcache->misses} "; 09981 }*/ 09982 $info['html'] = '<div class="performanceinfo siteinfo">'.$info['html'].'</div>'; 09983 return $info; 09984 } 09985 09989 function apd_get_profiling() { 09990 return shell_exec('pprofp -u ' . ini_get('apd.dumpdir') . '/pprof.' . getmypid() . '.*'); 09991 } 09992 10000 function remove_dir($dir, $content_only=false) { 10001 if (!file_exists($dir)) { 10002 // nothing to do 10003 return true; 10004 } 10005 $handle = opendir($dir); 10006 $result = true; 10007 while (false!==($item = readdir($handle))) { 10008 if($item != '.' && $item != '..') { 10009 if(is_dir($dir.'/'.$item)) { 10010 $result = remove_dir($dir.'/'.$item) && $result; 10011 }else{ 10012 $result = unlink($dir.'/'.$item) && $result; 10013 } 10014 } 10015 } 10016 closedir($handle); 10017 if ($content_only) { 10018 clearstatcache(); // make sure file stat cache is properly invalidated 10019 return $result; 10020 } 10021 $result = rmdir($dir); // if anything left the result will be false, no need for && $result 10022 clearstatcache(); // make sure file stat cache is properly invalidated 10023 return $result; 10024 } 10025 10034 function object_property_exists( $obj, $property ) { 10035 if (is_string( $obj )) { 10036 $properties = get_class_vars( $obj ); 10037 } 10038 else { 10039 $properties = get_object_vars( $obj ); 10040 } 10041 return array_key_exists( $property, $properties ); 10042 } 10043 10044 10051 function custom_script_path() { 10052 global $CFG, $SCRIPT; 10053 10054 if ($SCRIPT === null) { 10055 // Probably some weird external script 10056 return false; 10057 } 10058 10059 $scriptpath = $CFG->customscripts . $SCRIPT; 10060 10061 // check the custom script exists 10062 if (file_exists($scriptpath) and is_file($scriptpath)) { 10063 return $scriptpath; 10064 } else { 10065 return false; 10066 } 10067 } 10068 10077 function is_mnet_remote_user($user) { 10078 global $CFG; 10079 10080 if (!isset($CFG->mnet_localhost_id)) { 10081 include_once $CFG->dirroot . '/mnet/lib.php'; 10082 $env = new mnet_environment(); 10083 $env->init(); 10084 unset($env); 10085 } 10086 10087 return (!empty($user->mnethostid) && $user->mnethostid != $CFG->mnet_localhost_id); 10088 } 10089 10098 function setup_lang_from_browser() { 10099 10100 global $CFG, $SESSION, $USER; 10101 10102 if (!empty($SESSION->lang) or !empty($USER->lang) or empty($CFG->autolang)) { 10103 // Lang is defined in session or user profile, nothing to do 10104 return; 10105 } 10106 10107 if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { // There isn't list of browser langs, nothing to do 10108 return; 10109 } 10110 10112 $rawlangs = $_SERVER['HTTP_ACCEPT_LANGUAGE']; 10113 $rawlangs = str_replace('-', '_', $rawlangs); // we are using underscores 10114 $rawlangs = explode(',', $rawlangs); // Convert to array 10115 $langs = array(); 10116 10117 $order = 1.0; 10118 foreach ($rawlangs as $lang) { 10119 if (strpos($lang, ';') === false) { 10120 $langs[(string)$order] = $lang; 10121 $order = $order-0.01; 10122 } else { 10123 $parts = explode(';', $lang); 10124 $pos = strpos($parts[1], '='); 10125 $langs[substr($parts[1], $pos+1)] = $parts[0]; 10126 } 10127 } 10128 krsort($langs, SORT_NUMERIC); 10129 10131 foreach ($langs as $lang) { 10132 $lang = strtolower(clean_param($lang, PARAM_SAFEDIR)); // clean it properly for include 10133 if (get_string_manager()->translation_exists($lang, false)) { 10134 $SESSION->lang = $lang; 10135 break; 10136 } 10137 } 10138 return; 10139 } 10140 10150 function is_proxybypass( $url ) { 10151 global $CFG; 10152 10153 // sanity check 10154 if (empty($CFG->proxyhost) or empty($CFG->proxybypass)) { 10155 return false; 10156 } 10157 10158 // get the host part out of the url 10159 if (!$host = parse_url( $url, PHP_URL_HOST )) { 10160 return false; 10161 } 10162 10163 // get the possible bypass hosts into an array 10164 $matches = explode( ',', $CFG->proxybypass ); 10165 10166 // check for a match 10167 // (IPs need to match the left hand side and hosts the right of the url, 10168 // but we can recklessly check both as there can't be a false +ve) 10169 $bypass = false; 10170 foreach ($matches as $match) { 10171 $match = trim($match); 10172 10173 // try for IP match (Left side) 10174 $lhs = substr($host,0,strlen($match)); 10175 if (strcasecmp($match,$lhs)==0) { 10176 return true; 10177 } 10178 10179 // try for host match (Right side) 10180 $rhs = substr($host,-strlen($match)); 10181 if (strcasecmp($match,$rhs)==0) { 10182 return true; 10183 } 10184 } 10185 10186 // nothing matched. 10187 return false; 10188 } 10189 10190 10192 10199 function is_newnav($navigation) { 10200 if (is_array($navigation) && !empty($navigation['newnav'])) { 10201 return true; 10202 } else { 10203 return false; 10204 } 10205 } 10206 10216 function in_object_vars($var, $object) { 10217 $class_vars = get_class_vars(get_class($object)); 10218 $class_vars = array_keys($class_vars); 10219 return in_array($var, $class_vars); 10220 } 10221 10230 function object_array_unique($array, $keep_key_assoc = true) { 10231 $duplicate_keys = array(); 10232 $tmp = array(); 10233 10234 foreach ($array as $key=>$val) { 10235 // convert objects to arrays, in_array() does not support objects 10236 if (is_object($val)) { 10237 $val = (array)$val; 10238 } 10239 10240 if (!in_array($val, $tmp)) { 10241 $tmp[] = $val; 10242 } else { 10243 $duplicate_keys[] = $key; 10244 } 10245 } 10246 10247 foreach ($duplicate_keys as $key) { 10248 unset($array[$key]); 10249 } 10250 10251 return $keep_key_assoc ? $array : array_values($array); 10252 } 10253 10260 function is_primary_admin($userid){ 10261 $primaryadmin = get_admin(); 10262 10263 if($userid == $primaryadmin->id){ 10264 return true; 10265 }else{ 10266 return false; 10267 } 10268 } 10269 10276 function get_site_identifier() { 10277 global $CFG; 10278 // Check to see if it is missing. If so, initialise it. 10279 if (empty($CFG->siteidentifier)) { 10280 set_config('siteidentifier', random_string(32) . $_SERVER['HTTP_HOST']); 10281 } 10282 // Return it. 10283 return $CFG->siteidentifier; 10284 } 10285 10293 function check_consecutive_identical_characters($password, $maxchars) { 10294 10295 if ($maxchars < 1) { 10296 return true; // 0 is to disable this check 10297 } 10298 if (strlen($password) <= $maxchars) { 10299 return true; // too short to fail this test 10300 } 10301 10302 $previouschar = ''; 10303 $consecutivecount = 1; 10304 foreach (str_split($password) as $char) { 10305 if ($char != $previouschar) { 10306 $consecutivecount = 1; 10307 } 10308 else { 10309 $consecutivecount++; 10310 if ($consecutivecount > $maxchars) { 10311 return false; // check failed already 10312 } 10313 } 10314 10315 $previouschar = $char; 10316 } 10317 10318 return true; 10319 } 10320 10341 function partial() { 10342 if (!class_exists('partial')) { 10343 class partial{ 10344 var $values = array(); 10345 var $func; 10346 10347 function __construct($func, $args) { 10348 $this->values = $args; 10349 $this->func = $func; 10350 } 10351 10352 function method() { 10353 $args = func_get_args(); 10354 return call_user_func_array($this->func, array_merge($this->values, $args)); 10355 } 10356 } 10357 } 10358 $args = func_get_args(); 10359 $func = array_shift($args); 10360 $p = new partial($func, $args); 10361 return array($p, 'method'); 10362 } 10363 10370 function get_mnet_environment() { 10371 global $CFG; 10372 require_once($CFG->dirroot . '/mnet/lib.php'); 10373 static $instance = null; 10374 if (empty($instance)) { 10375 $instance = new mnet_environment(); 10376 $instance->init(); 10377 } 10378 return $instance; 10379 } 10380 10387 function get_mnet_remote_client() { 10388 if (!defined('MNET_SERVER')) { 10389 debugging(get_string('notinxmlrpcserver', 'mnet')); 10390 return false; 10391 } 10392 global $MNET_REMOTE_CLIENT; 10393 if (isset($MNET_REMOTE_CLIENT)) { 10394 return $MNET_REMOTE_CLIENT; 10395 } 10396 return false; 10397 } 10398 10405 function set_mnet_remote_client($client) { 10406 if (!defined('MNET_SERVER')) { 10407 throw new moodle_exception('notinxmlrpcserver', 'mnet'); 10408 } 10409 global $MNET_REMOTE_CLIENT; 10410 $MNET_REMOTE_CLIENT = $client; 10411 } 10412 10419 function mnet_get_idp_jump_url($user) { 10420 global $CFG; 10421 10422 static $mnetjumps = array(); 10423 if (!array_key_exists($user->mnethostid, $mnetjumps)) { 10424 $idp = mnet_get_peer_host($user->mnethostid); 10425 $idpjumppath = mnet_get_app_jumppath($idp->applicationid); 10426 $mnetjumps[$user->mnethostid] = $idp->wwwroot . $idpjumppath . '?hostwwwroot=' . $CFG->wwwroot . '&wantsurl='; 10427 } 10428 return $mnetjumps[$user->mnethostid]; 10429 } 10430 10436 function get_home_page() { 10437 global $CFG; 10438 10439 if (isloggedin() && !isguestuser() && !empty($CFG->defaulthomepage)) { 10440 if ($CFG->defaulthomepage == HOMEPAGE_MY) { 10441 return HOMEPAGE_MY; 10442 } else { 10443 return (int)get_user_preferences('user_home_page_preference', HOMEPAGE_MY); 10444 } 10445 } 10446 return HOMEPAGE_SITE; 10447 }