|
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 00031 defined('MOODLE_INTERNAL') || die(); 00032 00037 define('MAX_COURSES_IN_CATEGORY', 10000); 00038 00043 define('MAX_COURSE_CATEGORIES', 10000); 00044 00048 define('LASTACCESS_UPDATE_SECS', 60); 00049 00056 function get_admin() { 00057 global $CFG, $DB; 00058 00059 static $mainadmin = null; 00060 00061 if (isset($mainadmin)) { 00062 return clone($mainadmin); 00063 } 00064 00065 if (empty($CFG->siteadmins)) { // Should not happen on an ordinary site 00066 return false; 00067 } 00068 00069 foreach (explode(',', $CFG->siteadmins) as $id) { 00070 if ($user = $DB->get_record('user', array('id'=>$id, 'deleted'=>0))) { 00071 $mainadmin = $user; 00072 break; 00073 } 00074 } 00075 00076 if ($mainadmin) { 00077 return clone($mainadmin); 00078 } else { 00079 // this should not happen 00080 return false; 00081 } 00082 } 00083 00089 function get_admins() { 00090 global $DB, $CFG; 00091 00092 if (empty($CFG->siteadmins)) { // Should not happen on an ordinary site 00093 return array(); 00094 } 00095 00096 $sql = "SELECT u.* 00097 FROM {user} u 00098 WHERE u.deleted = 0 AND u.id IN ($CFG->siteadmins)"; 00099 00100 return $DB->get_records_sql($sql); 00101 } 00102 00120 function search_users($courseid, $groupid, $searchtext, $sort='', array $exceptions=null) { 00121 global $DB; 00122 00123 $fullname = $DB->sql_fullname('u.firstname', 'u.lastname'); 00124 00125 if (!empty($exceptions)) { 00126 list($exceptions, $params) = $DB->get_in_or_equal($exceptions, SQL_PARAMS_NAMED, 'ex', false); 00127 $except = "AND u.id $exceptions"; 00128 } else { 00129 $except = ""; 00130 $params = array(); 00131 } 00132 00133 if (!empty($sort)) { 00134 $order = "ORDER BY $sort"; 00135 } else { 00136 $order = ""; 00137 } 00138 00139 $select = "u.deleted = 0 AND u.confirmed = 1 AND (".$DB->sql_like($fullname, ':search1', false)." OR ".$DB->sql_like('u.email', ':search2', false).")"; 00140 $params['search1'] = "%$searchtext%"; 00141 $params['search2'] = "%$searchtext%"; 00142 00143 if (!$courseid or $courseid == SITEID) { 00144 $sql = "SELECT u.id, u.firstname, u.lastname, u.email 00145 FROM {user} u 00146 WHERE $select 00147 $except 00148 $order"; 00149 return $DB->get_records_sql($sql, $params); 00150 00151 } else { 00152 if ($groupid) { 00153 $sql = "SELECT u.id, u.firstname, u.lastname, u.email 00154 FROM {user} u 00155 JOIN {groups_members} gm ON gm.userid = u.id 00156 WHERE $select AND gm.groupid = :groupid 00157 $except 00158 $order"; 00159 $params['groupid'] = $groupid; 00160 return $DB->get_records_sql($sql, $params); 00161 00162 } else { 00163 $context = get_context_instance(CONTEXT_COURSE, $courseid); 00164 $contextlists = get_related_contexts_string($context); 00165 00166 $sql = "SELECT u.id, u.firstname, u.lastname, u.email 00167 FROM {user} u 00168 JOIN {role_assignments} ra ON ra.userid = u.id 00169 WHERE $select AND ra.contextid $contextlists 00170 $except 00171 $order"; 00172 return $DB->get_records_sql($sql, $params); 00173 } 00174 } 00175 } 00176 00196 function get_users($get=true, $search='', $confirmed=false, array $exceptions=null, $sort='firstname ASC', 00197 $firstinitial='', $lastinitial='', $page='', $recordsperpage='', $fields='*', $extraselect='', array $extraparams=null) { 00198 global $DB, $CFG; 00199 00200 if ($get && !$recordsperpage) { 00201 debugging('Call to get_users with $get = true no $recordsperpage limit. ' . 00202 'On large installations, this will probably cause an out of memory error. ' . 00203 'Please think again and change your code so that it does not try to ' . 00204 'load so much data into memory.', DEBUG_DEVELOPER); 00205 } 00206 00207 $fullname = $DB->sql_fullname(); 00208 00209 $select = " id <> :guestid AND deleted = 0"; 00210 $params = array('guestid'=>$CFG->siteguest); 00211 00212 if (!empty($search)){ 00213 $search = trim($search); 00214 $select .= " AND (".$DB->sql_like($fullname, ':search1', false)." OR ".$DB->sql_like('email', ':search2', false)." OR username = :search3)"; 00215 $params['search1'] = "%$search%"; 00216 $params['search2'] = "%$search%"; 00217 $params['search3'] = "$search"; 00218 } 00219 00220 if ($confirmed) { 00221 $select .= " AND confirmed = 1"; 00222 } 00223 00224 if ($exceptions) { 00225 list($exceptions, $eparams) = $DB->get_in_or_equal($exceptions, SQL_PARAMS_NAMED, 'ex', false); 00226 $params = $params + $eparams; 00227 $except = " AND id $exceptions"; 00228 } 00229 00230 if ($firstinitial) { 00231 $select .= " AND ".$DB->sql_like('firstname', ':fni', false, false); 00232 $params['fni'] = "$firstinitial%"; 00233 } 00234 if ($lastinitial) { 00235 $select .= " AND ".$DB->sql_like('lastname', ':lni', false, false); 00236 $params['lni'] = "$lastinitial%"; 00237 } 00238 00239 if ($extraselect) { 00240 $select .= " AND $extraselect"; 00241 $params = $params + (array)$extraparams; 00242 } 00243 00244 if ($get) { 00245 return $DB->get_records_select('user', $select, $params, $sort, $fields, $page, $recordsperpage); 00246 } else { 00247 return $DB->count_records_select('user', $select, $params); 00248 } 00249 } 00250 00251 00268 function get_users_listing($sort='lastaccess', $dir='ASC', $page=0, $recordsperpage=0, 00269 $search='', $firstinitial='', $lastinitial='', $extraselect='', 00270 array $extraparams=null, $extracontext = null) { 00271 global $DB; 00272 00273 $fullname = $DB->sql_fullname(); 00274 00275 $select = "deleted <> 1"; 00276 $params = array(); 00277 00278 if (!empty($search)) { 00279 $search = trim($search); 00280 $select .= " AND (". $DB->sql_like($fullname, ':search1', false, false). 00281 " OR ". $DB->sql_like('email', ':search2', false, false). 00282 " OR username = :search3)"; 00283 $params['search1'] = "%$search%"; 00284 $params['search2'] = "%$search%"; 00285 $params['search3'] = "$search"; 00286 } 00287 00288 if ($firstinitial) { 00289 $select .= " AND ". $DB->sql_like('firstname', ':fni', false, false); 00290 $params['fni'] = "$firstinitial%"; 00291 } 00292 if ($lastinitial) { 00293 $select .= " AND ". $DB->sql_like('lastname', ':lni', false, false); 00294 $params['lni'] = "$lastinitial%"; 00295 } 00296 00297 if ($extraselect) { 00298 $select .= " AND $extraselect"; 00299 $params = $params + (array)$extraparams; 00300 } 00301 00302 if ($sort) { 00303 $sort = " ORDER BY $sort $dir"; 00304 } 00305 00306 // If a context is specified, get extra user fields that the current user 00307 // is supposed to see. 00308 $extrafields = ''; 00309 if ($extracontext) { 00310 $extrafields = get_extra_user_fields_sql($extracontext, '', '', 00311 array('id', 'username', 'email', 'firstname', 'lastname', 'city', 'country', 00312 'lastaccess', 'confirmed', 'mnethostid')); 00313 } 00314 00315 // warning: will return UNCONFIRMED USERS 00316 return $DB->get_records_sql("SELECT id, username, email, firstname, lastname, city, country, 00317 lastaccess, confirmed, mnethostid, suspended $extrafields 00318 FROM {user} 00319 WHERE $select 00320 $sort", $params, $page, $recordsperpage); 00321 00322 } 00323 00324 00331 function get_users_confirmed() { 00332 global $DB, $CFG; 00333 return $DB->get_records_sql("SELECT * 00334 FROM {user} 00335 WHERE confirmed = 1 AND deleted = 0 AND id <> ?", array($CFG->siteguest)); 00336 } 00337 00338 00340 00341 00347 function get_site() { 00348 global $SITE, $DB; 00349 00350 if (!empty($SITE->id)) { // We already have a global to use, so return that 00351 return $SITE; 00352 } 00353 00354 if ($course = $DB->get_record('course', array('category'=>0))) { 00355 return $course; 00356 } else { 00357 // course table exists, but the site is not there, 00358 // unfortunately there is no automatic way to recover 00359 throw new moodle_exception('nosite', 'error'); 00360 } 00361 } 00362 00380 function get_courses($categoryid="all", $sort="c.sortorder ASC", $fields="c.*") { 00381 00382 global $USER, $CFG, $DB; 00383 00384 $params = array(); 00385 00386 if ($categoryid !== "all" && is_numeric($categoryid)) { 00387 $categoryselect = "WHERE c.category = :catid"; 00388 $params['catid'] = $categoryid; 00389 } else { 00390 $categoryselect = ""; 00391 } 00392 00393 if (empty($sort)) { 00394 $sortstatement = ""; 00395 } else { 00396 $sortstatement = "ORDER BY $sort"; 00397 } 00398 00399 $visiblecourses = array(); 00400 00401 list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx'); 00402 00403 $sql = "SELECT $fields $ccselect 00404 FROM {course} c 00405 $ccjoin 00406 $categoryselect 00407 $sortstatement"; 00408 00409 // pull out all course matching the cat 00410 if ($courses = $DB->get_records_sql($sql, $params)) { 00411 00412 // loop throught them 00413 foreach ($courses as $course) { 00414 context_instance_preload($course); 00415 if (isset($course->visible) && $course->visible <= 0) { 00416 // for hidden courses, require visibility check 00417 if (has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course->id))) { 00418 $visiblecourses [$course->id] = $course; 00419 } 00420 } else { 00421 $visiblecourses [$course->id] = $course; 00422 } 00423 } 00424 } 00425 return $visiblecourses; 00426 } 00427 00428 00449 function get_courses_page($categoryid="all", $sort="c.sortorder ASC", $fields="c.*", 00450 &$totalcount, $limitfrom="", $limitnum="") { 00451 global $USER, $CFG, $DB; 00452 00453 $params = array(); 00454 00455 $categoryselect = ""; 00456 if ($categoryid != "all" && is_numeric($categoryid)) { 00457 $categoryselect = "WHERE c.category = :catid"; 00458 $params['catid'] = $categoryid; 00459 } else { 00460 $categoryselect = ""; 00461 } 00462 00463 list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx'); 00464 00465 $totalcount = 0; 00466 if (!$limitfrom) { 00467 $limitfrom = 0; 00468 } 00469 $visiblecourses = array(); 00470 00471 $sql = "SELECT $fields $ccselect 00472 FROM {course} c 00473 $ccjoin 00474 $categoryselect 00475 ORDER BY $sort"; 00476 00477 // pull out all course matching the cat 00478 $rs = $DB->get_recordset_sql($sql, $params); 00479 // iteration will have to be done inside loop to keep track of the limitfrom and limitnum 00480 foreach($rs as $course) { 00481 context_instance_preload($course); 00482 if ($course->visible <= 0) { 00483 // for hidden courses, require visibility check 00484 if (has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course->id))) { 00485 $totalcount++; 00486 if ($totalcount > $limitfrom && (!$limitnum or count($visiblecourses) < $limitnum)) { 00487 $visiblecourses [$course->id] = $course; 00488 } 00489 } 00490 } else { 00491 $totalcount++; 00492 if ($totalcount > $limitfrom && (!$limitnum or count($visiblecourses) < $limitnum)) { 00493 $visiblecourses [$course->id] = $course; 00494 } 00495 } 00496 } 00497 $rs->close(); 00498 return $visiblecourses; 00499 } 00500 00523 function get_courses_wmanagers($categoryid=0, $sort="c.sortorder ASC", $fields=array()) { 00524 /* 00525 * The plan is to 00526 * 00527 * - Grab the courses JOINed w/context 00528 * 00529 * - Grab the interesting course-manager RAs 00530 * JOINed with a base user obj and add them to each course 00531 * 00532 * So as to do all the work in 2 DB queries. The RA+user JOIN 00533 * ends up being pretty expensive if it happens over _all_ 00534 * courses on a large site. (Are we surprised!?) 00535 * 00536 * So this should _never_ get called with 'all' on a large site. 00537 * 00538 */ 00539 global $USER, $CFG, $DB; 00540 00541 $params = array(); 00542 $allcats = false; // bool flag 00543 if ($categoryid === 'all') { 00544 $categoryclause = ''; 00545 $allcats = true; 00546 } elseif (is_numeric($categoryid)) { 00547 $categoryclause = "c.category = :catid"; 00548 $params['catid'] = $categoryid; 00549 } else { 00550 debugging("Could not recognise categoryid = $categoryid"); 00551 $categoryclause = ''; 00552 } 00553 00554 $basefields = array('id', 'category', 'sortorder', 00555 'shortname', 'fullname', 'idnumber', 00556 'startdate', 'visible', 00557 'newsitems', 'groupmode', 'groupmodeforce'); 00558 00559 if (!is_null($fields) && is_string($fields)) { 00560 if (empty($fields)) { 00561 $fields = $basefields; 00562 } else { 00563 // turn the fields from a string to an array that 00564 // get_user_courses_bycap() will like... 00565 $fields = explode(',',$fields); 00566 $fields = array_map('trim', $fields); 00567 $fields = array_unique(array_merge($basefields, $fields)); 00568 } 00569 } elseif (is_array($fields)) { 00570 $fields = array_merge($basefields,$fields); 00571 } 00572 $coursefields = 'c.' .join(',c.', $fields); 00573 00574 if (empty($sort)) { 00575 $sortstatement = ""; 00576 } else { 00577 $sortstatement = "ORDER BY $sort"; 00578 } 00579 00580 $where = 'WHERE c.id != ' . SITEID; 00581 if ($categoryclause !== ''){ 00582 $where = "$where AND $categoryclause"; 00583 } 00584 00585 // pull out all courses matching the cat 00586 list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx'); 00587 $sql = "SELECT $coursefields $ccselect 00588 FROM {course} c 00589 $ccjoin 00590 $where 00591 $sortstatement"; 00592 00593 $catpaths = array(); 00594 $catpath = NULL; 00595 if ($courses = $DB->get_records_sql($sql, $params)) { 00596 // loop on courses materialising 00597 // the context, and prepping data to fetch the 00598 // managers efficiently later... 00599 foreach ($courses as $k => $course) { 00600 context_instance_preload($course); 00601 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); 00602 $courses[$k] = $course; 00603 $courses[$k]->managers = array(); 00604 if ($allcats === false) { 00605 // single cat, so take just the first one... 00606 if ($catpath === NULL) { 00607 $catpath = preg_replace(':/\d+$:', '', $coursecontext->path); 00608 } 00609 } else { 00610 // chop off the contextid of the course itself 00611 // like dirname() does... 00612 $catpaths[] = preg_replace(':/\d+$:', '', $coursecontext->path); 00613 } 00614 } 00615 } else { 00616 return array(); // no courses! 00617 } 00618 00619 $CFG->coursecontact = trim($CFG->coursecontact); 00620 if (empty($CFG->coursecontact)) { 00621 return $courses; 00622 } 00623 00624 $managerroles = explode(',', $CFG->coursecontact); 00625 $catctxids = ''; 00626 if (count($managerroles)) { 00627 if ($allcats === true) { 00628 $catpaths = array_unique($catpaths); 00629 $ctxids = array(); 00630 foreach ($catpaths as $cpath) { 00631 $ctxids = array_merge($ctxids, explode('/',substr($cpath,1))); 00632 } 00633 $ctxids = array_unique($ctxids); 00634 $catctxids = implode( ',' , $ctxids); 00635 unset($catpaths); 00636 unset($cpath); 00637 } else { 00638 // take the ctx path from the first course 00639 // as all categories will be the same... 00640 $catpath = substr($catpath,1); 00641 $catpath = preg_replace(':/\d+$:','',$catpath); 00642 $catctxids = str_replace('/',',',$catpath); 00643 } 00644 if ($categoryclause !== '') { 00645 $categoryclause = "AND $categoryclause"; 00646 } 00647 /* 00648 * Note: Here we use a LEFT OUTER JOIN that can 00649 * "optionally" match to avoid passing a ton of context 00650 * ids in an IN() clause. Perhaps a subselect is faster. 00651 * 00652 * In any case, this SQL is not-so-nice over large sets of 00653 * courses with no $categoryclause. 00654 * 00655 */ 00656 $sql = "SELECT ctx.path, ctx.instanceid, ctx.contextlevel, 00657 r.id AS roleid, r.name as rolename, 00658 u.id AS userid, u.firstname, u.lastname 00659 FROM {role_assignments} ra 00660 JOIN {context} ctx ON ra.contextid = ctx.id 00661 JOIN {user} u ON ra.userid = u.id 00662 JOIN {role} r ON ra.roleid = r.id 00663 LEFT OUTER JOIN {course} c 00664 ON (ctx.instanceid=c.id AND ctx.contextlevel=".CONTEXT_COURSE.") 00665 WHERE ( c.id IS NOT NULL"; 00666 // under certain conditions, $catctxids is NULL 00667 if($catctxids == NULL){ 00668 $sql .= ") "; 00669 }else{ 00670 $sql .= " OR ra.contextid IN ($catctxids) )"; 00671 } 00672 00673 $sql .= "AND ra.roleid IN ({$CFG->coursecontact}) 00674 $categoryclause 00675 ORDER BY r.sortorder ASC, ctx.contextlevel ASC, ra.sortorder ASC"; 00676 $rs = $DB->get_recordset_sql($sql, $params); 00677 00678 // This loop is fairly stupid as it stands - might get better 00679 // results doing an initial pass clustering RAs by path. 00680 foreach($rs as $ra) { 00681 $user = new stdClass; 00682 $user->id = $ra->userid; unset($ra->userid); 00683 $user->firstname = $ra->firstname; unset($ra->firstname); 00684 $user->lastname = $ra->lastname; unset($ra->lastname); 00685 $ra->user = $user; 00686 if ($ra->contextlevel == CONTEXT_SYSTEM) { 00687 foreach ($courses as $k => $course) { 00688 $courses[$k]->managers[] = $ra; 00689 } 00690 } else if ($ra->contextlevel == CONTEXT_COURSECAT) { 00691 if ($allcats === false) { 00692 // It always applies 00693 foreach ($courses as $k => $course) { 00694 $courses[$k]->managers[] = $ra; 00695 } 00696 } else { 00697 foreach ($courses as $k => $course) { 00698 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); 00699 // Note that strpos() returns 0 as "matched at pos 0" 00700 if (strpos($coursecontext->path, $ra->path.'/') === 0) { 00701 // Only add it to subpaths 00702 $courses[$k]->managers[] = $ra; 00703 } 00704 } 00705 } 00706 } else { // course-level 00707 if (!array_key_exists($ra->instanceid, $courses)) { 00708 //this course is not in a list, probably a frontpage course 00709 continue; 00710 } 00711 $courses[$ra->instanceid]->managers[] = $ra; 00712 } 00713 } 00714 $rs->close(); 00715 } 00716 00717 return $courses; 00718 } 00719 00732 function get_courses_search($searchterms, $sort='fullname ASC', $page=0, $recordsperpage=50, &$totalcount) { 00733 global $CFG, $DB; 00734 00735 if ($DB->sql_regex_supported()) { 00736 $REGEXP = $DB->sql_regex(true); 00737 $NOTREGEXP = $DB->sql_regex(false); 00738 } 00739 00740 $searchcond = array(); 00741 $params = array(); 00742 $i = 0; 00743 00744 // Thanks Oracle for your non-ansi concat and type limits in coalesce. MDL-29912 00745 if ($DB->get_dbfamily() == 'oracle') { 00746 $concat = $DB->sql_concat('c.summary', "' '", 'c.fullname', "' '", 'c.idnumber', "' '", 'c.shortname'); 00747 } else { 00748 $concat = $DB->sql_concat("COALESCE(c.summary, '". $DB->sql_empty() ."')", "' '", 'c.fullname', "' '", 'c.idnumber', "' '", 'c.shortname'); 00749 } 00750 00751 foreach ($searchterms as $searchterm) { 00752 $i++; 00753 00754 $NOT = false; 00755 00756 00759 if (!$DB->sql_regex_supported()) { 00760 if (substr($searchterm, 0, 1) == '-') { 00761 $NOT = true; 00762 } 00763 $searchterm = trim($searchterm, '+-'); 00764 } 00765 00766 // TODO: +- may not work for non latin languages 00767 00768 if (substr($searchterm,0,1) == '+') { 00769 $searchterm = trim($searchterm, '+-'); 00770 $searchterm = preg_quote($searchterm, '|'); 00771 $searchcond[] = "$concat $REGEXP :ss$i"; 00772 $params['ss'.$i] = "(^|[^a-zA-Z0-9])$searchterm([^a-zA-Z0-9]|$)"; 00773 00774 } else if (substr($searchterm,0,1) == "-") { 00775 $searchterm = trim($searchterm, '+-'); 00776 $searchterm = preg_quote($searchterm, '|'); 00777 $searchcond[] = "$concat $NOTREGEXP :ss$i"; 00778 $params['ss'.$i] = "(^|[^a-zA-Z0-9])$searchterm([^a-zA-Z0-9]|$)"; 00779 00780 } else { 00781 $searchcond[] = $DB->sql_like($concat,":ss$i", false, true, $NOT); 00782 $params['ss'.$i] = "%$searchterm%"; 00783 } 00784 } 00785 00786 if (empty($searchcond)) { 00787 $totalcount = 0; 00788 return array(); 00789 } 00790 00791 $searchcond = implode(" AND ", $searchcond); 00792 00793 $courses = array(); 00794 $c = 0; // counts how many visible courses we've seen 00795 00796 // Tiki pagination 00797 $limitfrom = $page * $recordsperpage; 00798 $limitto = $limitfrom + $recordsperpage; 00799 00800 list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx'); 00801 $sql = "SELECT c.* $ccselect 00802 FROM {course} c 00803 $ccjoin 00804 WHERE $searchcond AND c.id <> ".SITEID." 00805 ORDER BY $sort"; 00806 00807 $rs = $DB->get_recordset_sql($sql, $params); 00808 foreach($rs as $course) { 00809 context_instance_preload($course); 00810 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); 00811 if ($course->visible || has_capability('moodle/course:viewhiddencourses', $coursecontext)) { 00812 // Don't exit this loop till the end 00813 // we need to count all the visible courses 00814 // to update $totalcount 00815 if ($c >= $limitfrom && $c < $limitto) { 00816 $courses[$course->id] = $course; 00817 } 00818 $c++; 00819 } 00820 } 00821 $rs->close(); 00822 00823 // our caller expects 2 bits of data - our return 00824 // array, and an updated $totalcount 00825 $totalcount = $c; 00826 return $courses; 00827 } 00828 00829 00846 function get_categories($parent='none', $sort=NULL, $shallow=true) { 00847 global $DB; 00848 00849 if ($sort === NULL) { 00850 $sort = 'ORDER BY cc.sortorder ASC'; 00851 } elseif ($sort ==='') { 00852 // leave it as empty 00853 } else { 00854 $sort = "ORDER BY $sort"; 00855 } 00856 00857 list($ccselect, $ccjoin) = context_instance_preload_sql('cc.id', CONTEXT_COURSECAT, 'ctx'); 00858 00859 if ($parent === 'none') { 00860 $sql = "SELECT cc.* $ccselect 00861 FROM {course_categories} cc 00862 $ccjoin 00863 $sort"; 00864 $params = array(); 00865 00866 } elseif ($shallow) { 00867 $sql = "SELECT cc.* $ccselect 00868 FROM {course_categories} cc 00869 $ccjoin 00870 WHERE cc.parent=? 00871 $sort"; 00872 $params = array($parent); 00873 00874 } else { 00875 $sql = "SELECT cc.* $ccselect 00876 FROM {course_categories} cc 00877 $ccjoin 00878 JOIN {course_categories} ccp 00879 ON ((cc.parent = ccp.id) OR (cc.path LIKE ".$DB->sql_concat('ccp.path',"'/%'").")) 00880 WHERE ccp.id=? 00881 $sort"; 00882 $params = array($parent); 00883 } 00884 $categories = array(); 00885 00886 $rs = $DB->get_recordset_sql($sql, $params); 00887 foreach($rs as $cat) { 00888 context_instance_preload($cat); 00889 $catcontext = get_context_instance(CONTEXT_COURSECAT, $cat->id); 00890 if ($cat->visible || has_capability('moodle/category:viewhiddencategories', $catcontext)) { 00891 $categories[$cat->id] = $cat; 00892 } 00893 } 00894 $rs->close(); 00895 return $categories; 00896 } 00897 00898 00907 function get_all_subcategories($catid) { 00908 global $DB; 00909 00910 $subcats = array(); 00911 00912 if ($categories = $DB->get_records('course_categories', array('parent'=>$catid))) { 00913 foreach ($categories as $cat) { 00914 array_push($subcats, $cat->id); 00915 $subcats = array_merge($subcats, get_all_subcategories($cat->id)); 00916 } 00917 } 00918 return $subcats; 00919 } 00920 00931 function get_course_category($catid=0) { 00932 global $DB; 00933 00934 $category = false; 00935 00936 if (!empty($catid)) { 00937 $category = $DB->get_record('course_categories', array('id'=>$catid)); 00938 } 00939 00940 if (!$category) { 00941 // the first category is considered default for now 00942 if ($category = $DB->get_records('course_categories', null, 'sortorder', '*', 0, 1)) { 00943 $category = reset($category); 00944 00945 } else { 00946 $cat = new stdClass(); 00947 $cat->name = get_string('miscellaneous'); 00948 $cat->depth = 1; 00949 $cat->sortorder = MAX_COURSES_IN_CATEGORY; 00950 $cat->timemodified = time(); 00951 $catid = $DB->insert_record('course_categories', $cat); 00952 // make sure category context exists 00953 get_context_instance(CONTEXT_COURSECAT, $catid); 00954 mark_context_dirty('/'.SYSCONTEXTID); 00955 fix_course_sortorder(); // Required to build course_categories.depth and .path. 00956 $category = $DB->get_record('course_categories', array('id'=>$catid)); 00957 } 00958 } 00959 00960 return $category; 00961 } 00962 00975 function fix_course_sortorder() { 00976 global $DB, $SITE; 00977 00978 //WARNING: this is PHP5 only code! 00979 00980 if ($unsorted = $DB->get_records('course_categories', array('sortorder'=>0))) { 00981 //move all categories that are not sorted yet to the end 00982 $DB->set_field('course_categories', 'sortorder', MAX_COURSES_IN_CATEGORY*MAX_COURSE_CATEGORIES, array('sortorder'=>0)); 00983 } 00984 00985 $allcats = $DB->get_records('course_categories', null, 'sortorder, id', 'id, sortorder, parent, depth, path'); 00986 $topcats = array(); 00987 $brokencats = array(); 00988 foreach ($allcats as $cat) { 00989 $sortorder = (int)$cat->sortorder; 00990 if (!$cat->parent) { 00991 while(isset($topcats[$sortorder])) { 00992 $sortorder++; 00993 } 00994 $topcats[$sortorder] = $cat; 00995 continue; 00996 } 00997 if (!isset($allcats[$cat->parent])) { 00998 $brokencats[] = $cat; 00999 continue; 01000 } 01001 if (!isset($allcats[$cat->parent]->children)) { 01002 $allcats[$cat->parent]->children = array(); 01003 } 01004 while(isset($allcats[$cat->parent]->children[$sortorder])) { 01005 $sortorder++; 01006 } 01007 $allcats[$cat->parent]->children[$sortorder] = $cat; 01008 } 01009 unset($allcats); 01010 01011 // add broken cats to category tree 01012 if ($brokencats) { 01013 $defaultcat = reset($topcats); 01014 foreach ($brokencats as $cat) { 01015 $topcats[] = $cat; 01016 } 01017 } 01018 01019 // now walk recursively the tree and fix any problems found 01020 $sortorder = 0; 01021 $fixcontexts = array(); 01022 _fix_course_cats($topcats, $sortorder, 0, 0, '', $fixcontexts); 01023 01024 // detect if there are "multiple" frontpage courses and fix them if needed 01025 $frontcourses = $DB->get_records('course', array('category'=>0), 'id'); 01026 if (count($frontcourses) > 1) { 01027 if (isset($frontcourses[SITEID])) { 01028 $frontcourse = $frontcourses[SITEID]; 01029 unset($frontcourses[SITEID]); 01030 } else { 01031 $frontcourse = array_shift($frontcourses); 01032 } 01033 $defaultcat = reset($topcats); 01034 foreach ($frontcourses as $course) { 01035 $DB->set_field('course', 'category', $defaultcat->id, array('id'=>$course->id)); 01036 $context = get_context_instance(CONTEXT_COURSE, $course->id); 01037 $fixcontexts[$context->id] = $context; 01038 } 01039 unset($frontcourses); 01040 } else { 01041 $frontcourse = reset($frontcourses); 01042 } 01043 01044 // now fix the paths and depths in context table if needed 01045 if ($fixcontexts) { 01046 foreach ($fixcontexts as $fixcontext) { 01047 $fixcontext->reset_paths(false); 01048 } 01049 context_helper::build_all_paths(false); 01050 unset($fixcontexts); 01051 } 01052 01053 // release memory 01054 unset($topcats); 01055 unset($brokencats); 01056 unset($fixcontexts); 01057 01058 // fix frontpage course sortorder 01059 if ($frontcourse->sortorder != 1) { 01060 $DB->set_field('course', 'sortorder', 1, array('id'=>$frontcourse->id)); 01061 } 01062 01063 // now fix the course counts in category records if needed 01064 $sql = "SELECT cc.id, cc.coursecount, COUNT(c.id) AS newcount 01065 FROM {course_categories} cc 01066 LEFT JOIN {course} c ON c.category = cc.id 01067 GROUP BY cc.id, cc.coursecount 01068 HAVING cc.coursecount <> COUNT(c.id)"; 01069 01070 if ($updatecounts = $DB->get_records_sql($sql)) { 01071 // categories with more courses than MAX_COURSES_IN_CATEGORY 01072 $categories = array(); 01073 foreach ($updatecounts as $cat) { 01074 $cat->coursecount = $cat->newcount; 01075 if ($cat->coursecount >= MAX_COURSES_IN_CATEGORY) { 01076 $categories[] = $cat->id; 01077 } 01078 unset($cat->newcount); 01079 $DB->update_record_raw('course_categories', $cat, true); 01080 } 01081 if (!empty($categories)) { 01082 $str = implode(', ', $categories); 01083 debugging("The number of courses (category id: $str) has reached MAX_COURSES_IN_CATEGORY (" . MAX_COURSES_IN_CATEGORY . "), it will cause a sorting performance issue, please increase the value of MAX_COURSES_IN_CATEGORY in lib/datalib.php file. See tracker issue: MDL-25669", DEBUG_DEVELOPER); 01084 } 01085 } 01086 01087 // now make sure that sortorders in course table are withing the category sortorder ranges 01088 $sql = "SELECT DISTINCT cc.id, cc.sortorder 01089 FROM {course_categories} cc 01090 JOIN {course} c ON c.category = cc.id 01091 WHERE c.sortorder < cc.sortorder OR c.sortorder > cc.sortorder + ".MAX_COURSES_IN_CATEGORY; 01092 01093 if ($fixcategories = $DB->get_records_sql($sql)) { 01094 //fix the course sortorder ranges 01095 foreach ($fixcategories as $cat) { 01096 $sql = "UPDATE {course} 01097 SET sortorder = ".$DB->sql_modulo('sortorder', MAX_COURSES_IN_CATEGORY)." + ? 01098 WHERE category = ?"; 01099 $DB->execute($sql, array($cat->sortorder, $cat->id)); 01100 } 01101 } 01102 unset($fixcategories); 01103 01104 // categories having courses with sortorder duplicates or having gaps in sortorder 01105 $sql = "SELECT DISTINCT c1.category AS id , cc.sortorder 01106 FROM {course} c1 01107 JOIN {course} c2 ON c1.sortorder = c2.sortorder 01108 JOIN {course_categories} cc ON (c1.category = cc.id) 01109 WHERE c1.id <> c2.id"; 01110 $fixcategories = $DB->get_records_sql($sql); 01111 01112 $sql = "SELECT cc.id, cc.sortorder, cc.coursecount, MAX(c.sortorder) AS maxsort, MIN(c.sortorder) AS minsort 01113 FROM {course_categories} cc 01114 JOIN {course} c ON c.category = cc.id 01115 GROUP BY cc.id, cc.sortorder, cc.coursecount 01116 HAVING (MAX(c.sortorder) <> cc.sortorder + cc.coursecount) OR (MIN(c.sortorder) <> cc.sortorder + 1)"; 01117 $gapcategories = $DB->get_records_sql($sql); 01118 01119 foreach ($gapcategories as $cat) { 01120 if (isset($fixcategories[$cat->id])) { 01121 // duplicates detected already 01122 01123 } else if ($cat->minsort == $cat->sortorder and $cat->maxsort == $cat->sortorder + $cat->coursecount - 1) { 01124 // easy - new course inserted with sortorder 0, the rest is ok 01125 $sql = "UPDATE {course} 01126 SET sortorder = sortorder + 1 01127 WHERE category = ?"; 01128 $DB->execute($sql, array($cat->id)); 01129 01130 } else { 01131 // it needs full resorting 01132 $fixcategories[$cat->id] = $cat; 01133 } 01134 } 01135 unset($gapcategories); 01136 01137 // fix course sortorders in problematic categories only 01138 foreach ($fixcategories as $cat) { 01139 $i = 1; 01140 $courses = $DB->get_records('course', array('category'=>$cat->id), 'sortorder ASC, id DESC', 'id, sortorder'); 01141 foreach ($courses as $course) { 01142 if ($course->sortorder != $cat->sortorder + $i) { 01143 $course->sortorder = $cat->sortorder + $i; 01144 $DB->update_record_raw('course', $course, true); 01145 } 01146 $i++; 01147 } 01148 } 01149 } 01150 01167 function _fix_course_cats($children, &$sortorder, $parent, $depth, $path, &$fixcontexts) { 01168 global $DB; 01169 01170 $depth++; 01171 01172 foreach ($children as $cat) { 01173 $sortorder = $sortorder + MAX_COURSES_IN_CATEGORY; 01174 $update = false; 01175 if ($parent != $cat->parent or $depth != $cat->depth or $path.'/'.$cat->id != $cat->path) { 01176 $cat->parent = $parent; 01177 $cat->depth = $depth; 01178 $cat->path = $path.'/'.$cat->id; 01179 $update = true; 01180 01181 // make sure context caches are rebuild and dirty contexts marked 01182 $context = get_context_instance(CONTEXT_COURSECAT, $cat->id); 01183 $fixcontexts[$context->id] = $context; 01184 } 01185 if ($cat->sortorder != $sortorder) { 01186 $cat->sortorder = $sortorder; 01187 $update = true; 01188 } 01189 if ($update) { 01190 $DB->update_record('course_categories', $cat, true); 01191 } 01192 if (isset($cat->children)) { 01193 _fix_course_cats($cat->children, $sortorder, $cat->id, $cat->depth, $cat->path, $fixcontexts); 01194 } 01195 } 01196 } 01197 01207 function get_my_remotecourses($userid=0) { 01208 global $DB, $USER; 01209 01210 if (empty($userid)) { 01211 $userid = $USER->id; 01212 } 01213 01214 // we can not use SELECT DISTINCT + text field (summary) because of MS SQL and Oracle, subselect used therefore 01215 $sql = "SELECT c.id, c.remoteid, c.shortname, c.fullname, 01216 c.hostid, c.summary, c.summaryformat, c.categoryname AS cat_name, 01217 h.name AS hostname 01218 FROM {mnetservice_enrol_courses} c 01219 JOIN (SELECT DISTINCT hostid, remotecourseid 01220 FROM {mnetservice_enrol_enrolments} 01221 WHERE userid = ? 01222 ) e ON (e.hostid = c.hostid AND e.remotecourseid = c.remoteid) 01223 JOIN {mnet_host} h ON h.id = c.hostid"; 01224 01225 return $DB->get_records_sql($sql, array($userid)); 01226 } 01227 01236 function get_my_remotehosts() { 01237 global $CFG, $USER; 01238 01239 if ($USER->mnethostid == $CFG->mnet_localhost_id) { 01240 return false; // Return nothing on the IDP 01241 } 01242 if (!empty($USER->mnet_foreign_host_array) && is_array($USER->mnet_foreign_host_array)) { 01243 return $USER->mnet_foreign_host_array; 01244 } 01245 return false; 01246 } 01247 01260 function make_default_scale() { 01261 global $DB; 01262 01263 $defaultscale = new stdClass(); 01264 $defaultscale->courseid = 0; 01265 $defaultscale->userid = 0; 01266 $defaultscale->name = get_string('separateandconnected'); 01267 $defaultscale->description = get_string('separateandconnectedinfo'); 01268 $defaultscale->scale = get_string('postrating1', 'forum').','. 01269 get_string('postrating2', 'forum').','. 01270 get_string('postrating3', 'forum'); 01271 $defaultscale->timemodified = time(); 01272 01273 $defaultscale->id = $DB->insert_record('scale', $defaultscale); 01274 $DB->execute("UPDATE {forum} SET scale = ?", array($defaultscale->id)); 01275 } 01276 01277 01285 function get_scales_menu($courseid=0) { 01286 global $DB; 01287 01288 $sql = "SELECT id, name 01289 FROM {scale} 01290 WHERE courseid = 0 or courseid = ? 01291 ORDER BY courseid ASC, name ASC"; 01292 $params = array($courseid); 01293 01294 if ($scales = $DB->get_records_sql_menu($sql, $params)) { 01295 return $scales; 01296 } 01297 01298 make_default_scale(); 01299 01300 return $DB->get_records_sql_menu($sql, $params); 01301 } 01302 01303 01304 01312 function update_timezone_records($timezones) { 01313 global $DB; 01314 01316 $DB->delete_records('timezone'); 01317 01319 foreach ($timezones as $timezone) { 01320 if (is_array($timezone)) { 01321 $timezone = (object)$timezone; 01322 } 01323 $DB->insert_record('timezone', $timezone); 01324 } 01325 } 01326 01327 01329 01337 function get_course_mods($courseid) { 01338 global $DB; 01339 01340 if (empty($courseid)) { 01341 return false; // avoid warnings 01342 } 01343 01344 return $DB->get_records_sql("SELECT cm.*, m.name as modname 01345 FROM {modules} m, {course_modules} cm 01346 WHERE cm.course = ? AND cm.module = m.id AND m.visible = 1", 01347 array($courseid)); // no disabled mods 01348 } 01349 01350 01364 function get_coursemodule_from_id($modulename, $cmid, $courseid=0, $sectionnum=false, $strictness=IGNORE_MISSING) { 01365 global $DB; 01366 01367 $params = array('cmid'=>$cmid); 01368 01369 if (!$modulename) { 01370 if (!$modulename = $DB->get_field_sql("SELECT md.name 01371 FROM {modules} md 01372 JOIN {course_modules} cm ON cm.module = md.id 01373 WHERE cm.id = :cmid", $params, $strictness)) { 01374 return false; 01375 } 01376 } 01377 01378 $params['modulename'] = $modulename; 01379 01380 $courseselect = ""; 01381 $sectionfield = ""; 01382 $sectionjoin = ""; 01383 01384 if ($courseid) { 01385 $courseselect = "AND cm.course = :courseid"; 01386 $params['courseid'] = $courseid; 01387 } 01388 01389 if ($sectionnum) { 01390 $sectionfield = ", cw.section AS sectionnum"; 01391 $sectionjoin = "LEFT JOIN {course_sections} cw ON cw.id = cm.section"; 01392 } 01393 01394 $sql = "SELECT cm.*, m.name, md.name AS modname $sectionfield 01395 FROM {course_modules} cm 01396 JOIN {modules} md ON md.id = cm.module 01397 JOIN {".$modulename."} m ON m.id = cm.instance 01398 $sectionjoin 01399 WHERE cm.id = :cmid AND md.name = :modulename 01400 $courseselect"; 01401 01402 return $DB->get_record_sql($sql, $params, $strictness); 01403 } 01404 01418 function get_coursemodule_from_instance($modulename, $instance, $courseid=0, $sectionnum=false, $strictness=IGNORE_MISSING) { 01419 global $DB; 01420 01421 $params = array('instance'=>$instance, 'modulename'=>$modulename); 01422 01423 $courseselect = ""; 01424 $sectionfield = ""; 01425 $sectionjoin = ""; 01426 01427 if ($courseid) { 01428 $courseselect = "AND cm.course = :courseid"; 01429 $params['courseid'] = $courseid; 01430 } 01431 01432 if ($sectionnum) { 01433 $sectionfield = ", cw.section AS sectionnum"; 01434 $sectionjoin = "LEFT JOIN {course_sections} cw ON cw.id = cm.section"; 01435 } 01436 01437 $sql = "SELECT cm.*, m.name, md.name AS modname $sectionfield 01438 FROM {course_modules} cm 01439 JOIN {modules} md ON md.id = cm.module 01440 JOIN {".$modulename."} m ON m.id = cm.instance 01441 $sectionjoin 01442 WHERE m.id = :instance AND md.name = :modulename 01443 $courseselect"; 01444 01445 return $DB->get_record_sql($sql, $params, $strictness); 01446 } 01447 01456 function get_coursemodules_in_course($modulename, $courseid, $extrafields='') { 01457 global $DB; 01458 01459 if (!empty($extrafields)) { 01460 $extrafields = ", $extrafields"; 01461 } 01462 $params = array(); 01463 $params['courseid'] = $courseid; 01464 $params['modulename'] = $modulename; 01465 01466 01467 return $DB->get_records_sql("SELECT cm.*, m.name, md.name as modname $extrafields 01468 FROM {course_modules} cm, {modules} md, {".$modulename."} m 01469 WHERE cm.course = :courseid AND 01470 cm.instance = m.id AND 01471 md.name = :modulename AND 01472 md.id = cm.module", $params); 01473 } 01474 01494 function get_all_instances_in_courses($modulename, $courses, $userid=NULL, $includeinvisible=false) { 01495 global $CFG, $DB; 01496 01497 $outputarray = array(); 01498 01499 if (empty($courses) || !is_array($courses) || count($courses) == 0) { 01500 return $outputarray; 01501 } 01502 01503 list($coursessql, $params) = $DB->get_in_or_equal(array_keys($courses), SQL_PARAMS_NAMED, 'c0'); 01504 $params['modulename'] = $modulename; 01505 01506 if (!$rawmods = $DB->get_records_sql("SELECT cm.id AS coursemodule, m.*, cw.section, cm.visible AS visible, 01507 cm.groupmode, cm.groupingid, cm.groupmembersonly 01508 FROM {course_modules} cm, {course_sections} cw, {modules} md, 01509 {".$modulename."} m 01510 WHERE cm.course $coursessql AND 01511 cm.instance = m.id AND 01512 cm.section = cw.id AND 01513 md.name = :modulename AND 01514 md.id = cm.module", $params)) { 01515 return $outputarray; 01516 } 01517 01518 foreach ($courses as $course) { 01519 $modinfo = get_fast_modinfo($course, $userid); 01520 01521 if (empty($modinfo->instances[$modulename])) { 01522 continue; 01523 } 01524 01525 foreach ($modinfo->instances[$modulename] as $cm) { 01526 if (!$includeinvisible and !$cm->uservisible) { 01527 continue; 01528 } 01529 if (!isset($rawmods[$cm->id])) { 01530 continue; 01531 } 01532 $instance = $rawmods[$cm->id]; 01533 if (!empty($cm->extra)) { 01534 $instance->extra = $cm->extra; 01535 } 01536 $outputarray[] = $instance; 01537 } 01538 } 01539 01540 return $outputarray; 01541 } 01542 01563 function get_all_instances_in_course($modulename, $course, $userid=NULL, $includeinvisible=false) { 01564 return get_all_instances_in_courses($modulename, array($course->id => $course), $userid, $includeinvisible); 01565 } 01566 01567 01581 function instance_is_visible($moduletype, $module) { 01582 global $DB; 01583 01584 if (!empty($module->id)) { 01585 $params = array('courseid'=>$module->course, 'moduletype'=>$moduletype, 'moduleid'=>$module->id); 01586 if ($records = $DB->get_records_sql("SELECT cm.instance, cm.visible, cm.groupingid, cm.id, cm.groupmembersonly, cm.course 01587 FROM {course_modules} cm, {modules} m 01588 WHERE cm.course = :courseid AND 01589 cm.module = m.id AND 01590 m.name = :moduletype AND 01591 cm.instance = :moduleid", $params)) { 01592 01593 foreach ($records as $record) { // there should only be one - use the first one 01594 return $record->visible; 01595 } 01596 } 01597 } 01598 return true; // visible by default! 01599 } 01600 01614 function coursemodule_visible_for_user($cm, $userid=0) { 01615 global $USER,$CFG; 01616 01617 if (empty($cm->id)) { 01618 debugging("Incorrect course module parameter!", DEBUG_DEVELOPER); 01619 return false; 01620 } 01621 if (empty($userid)) { 01622 $userid = $USER->id; 01623 } 01624 if (!$cm->visible and !has_capability('moodle/course:viewhiddenactivities', get_context_instance(CONTEXT_MODULE, $cm->id), $userid)) { 01625 return false; 01626 } 01627 if ($CFG->enableavailability) { 01628 require_once($CFG->libdir.'/conditionlib.php'); 01629 $ci=new condition_info($cm,CONDITION_MISSING_EXTRATABLE); 01630 if(!$ci->is_available($cm->availableinfo,false,$userid) and 01631 !has_capability('moodle/course:viewhiddenactivities', 01632 get_context_instance(CONTEXT_MODULE, $cm->id), $userid)) { 01633 return false; 01634 } 01635 } 01636 return groups_course_module_visible($cm, $userid); 01637 } 01638 01639 01640 01641 01643 01644 01667 function add_to_log($courseid, $module, $action, $url='', $info='', $cm=0, $user=0) { 01668 // Note that this function intentionally does not follow the normal Moodle DB access idioms. 01669 // This is for a good reason: it is the most frequently used DB update function, 01670 // so it has been optimised for speed. 01671 global $DB, $CFG, $USER; 01672 01673 if ($cm === '' || is_null($cm)) { // postgres won't translate empty string to its default 01674 $cm = 0; 01675 } 01676 01677 if ($user) { 01678 $userid = $user; 01679 } else { 01680 if (session_is_loggedinas()) { // Don't log 01681 return; 01682 } 01683 $userid = empty($USER->id) ? '0' : $USER->id; 01684 } 01685 01686 if (isset($CFG->logguests) and !$CFG->logguests) { 01687 if (!$userid or isguestuser($userid)) { 01688 return; 01689 } 01690 } 01691 01692 $REMOTE_ADDR = getremoteaddr(); 01693 01694 $timenow = time(); 01695 $info = $info; 01696 if (!empty($url)) { // could break doing html_entity_decode on an empty var. 01697 $url = html_entity_decode($url); 01698 } else { 01699 $url = ''; 01700 } 01701 01702 // Restrict length of log lines to the space actually available in the 01703 // database so that it doesn't cause a DB error. Log a warning so that 01704 // developers can avoid doing things which are likely to cause this on a 01705 // routine basis. 01706 $tl = textlib_get_instance(); 01707 if(!empty($info) && $tl->strlen($info)>255) { 01708 $info = $tl->substr($info,0,252).'...'; 01709 debugging('Warning: logged very long info',DEBUG_DEVELOPER); 01710 } 01711 01712 // If the 100 field size is changed, also need to alter print_log in course/lib.php 01713 if(!empty($url) && $tl->strlen($url)>100) { 01714 $url=$tl->substr($url,0,97).'...'; 01715 debugging('Warning: logged very long URL',DEBUG_DEVELOPER); 01716 } 01717 01718 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->logwrites++;}; 01719 01720 $log = array('time'=>$timenow, 'userid'=>$userid, 'course'=>$courseid, 'ip'=>$REMOTE_ADDR, 'module'=>$module, 01721 'cmid'=>$cm, 'action'=>$action, 'url'=>$url, 'info'=>$info); 01722 01723 try { 01724 $DB->insert_record_raw('log', $log, false); 01725 } catch (dml_exception $e) { 01726 debugging('Error: Could not insert a new entry to the Moodle log', DEBUG_ALL); 01727 // MDL-11893, alert $CFG->supportemail if insert into log failed 01728 if ($CFG->supportemail and empty($CFG->noemailever)) { 01729 // email_to_user is not usable because email_to_user tries to write to the logs table, 01730 // and this will get caught in an infinite loop, if disk is full 01731 $site = get_site(); 01732 $subject = 'Insert into log failed at your moodle site '.$site->fullname; 01733 $message = "Insert into log table failed at ". date('l dS \of F Y h:i:s A') .".\n It is possible that your disk is full.\n\n"; 01734 $message .= "The failed query parameters are:\n\n" . var_export($log, true); 01735 01736 $lasttime = get_config('admin', 'lastloginserterrormail'); 01737 if(empty($lasttime) || time() - $lasttime > 60*60*24) { // limit to 1 email per day 01738 //using email directly rather than messaging as they may not be able to log in to access a message 01739 mail($CFG->supportemail, $subject, $message); 01740 set_config('lastloginserterrormail', time(), 'admin'); 01741 } 01742 } 01743 } 01744 } 01745 01757 function user_accesstime_log($courseid=0) { 01758 global $USER, $CFG, $DB; 01759 01760 if (!isloggedin() or session_is_loggedinas()) { 01761 // no access tracking 01762 return; 01763 } 01764 01765 if (empty($courseid)) { 01766 $courseid = SITEID; 01767 } 01768 01769 $timenow = time(); 01770 01772 if ($timenow - $USER->lastaccess > LASTACCESS_UPDATE_SECS) { 01774 $USER->lastaccess = $timenow; 01775 01776 $last = new stdClass(); 01777 $last->id = $USER->id; 01778 $last->lastip = getremoteaddr(); 01779 $last->lastaccess = $timenow; 01780 01781 $DB->update_record_raw('user', $last); 01782 } 01783 01784 if ($courseid == SITEID) { 01786 return; 01787 } 01788 01790 if (empty($USER->currentcourseaccess[$courseid]) or ($timenow - $USER->currentcourseaccess[$courseid] > LASTACCESS_UPDATE_SECS)) { 01791 01792 $lastaccess = $DB->get_field('user_lastaccess', 'timeaccess', array('userid'=>$USER->id, 'courseid'=>$courseid)); 01793 01794 if ($lastaccess === false) { 01795 // Update course lastaccess for next checks 01796 $USER->currentcourseaccess[$courseid] = $timenow; 01797 01798 $last = new stdClass(); 01799 $last->userid = $USER->id; 01800 $last->courseid = $courseid; 01801 $last->timeaccess = $timenow; 01802 $DB->insert_record_raw('user_lastaccess', $last, false); 01803 01804 } else if ($timenow - $lastaccess < LASTACCESS_UPDATE_SECS) { 01805 // no need to update now, it was updated recently in concurrent login ;-) 01806 01807 } else { 01808 // Update course lastaccess for next checks 01809 $USER->currentcourseaccess[$courseid] = $timenow; 01810 01811 $DB->set_field('user_lastaccess', 'timeaccess', $timenow, array('userid'=>$USER->id, 'courseid'=>$courseid)); 01812 } 01813 } 01814 } 01815 01830 function get_logs($select, array $params=null, $order='l.time DESC', $limitfrom='', $limitnum='', &$totalcount) { 01831 global $DB; 01832 01833 if ($order) { 01834 $order = "ORDER BY $order"; 01835 } 01836 01837 $selectsql = ""; 01838 $countsql = ""; 01839 01840 if ($select) { 01841 $select = "WHERE $select"; 01842 } 01843 01844 $sql = "SELECT COUNT(*) 01845 FROM {log} l 01846 $select"; 01847 01848 $totalcount = $DB->count_records_sql($sql, $params); 01849 01850 $sql = "SELECT l.*, u.firstname, u.lastname, u.picture 01851 FROM {log} l 01852 LEFT JOIN {user} u ON l.userid = u.id 01853 $select 01854 $order"; 01855 01856 return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum) ; 01857 } 01858 01859 01871 function get_logs_usercourse($userid, $courseid, $coursestart) { 01872 global $DB; 01873 01874 $params = array(); 01875 01876 $courseselect = ''; 01877 if ($courseid) { 01878 $courseselect = "AND course = :courseid"; 01879 $params['courseid'] = $courseid; 01880 } 01881 $params['userid'] = $userid; 01882 $$coursestart = (int)$coursestart; // note: unfortunately pg complains if you use name parameter or column alias in GROUP BY 01883 01884 return $DB->get_records_sql("SELECT FLOOR((time - $coursestart)/". DAYSECS .") AS day, COUNT(*) AS num 01885 FROM {log} 01886 WHERE userid = :userid 01887 AND time > $coursestart $courseselect 01888 GROUP BY FLOOR((time - $coursestart)/". DAYSECS .")", $params); 01889 } 01890 01901 function get_logs_userday($userid, $courseid, $daystart) { 01902 global $DB; 01903 01904 $params = array('userid'=>$userid); 01905 01906 $courseselect = ''; 01907 if ($courseid) { 01908 $courseselect = "AND course = :courseid"; 01909 $params['courseid'] = $courseid; 01910 } 01911 $daystart = (int)$daystart; // note: unfortunately pg complains if you use name parameter or column alias in GROUP BY 01912 01913 return $DB->get_records_sql("SELECT FLOOR((time - $daystart)/". HOURSECS .") AS hour, COUNT(*) AS num 01914 FROM {log} 01915 WHERE userid = :userid 01916 AND time > $daystart $courseselect 01917 GROUP BY FLOOR((time - $daystart)/". HOURSECS .") ", $params); 01918 } 01919 01935 function count_login_failures($mode, $username, $lastlogin) { 01936 global $DB; 01937 01938 $params = array('mode'=>$mode, 'username'=>$username, 'lastlogin'=>$lastlogin); 01939 $select = "module='login' AND action='error' AND time > :lastlogin"; 01940 01941 $count = new stdClass(); 01942 01943 if (is_siteadmin()) { 01944 if ($count->attempts = $DB->count_records_select('log', $select, $params)) { 01945 $count->accounts = $DB->count_records_select('log', $select, $params, 'COUNT(DISTINCT info)'); 01946 return $count; 01947 } 01948 } else if ($mode == 'everybody') { 01949 if ($count->attempts = $DB->count_records_select('log', "$select AND info = :username", $params)) { 01950 return $count; 01951 } 01952 } 01953 return NULL; 01954 } 01955 01956 01958 01969 function print_object($object) { 01970 01971 // we may need a lot of memory here 01972 raise_memory_limit(MEMORY_EXTRA); 01973 01974 if (CLI_SCRIPT) { 01975 fwrite(STDERR, print_r($object, true)); 01976 fwrite(STDERR, PHP_EOL); 01977 } else { 01978 echo html_writer::tag('pre', s(print_r($object, true)), array('class' => 'notifytiny')); 01979 } 01980 } 01981 01993 function xmldb_debug($message, $object) { 01994 01995 debugging($message, DEBUG_DEVELOPER); 01996 } 01997 02003 function user_can_create_courses() { 02004 global $DB; 02005 $catsrs = $DB->get_recordset('course_categories'); 02006 foreach ($catsrs as $cat) { 02007 if (has_capability('moodle/course:create', get_context_instance(CONTEXT_COURSECAT, $cat->id))) { 02008 $catsrs->close(); 02009 return true; 02010 } 02011 } 02012 $catsrs->close(); 02013 return false; 02014 }