removed foter login link during initial phase of installation
[moodle.git] / lib / datalib.php
CommitLineData
6078ba30 1<?php // $Id$
7cf1c7bd 2
3/**
4 * Library of functions for database manipulation.
5 *
7cf1c7bd 6 * Other main libraries:
7 * - weblib.php - functions that produce web output
8 * - moodlelib.php - general-purpose Moodle functions
6159ce65 9 * @author Martin Dougiamas and many others
7cf1c7bd 10 * @version $Id$
89dcb99d 11 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
7cf1c7bd 12 * @package moodlecore
13 */
14
df28d6c5 15
11a052a6 16/**
17 * Escape all dangerous characters in a data record
18 *
19 * $dataobject is an object containing needed data
20 * Run over each field exectuting addslashes() function
21 * to escape SQL unfriendly characters (e.g. quotes)
22 * Handy when writing back data read from the database
23 *
24 * @param $dataobject Object containing the database record
25 * @return object Same object with neccessary characters escaped
26 */
27function addslashes_object( $dataobject ) {
28 $a = get_object_vars( $dataobject);
29 foreach ($a as $key=>$value) {
30 $a[$key] = addslashes( $value );
31 }
32 return (object)$a;
33}
0892f7bd 34
df28d6c5 35/// USER DATABASE ////////////////////////////////////////////////
36
18a97fd8 37/**
fbc21ae8 38 * Returns $user object of the main admin user
20aeb4b8 39 * primary admin = admin with lowest role_assignment id among admins
fbc21ae8 40 * @uses $CFG
41 * @return object(admin) An associative array representing the admin user.
fbc21ae8 42 */
df28d6c5 43function get_admin () {
df28d6c5 44
45 global $CFG;
46
47 if ( $admins = get_admins() ) {
48 foreach ($admins as $admin) {
8f0cd6ef 49 return $admin; // ie the first one
df28d6c5 50 }
51 } else {
52 return false;
53 }
54}
55
18a97fd8 56/**
fbc21ae8 57 * Returns list of all admins
58 *
59 * @uses $CFG
7290c7fa 60 * @return object
fbc21ae8 61 */
df28d6c5 62function get_admins() {
df28d6c5 63
64 global $CFG;
20aeb4b8 65
66 $context = get_context_instance(CONTEXT_SYSTEM, SITEID);
df28d6c5 67
41f6ed56 68 return get_users_by_capability($context, 'moodle/site:doanything', 'u.*, ra.id as adminid', 'ra.id ASC'); // only need first one
20aeb4b8 69
df28d6c5 70}
71
72
b61efafb 73function get_courses_in_metacourse($metacourseid) {
74 global $CFG;
75
5f37b628 76 $sql = "SELECT c.id,c.shortname,c.fullname FROM {$CFG->prefix}course c, {$CFG->prefix}course_meta mc WHERE mc.parent_course = $metacourseid
5afa0de6 77 AND mc.child_course = c.id ORDER BY c.shortname";
b61efafb 78
79 return get_records_sql($sql);
80}
81
82function get_courses_notin_metacourse($metacourseid,$count=false) {
83
84 global $CFG;
85
b61efafb 86 if ($count) {
87 $sql = "SELECT COUNT(c.id)";
c44d5d42 88 } else {
b61efafb 89 $sql = "SELECT c.id,c.shortname,c.fullname";
90 }
178ccd11 91
ffed6bf3 92 $alreadycourses = get_courses_in_metacourse($metacourseid);
93
c44d5d42 94 $sql .= " FROM {$CFG->prefix}course c WHERE ".((!empty($alreadycourses)) ? "c.id NOT IN (".implode(',',array_keys($alreadycourses)).")
5afa0de6 95 AND " : "")." c.id !=$metacourseid and c.id != ".SITEID." and c.metacourse != 1 ".((empty($count)) ? " ORDER BY c.shortname" : "");
96
b61efafb 97 return get_records_sql($sql);
98}
99
900df8b6 100/**
fbc21ae8 101 * Search through course users
102 *
103 * If $coursid specifies the site course then this function searches
104 * through all undeleted and confirmed users
105 *
106 * @uses $CFG
107 * @uses SITEID
108 * @param int $courseid The course in question.
109 * @param int $groupid The group in question.
110 * @param string $searchtext ?
111 * @param string $sort ?
112 * @param string $exceptions ?
7290c7fa 113 * @return object
fbc21ae8 114 */
900df8b6 115function search_users($courseid, $groupid, $searchtext, $sort='', $exceptions='') {
116 global $CFG;
0720313b 117
29daf3a0 118 $LIKE = sql_ilike();
119 $fullname = sql_fullname('u.firstname', 'u.lastname');
8f0cd6ef 120
900df8b6 121 if (!empty($exceptions)) {
d4419d55 122 $except = ' AND u.id NOT IN ('. $exceptions .') ';
900df8b6 123 } else {
124 $except = '';
125 }
2700d113 126
900df8b6 127 if (!empty($sort)) {
d4419d55 128 $order = ' ORDER BY '. $sort;
900df8b6 129 } else {
130 $order = '';
131 }
8f0cd6ef 132
d4419d55 133 $select = 'u.deleted = \'0\' AND u.confirmed = \'1\'';
2700d113 134
222ac91b 135 if (!$courseid or $courseid == SITEID) {
2700d113 136 return get_records_sql("SELECT u.id, u.firstname, u.lastname, u.email
137 FROM {$CFG->prefix}user u
138 WHERE $select
900df8b6 139 AND ($fullname $LIKE '%$searchtext%' OR u.email $LIKE '%$searchtext%')
2700d113 140 $except $order");
8f0cd6ef 141 } else {
2700d113 142
900df8b6 143 if ($groupid) {
144 return get_records_sql("SELECT u.id, u.firstname, u.lastname, u.email
8f0cd6ef 145 FROM {$CFG->prefix}user u,
900df8b6 146 {$CFG->prefix}groups_members g
2700d113 147 WHERE $select AND g.groupid = '$groupid' AND g.userid = u.id
900df8b6 148 AND ($fullname $LIKE '%$searchtext%' OR u.email $LIKE '%$searchtext%')
149 $except $order");
150 } else {
ea8158c1 151 $context = get_context_instance(CONTEXT_COURSE, $courseid);
152 $contextlists = get_related_contexts_string($context);
153 $users = get_records_sql("SELECT u.id, u.firstname, u.lastname, u.email
8f0cd6ef 154 FROM {$CFG->prefix}user u,
ea8158c1 155 {$CFG->prefix}role_assignments ra
156 WHERE $select AND ra.contextid $contextlists AND ra.userid = u.id
900df8b6 157 AND ($fullname $LIKE '%$searchtext%' OR u.email $LIKE '%$searchtext%')
ea8158c1 158 $except $order");
900df8b6 159 }
ea8158c1 160 return $users;
900df8b6 161 }
df28d6c5 162}
163
2700d113 164
18a97fd8 165/**
fbc21ae8 166 * Returns a list of all site users
167 * Obsolete, just calls get_course_users(SITEID)
168 *
169 * @uses SITEID
c6d15803 170 * @deprecated Use {@link get_course_users()} instead.
fbc21ae8 171 * @param string $fields A comma separated list of fields to be returned from the chosen table.
7290c7fa 172 * @return object|false {@link $USER} records or false if error.
fbc21ae8 173 */
d4419d55 174function get_site_users($sort='u.lastaccess DESC', $fields='*', $exceptions='') {
2d0b30a0 175
65ee9c16 176 return get_course_users(SITEID, $sort, $exceptions, $fields);
2d0b30a0 177}
178
9fa49e22 179
18a97fd8 180/**
fbc21ae8 181 * Returns a subset of users
182 *
183 * @uses $CFG
7290c7fa 184 * @param bool $get If false then only a count of the records is returned
fbc21ae8 185 * @param string $search A simple string to search for
7290c7fa 186 * @param bool $confirmed A switch to allow/disallow unconfirmed users
fbc21ae8 187 * @param array(int) $exceptions A list of IDs to ignore, eg 2,4,5,8,9,10
188 * @param string $sort A SQL snippet for the sorting criteria to use
189 * @param string $firstinitial ?
190 * @param string $lastinitial ?
191 * @param string $page ?
192 * @param string $recordsperpage ?
193 * @param string $fields A comma separated list of fields to be returned from the chosen table.
7290c7fa 194 * @return object|false|int {@link $USER} records unless get is false in which case the integer count of the records found is returned. False is returned if an error is encountered.
fbc21ae8 195 */
d4419d55 196function get_users($get=true, $search='', $confirmed=false, $exceptions='', $sort='firstname ASC',
36075e09 197 $firstinitial='', $lastinitial='', $page='', $recordsperpage='', $fields='*') {
18a97fd8 198
199 global $CFG;
36075e09 200
201 if ($get && !$recordsperpage) {
202 debugging('Call to get_users with $get = true no $recordsperpage limit. ' .
203 'On large installations, this will probably cause an out of memory error. ' .
204 'Please think again and change your code so that it does not try to ' .
205 'load so much data into memory.', E_USER_WARNING);
206 }
18a97fd8 207
29daf3a0 208 $LIKE = sql_ilike();
209 $fullname = sql_fullname();
e384fb7b 210
d4419d55 211 $select = 'username <> \'guest\' AND deleted = 0';
488acd1b 212
0044147e 213 if (!empty($search)){
214 $search = trim($search);
488acd1b 215 $select .= " AND ($fullname $LIKE '%$search%' OR email $LIKE '%$search%') ";
e384fb7b 216 }
217
5a741655 218 if ($confirmed) {
d4419d55 219 $select .= ' AND confirmed = \'1\' ';
5a741655 220 }
221
222 if ($exceptions) {
d4419d55 223 $select .= ' AND id NOT IN ('. $exceptions .') ';
5a741655 224 }
225
488acd1b 226 if ($firstinitial) {
d4419d55 227 $select .= ' AND firstname '. $LIKE .' \''. $firstinitial .'%\'';
8f0cd6ef 228 }
488acd1b 229 if ($lastinitial) {
d4419d55 230 $select .= ' AND lastname '. $LIKE .' \''. $lastinitial .'%\'';
8f0cd6ef 231 }
488acd1b 232
5a741655 233 if ($get) {
36075e09 234 return get_records_select('user', $select, $sort, $fields, $page, $recordsperpage);
5a741655 235 } else {
36075e09 236 return count_records_select('user', $select);
5a741655 237 }
9fa49e22 238}
239
5a741655 240
18a97fd8 241/**
fbc21ae8 242 * shortdesc (optional)
243 *
244 * longdesc
245 *
246 * @uses $CFG
247 * @param string $sort ?
248 * @param string $dir ?
249 * @param int $categoryid ?
250 * @param int $categoryid ?
251 * @param string $search ?
252 * @param string $firstinitial ?
253 * @param string $lastinitial ?
7290c7fa 254 * @returnobject {@link $USER} records
fbc21ae8 255 * @todo Finish documenting this function
256 */
257
36075e09 258function get_users_listing($sort='lastaccess', $dir='ASC', $page=0, $recordsperpage=0,
7cf1c7bd 259 $search='', $firstinitial='', $lastinitial='') {
488acd1b 260
9fa49e22 261 global $CFG;
31fefa63 262
36075e09 263 if ($recordsperpage) {
264 $limit = sql_paging_limit($page, $recordsperpage);
265 } else {
266 $limit = '';
267 }
29daf3a0 268 $LIKE = sql_ilike();
269 $fullname = sql_fullname();
c2a96d6b 270
488acd1b 271 $select = 'deleted <> 1';
272
0044147e 273 if (!empty($search)) {
274 $search = trim($search);
488acd1b 275 $select .= " AND ($fullname $LIKE '%$search%' OR email $LIKE '%$search%') ";
276 }
277
278 if ($firstinitial) {
d4419d55 279 $select .= ' AND firstname '. $LIKE .' \''. $firstinitial .'%\' ';
488acd1b 280 }
281
282 if ($lastinitial) {
d4419d55 283 $select .= ' AND lastname '. $LIKE .' \''. $lastinitial .'%\' ';
c750592a 284 }
285
488acd1b 286 if ($sort) {
d4419d55 287 $sort = ' ORDER BY '. $sort .' '. $dir;
488acd1b 288 }
289
290/// warning: will return UNCONFIRMED USERS
ea5d48ee 291 return get_records_sql("SELECT id, username, email, firstname, lastname, city, country, lastaccess, confirmed
8f0cd6ef 292 FROM {$CFG->prefix}user
488acd1b 293 WHERE $select $sort $limit ");
9fa49e22 294
295}
296
488acd1b 297
18a97fd8 298/**
7290c7fa 299 * Full list of users that have confirmed their accounts.
fbc21ae8 300 *
301 * @uses $CFG
7290c7fa 302 * @return object
fbc21ae8 303 */
9fa49e22 304function get_users_confirmed() {
305 global $CFG;
8f0cd6ef 306 return get_records_sql("SELECT *
307 FROM {$CFG->prefix}user
308 WHERE confirmed = 1
9fa49e22 309 AND deleted = 0
8f0cd6ef 310 AND username <> 'guest'
9fa49e22 311 AND username <> 'changeme'");
312}
313
314
18a97fd8 315/**
7290c7fa 316 * Full list of users that have not yet confirmed their accounts.
fbc21ae8 317 *
318 * @uses $CFG
319 * @param string $cutofftime ?
7290c7fa 320 * @return object {@link $USER} records
fbc21ae8 321 */
99988d1a 322function get_users_unconfirmed($cutofftime=2000000000) {
9fa49e22 323 global $CFG;
8f0cd6ef 324 return get_records_sql("SELECT *
325 FROM {$CFG->prefix}user
9fa49e22 326 WHERE confirmed = 0
8f0cd6ef 327 AND firstaccess > 0
9fa49e22 328 AND firstaccess < '$cutofftime'");
329}
330
613bbd7c 331/**
332 * All users that we have not seen for a really long time (ie dead accounts)
333 *
334 * @uses $CFG
335 * @param string $cutofftime ?
336 * @return object {@link $USER} records
613bbd7c 337 */
338function get_users_longtimenosee($cutofftime) {
339 global $CFG;
340 return get_records_sql("SELECT DISTINCT *
341 FROM {$CFG->prefix}user
342 WHERE lastaccess > '0'
343 AND lastaccess < '$cutofftime' ");
344}
9fa49e22 345
fa22fd5f 346/**
347 * Full list of bogus accounts that are probably not ever going to be used
348 *
349 * @uses $CFG
350 * @param string $cutofftime ?
351 * @return object {@link $USER} records
fa22fd5f 352 */
353
354function get_users_not_fully_set_up($cutofftime=2000000000) {
355 global $CFG;
356 return get_records_sql("SELECT *
357 FROM {$CFG->prefix}user
358 WHERE confirmed = 1
359 AND lastaccess > 0
360 AND lastaccess < '$cutofftime'
361 AND deleted = 0
362 AND (lastname = '' OR firstname = '' OR email = '')");
363}
364
365
f374fb10 366/**
fbc21ae8 367 * Returns an array of group objects that the user is a member of
368 * in the given course. If userid isn't specified, then return a
369 * list of all groups in the course.
370 *
371 * @uses $CFG
89dcb99d 372 * @param int $courseid The id of the course in question.
fbc21ae8 373 * @param int $userid The id of the user in question as found in the 'user' table 'id' field.
7290c7fa 374 * @return object
fbc21ae8 375 */
f374fb10 376function get_groups($courseid, $userid=0) {
377 global $CFG;
378
379 if ($userid) {
d4419d55 380 $dbselect = ', '. $CFG->prefix .'groups_members m';
381 $userselect = 'AND m.groupid = g.id AND m.userid = \''. $userid .'\'';
2d439c9d 382 } else {
383 $dbselect = '';
384 $userselect = '';
f374fb10 385 }
386
94ef00f3 387 return get_records_sql("SELECT g.*
2d439c9d 388 FROM {$CFG->prefix}groups g $dbselect
f374fb10 389 WHERE g.courseid = '$courseid' $userselect ");
390}
391
392
393/**
613bbd7c 394 * Returns an array of user objects that belong to a given group
fbc21ae8 395 *
396 * @uses $CFG
397 * @param int $groupid The group in question.
398 * @param string $sort ?
399 * @param string $exceptions ?
7290c7fa 400 * @return object
fbc21ae8 401 */
49668367 402function get_group_users($groupid, $sort='u.lastaccess DESC', $exceptions='', $fields='u.*') {
f374fb10 403 global $CFG;
900df8b6 404 if (!empty($exceptions)) {
d4419d55 405 $except = ' AND u.id NOT IN ('. $exceptions .') ';
900df8b6 406 } else {
407 $except = '';
408 }
c1147b7e 409 // in postgres, you can't have things in sort that aren't in the select, so...
410 $extrafield = str_replace('ASC','',$sort);
d5efb299 411 $extrafield = str_replace('DESC','',$extrafield);
c1147b7e 412 $extrafield = trim($extrafield);
413 if (!empty($extrafield)) {
414 $extrafield = ','.$extrafield;
415 }
416 return get_records_sql("SELECT DISTINCT $fields $extrafield
f374fb10 417 FROM {$CFG->prefix}user u,
8f0cd6ef 418 {$CFG->prefix}groups_members m
f374fb10 419 WHERE m.groupid = '$groupid'
900df8b6 420 AND m.userid = u.id $except
2c4263c4 421 ORDER BY $sort");
f374fb10 422}
423
f374fb10 424/**
fbc21ae8 425 * Returns the user's group in a particular course
426 *
427 * @uses $CFG
428 * @param int $courseid The course in question.
429 * @param int $userid The id of the user as found in the 'user' table.
fa22fd5f 430 * @param int $groupid The id of the group the user is in.
7290c7fa 431 * @return object
fbc21ae8 432 */
f374fb10 433function user_group($courseid, $userid) {
434 global $CFG;
435
fa22fd5f 436 return get_records_sql("SELECT g.*
0da33e07 437 FROM {$CFG->prefix}groups g,
438 {$CFG->prefix}groups_members m
f374fb10 439 WHERE g.courseid = '$courseid'
440 AND g.id = m.groupid
fa22fd5f 441 AND m.userid = '$userid'
442 ORDER BY name ASC");
f374fb10 443}
444
445
9fa49e22 446
02ebf404 447
448/// OTHER SITE AND COURSE FUNCTIONS /////////////////////////////////////////////
449
450
18a97fd8 451/**
fbc21ae8 452 * Returns $course object of the top-level site.
453 *
89dcb99d 454 * @return course A {@link $COURSE} object for the site
fbc21ae8 455 */
c44d5d42 456function get_site() {
457
458 global $SITE;
459
460 if (!empty($SITE->id)) { // We already have a global to use, so return that
461 return $SITE;
462 }
02ebf404 463
c44d5d42 464 if ($course = get_record('course', 'category', 0)) {
02ebf404 465 return $course;
466 } else {
467 return false;
468 }
469}
470
18a97fd8 471/**
613bbd7c 472 * Returns list of courses, for whole site, or category
473 *
474 * Returns list of courses, for whole site, or category
475 * Important: Using c.* for fields is extremely expensive because
476 * we are using distinct. You almost _NEVER_ need all the fields
477 * in such a large SELECT
478 *
479 * @param type description
480 *
613bbd7c 481 */
6315b1c8 482function get_courses($categoryid="all", $sort="c.sortorder ASC", $fields="c.*") {
02ebf404 483
8ef9cb56 484 global $USER, $CFG;
6315b1c8 485
486 $categoryselect = "";
487 if ($categoryid != "all" && is_numeric($categoryid)) {
71dea306 488 $categoryselect = "WHERE c.category = '$categoryid'";
489 } else {
490 $categoryselect = "";
491 }
492
493 // pull out all course matching the cat
494 $courses = get_records_sql("SELECT $fields
495 FROM {$CFG->prefix}course c
496 $categoryselect
497 ORDER BY $sort");
498 $visiblecourses = array();
499
500 // loop throught them
501 foreach ($courses as $course) {
502 if ($course->visible <= 0) {
503 // for hidden courses, require visibility check
504 if (has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course->id))) {
505 $visiblecourses [] = $course;
506 }
507 } else {
508 $visiblecourses [] = $course;
509 }
6315b1c8 510 }
71dea306 511 return $visiblecourses;
6315b1c8 512
71dea306 513/*
6315b1c8 514 $teachertable = "";
515 $visiblecourses = "";
516 $sqland = "";
517 if (!empty($categoryselect)) {
518 $sqland = "AND ";
519 }
520 if (!empty($USER->id)) { // May need to check they are a teacher
ae9e4c06 521 if (!has_capability('moodle/course:create', get_context_instance(CONTEXT_SYSTEM, SITEID))) {
6315b1c8 522 $visiblecourses = "$sqland ((c.visible > 0) OR t.userid = '$USER->id')";
523 $teachertable = "LEFT JOIN {$CFG->prefix}user_teachers t ON t.course = c.id";
524 }
525 } else {
526 $visiblecourses = "$sqland c.visible > 0";
8ef9cb56 527 }
528
6315b1c8 529 if ($categoryselect or $visiblecourses) {
530 $selectsql = "{$CFG->prefix}course c $teachertable WHERE $categoryselect $visiblecourses";
14f32609 531 } else {
6315b1c8 532 $selectsql = "{$CFG->prefix}course c $teachertable";
14f32609 533 }
534
5b66416f 535 $extrafield = str_replace('ASC','',$sort);
536 $extrafield = str_replace('DESC','',$extrafield);
537 $extrafield = trim($extrafield);
538 if (!empty($extrafield)) {
539 $extrafield = ','.$extrafield;
540 }
541 return get_records_sql("SELECT ".((!empty($teachertable)) ? " DISTINCT " : "")." $fields $extrafield FROM $selectsql ".((!empty($sort)) ? "ORDER BY $sort" : ""));
71dea306 542 */
8130b77b 543}
544
8130b77b 545
6315b1c8 546/**
613bbd7c 547 * Returns list of courses, for whole site, or category
548 *
549 * Similar to get_courses, but allows paging
550 * Important: Using c.* for fields is extremely expensive because
551 * we are using distinct. You almost _NEVER_ need all the fields
552 * in such a large SELECT
553 *
554 * @param type description
555 *
613bbd7c 556 */
6315b1c8 557function get_courses_page($categoryid="all", $sort="c.sortorder ASC", $fields="c.*",
558 &$totalcount, $limitfrom="", $limitnum="") {
c7fe5c6f 559
8130b77b 560 global $USER, $CFG;
71dea306 561
562 $categoryselect = "";
563 if ($categoryid != "all" && is_numeric($categoryid)) {
564 $categoryselect = "WHERE c.category = '$categoryid'";
565 } else {
566 $categoryselect = "";
567 }
568
569 // pull out all course matching the cat
570 $courses = get_records_sql("SELECT $fields
571 FROM {$CFG->prefix}course c
572 $categoryselect
573 ORDER BY $sort");
574 $visiblecourses = array();
575 $totalcount = 0;
576
577 if (!$limitnum) {
578 $limitnum = count($courses);
579 }
580
581 if (!limitfrom) {
582 $limitfrom = 0;
583 }
584
585 // iteration will have to be done inside loop to keep track of the limitfrom and limitnum
586 foreach ($courses as $course) {
587 if ($course->visible <= 0) {
588 // for hidden courses, require visibility check
589 if (has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course->id))) {
590 $totalcount++;
591 if ($totalcount > $limitfrom && count($visiblecourses) < $limitnum) {
592 $visiblecourses [] = $course;
593 }
594 }
595 } else {
596 $totalcount++;
597 if ($totalcount > $limitfrom && count($visiblecourses) < $limitnum) {
598 $visiblecourses [] = $course;
599 }
600 }
601 }
602
603 return $visiblecourses;
604
605/**
8130b77b 606
6315b1c8 607 $categoryselect = "";
b565bbdf 608 if ($categoryid != "all" && is_numeric($categoryid)) {
6315b1c8 609 $categoryselect = "c.category = '$categoryid'";
8130b77b 610 }
611
6315b1c8 612 $teachertable = "";
613 $visiblecourses = "";
614 $sqland = "";
615 if (!empty($categoryselect)) {
616 $sqland = "AND ";
c7fe5c6f 617 }
2d2da684 618 if (!empty($USER) and !empty($USER->id)) { // May need to check they are a teacher
ae9e4c06 619 if (!has_capability('moodle/course:create', get_context_instance(CONTEXT_SYSTEM, SITEID))) {
6315b1c8 620 $visiblecourses = "$sqland ((c.visible > 0) OR t.userid = '$USER->id')";
621 $teachertable = "LEFT JOIN {$CFG->prefix}user_teachers t ON t.course=c.id";
622 }
8130b77b 623 } else {
6315b1c8 624 $visiblecourses = "$sqland c.visible > 0";
8130b77b 625 }
626
6315b1c8 627 if ($limitfrom !== "") {
29daf3a0 628 $limit = sql_paging_limit($limitfrom, $limitnum);
6315b1c8 629 } else {
630 $limit = "";
02ebf404 631 }
8ef9cb56 632
6315b1c8 633 $selectsql = "{$CFG->prefix}course c $teachertable WHERE $categoryselect $visiblecourses";
8ef9cb56 634
6315b1c8 635 $totalcount = count_records_sql("SELECT COUNT(DISTINCT c.id) FROM $selectsql");
8ef9cb56 636
2338ad32 637 return get_records_sql("SELECT $fields FROM $selectsql ".((!empty($sort)) ? "ORDER BY $sort" : "")." $limit");
71dea306 638 */
02ebf404 639}
640
641
18a97fd8 642/**
7290c7fa 643 * List of courses that a user is a member of.
fbc21ae8 644 *
645 * @uses $CFG
7290c7fa 646 * @param int $userid The user of interest
fbc21ae8 647 * @param string $sort ?
7290c7fa 648 * @return object {@link $COURSE} records
fbc21ae8 649 */
d4419d55 650function get_my_courses($userid, $sort='visible DESC,sortorder ASC') {
2f3499b7 651
7fb0fec7 652 global $CFG, $USER;
02ebf404 653
61b03dc7 654 $mycourses = array();
e521770c 655 $SQL = "SELECT * from {$CFG->prefix}course ORDER BY $sort";
61b03dc7 656 $courses = get_records_sql($SQL);
657
658 foreach ($courses as $course) {
659 if ($course->id != SITEID) {
660 // users with moodle/course:view are considered course participants
661 // the course needs to be visible, or user must have moodle/course:viewhiddencourses capability set to view
662 // hidden courses
663 if (has_capability('moodle/course:view', get_context_instance(CONTEXT_COURSE, $course->id), $userid)
664 && ($course->visible || has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course->id), $userid))) {
665 $mycourses[] = $course;
666 }
667 }
668 }
2f3499b7 669
61b03dc7 670 return $mycourses;
671/*
d4419d55 672 if ($students = get_records('user_students', 'userid', $userid, '', 'id, course')) {
2f3499b7 673 foreach ($students as $student) {
674 $course[$student->course] = $student->course;
675 }
676 }
152a9060 677 if (count($course) > 0 && empty($USER->admin)) {
678 if ($courses = get_records_list('course', 'id', implode(',', $course), '', 'id,visible')) {
fbcbd77c 679 foreach ($courses as $k => $c) {
152a9060 680 if (!$c->visible) {
fbcbd77c 681 unset($course[$c->id]);
682 }
683 }
7fb0fec7 684 }
685 }
152a9060 686
d4419d55 687 if ($teachers = get_records('user_teachers', 'userid', $userid, '', 'id, course')) {
2f3499b7 688 foreach ($teachers as $teacher) {
689 $course[$teacher->course] = $teacher->course;
690 }
691 }
152a9060 692
2f3499b7 693 if (empty($course)) {
694 return $course;
695 }
696
697 $courseids = implode(',', $course);
698
d4419d55 699 return get_records_list('course', 'id', $courseids, $sort);
61b03dc7 700*/
2f3499b7 701// The following is correct but VERY slow with large datasets
702//
8f0cd6ef 703// return get_records_sql("SELECT c.*
704// FROM {$CFG->prefix}course c,
705// {$CFG->prefix}user_students s,
706// {$CFG->prefix}user_teachers t
2f3499b7 707// WHERE (s.userid = '$userid' AND s.course = c.id)
708// OR (t.userid = '$userid' AND t.course = c.id)
8f0cd6ef 709// GROUP BY c.id
2f3499b7 710// ORDER BY $sort");
02ebf404 711}
712
713
18a97fd8 714/**
7290c7fa 715 * A list of courses that match a search
fbc21ae8 716 *
717 * @uses $CFG
718 * @param array $searchterms ?
719 * @param string $sort ?
720 * @param int $page ?
721 * @param int $recordsperpage ?
722 * @param int $totalcount Passed in by reference. ?
7290c7fa 723 * @return object {@link $COURSE} records
fbc21ae8 724 */
d4419d55 725function get_courses_search($searchterms, $sort='fullname ASC', $page=0, $recordsperpage=50, &$totalcount) {
02ebf404 726
727 global $CFG;
728
29daf3a0 729 $limit = sql_paging_limit($page, $recordsperpage);
02ebf404 730
18a97fd8 731 //to allow case-insensitive search for postgesql
d4419d55 732 if ($CFG->dbtype == 'postgres7') {
733 $LIKE = 'ILIKE';
734 $NOTLIKE = 'NOT ILIKE'; // case-insensitive
735 $REGEXP = '~*';
736 $NOTREGEXP = '!~*';
02ebf404 737 } else {
d4419d55 738 $LIKE = 'LIKE';
739 $NOTLIKE = 'NOT LIKE';
740 $REGEXP = 'REGEXP';
741 $NOTREGEXP = 'NOT REGEXP';
02ebf404 742 }
743
d4419d55 744 $fullnamesearch = '';
745 $summarysearch = '';
02ebf404 746
02ebf404 747 foreach ($searchterms as $searchterm) {
748 if ($fullnamesearch) {
d4419d55 749 $fullnamesearch .= ' AND ';
02ebf404 750 }
02ebf404 751 if ($summarysearch) {
d4419d55 752 $summarysearch .= ' AND ';
02ebf404 753 }
a8b56716 754
d4419d55 755 if (substr($searchterm,0,1) == '+') {
a8b56716 756 $searchterm = substr($searchterm,1);
757 $summarysearch .= " summary $REGEXP '(^|[^a-zA-Z0-9])$searchterm([^a-zA-Z0-9]|$)' ";
758 $fullnamesearch .= " fullname $REGEXP '(^|[^a-zA-Z0-9])$searchterm([^a-zA-Z0-9]|$)' ";
759 } else if (substr($searchterm,0,1) == "-") {
760 $searchterm = substr($searchterm,1);
761 $summarysearch .= " summary $NOTREGEXP '(^|[^a-zA-Z0-9])$searchterm([^a-zA-Z0-9]|$)' ";
762 $fullnamesearch .= " fullname $NOTREGEXP '(^|[^a-zA-Z0-9])$searchterm([^a-zA-Z0-9]|$)' ";
763 } else {
d4419d55 764 $summarysearch .= ' summary '. $LIKE .'\'%'. $searchterm .'%\' ';
765 $fullnamesearch .= ' fullname '. $LIKE .'\'%'. $searchterm .'%\' ';
a8b56716 766 }
767
02ebf404 768 }
769
d4419d55 770 $selectsql = $CFG->prefix .'course WHERE ('. $fullnamesearch .' OR '. $summarysearch .') AND category > \'0\'';
a8b56716 771
d4419d55 772 $totalcount = count_records_sql('SELECT COUNT(*) FROM '. $selectsql);
02ebf404 773
d4419d55 774 $courses = get_records_sql('SELECT * FROM '. $selectsql .' ORDER BY '. $sort .' '. $limit);
02ebf404 775
776 if ($courses) { /// Remove unavailable courses from the list
777 foreach ($courses as $key => $course) {
152a9060 778 if (!$course->visible) {
1c45e42e 779 if (!has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course->id))) {
02ebf404 780 unset($courses[$key]);
a8b56716 781 $totalcount--;
02ebf404 782 }
783 }
784 }
785 }
786
787 return $courses;
788}
789
790
18a97fd8 791/**
fbc21ae8 792 * Returns a sorted list of categories
793 *
613bbd7c 794 * @param string $parent The parent category if any
795 * @param string $sort the sortorder
796 * @return array of categories
fbc21ae8 797 */
d4419d55 798function get_categories($parent='none', $sort='sortorder ASC') {
02ebf404 799
814748c9 800 if ($parent === 'none') {
d4419d55 801 $categories = get_records('course_categories', '', '', $sort);
02ebf404 802 } else {
d4419d55 803 $categories = get_records('course_categories', 'parent', $parent, $sort);
02ebf404 804 }
805 if ($categories) { /// Remove unavailable categories from the list
ae9e4c06 806 $creator = has_capability('moodle/course:create', get_context_instance(CONTEXT_SYSTEM, SITEID));
02ebf404 807 foreach ($categories as $key => $category) {
152a9060 808 if (!$category->visible) {
3af6e1db 809 if (!$creator) {
02ebf404 810 unset($categories[$key]);
811 }
812 }
813 }
814 }
815 return $categories;
816}
817
818
18a97fd8 819/**
ba87a4da 820* This recursive function makes sure that the courseorder is consecutive
821*
822* @param type description
823*
824* $n is the starting point, offered only for compatilibity -- will be ignored!
825* $safe (bool) prevents it from assuming category-sortorder is unique, used to upgrade
826* safely from 1.4 to 1.5
827*/
f41ef63e 828function fix_course_sortorder($categoryid=0, $n=0, $safe=0, $depth=0, $path='') {
829
ba87a4da 830 global $CFG;
8f0cd6ef 831
02ebf404 832 $count = 0;
ba87a4da 833
f41ef63e 834 $catgap = 1000; // "standard" category gap
835 $tolerance = 200; // how "close" categories can get
836
837 if ($categoryid > 0){
838 // update depth and path
839 $cat = get_record('course_categories', 'id', $categoryid);
840 if ($cat->parent == 0) {
841 $depth = 0;
842 $path = '';
843 } else if ($depth == 0 ) { // doesn't make sense; get from DB
844 // this is only called if the $depth parameter looks dodgy
845 $parent = get_record('course_categories', 'id', $cat->parent);
846 $path = $parent->path;
847 $depth = $parent->depth;
848 }
849 $path = $path . '/' . $categoryid;
850 $depth = $depth + 1;
ba87a4da 851
f41ef63e 852 set_field('course_categories', 'path', addslashes($path), 'id', $categoryid);
853 set_field('course_categories', 'depth', $depth, 'id', $categoryid);
854 }
39f65595 855
856 // get some basic info about courses in the category
ba87a4da 857 $info = get_record_sql('SELECT MIN(sortorder) AS min,
858 MAX(sortorder) AS max,
f41ef63e 859 COUNT(sortorder) AS count
ba87a4da 860 FROM ' . $CFG->prefix . 'course
861 WHERE category=' . $categoryid);
862 if (is_object($info)) { // no courses?
863 $max = $info->max;
864 $count = $info->count;
865 $min = $info->min;
866 unset($info);
867 }
868
814748c9 869 if ($categoryid > 0 && $n==0) { // only passed category so don't shift it
870 $n = $min;
871 }
872
39f65595 873 // $hasgap flag indicates whether there's a gap in the sequence
874 $hasgap = false;
875 if ($max-$min+1 != $count) {
876 $hasgap = true;
877 }
878
879 // $mustshift indicates whether the sequence must be shifted to
880 // meet its range
881 $mustshift = false;
882 if ($min < $n+$tolerance || $min > $n+$tolerance+$catgap ) {
883 $mustshift = true;
884 }
885
ba87a4da 886 // actually sort only if there are courses,
887 // and we meet one ofthe triggers:
888 // - safe flag
889 // - they are not in a continuos block
890 // - they are too close to the 'bottom'
39f65595 891 if ($count && ( $safe || $hasgap || $mustshift ) ) {
892 // special, optimized case where all we need is to shift
893 if ( $mustshift && !$safe && !$hasgap) {
894 $shift = $n + $catgap - $min;
895 // UPDATE course SET sortorder=sortorder+$shift
896 execute_sql("UPDATE {$CFG->prefix}course
897 SET sortorder=sortorder+$shift
898 WHERE category=$categoryid", 0);
899 $n = $n + $catgap + $count;
900
901 } else { // do it slowly
902 $n = $n + $catgap;
903 // if the new sequence overlaps the current sequence, lack of transactions
904 // will stop us -- shift things aside for a moment...
94afadb3 905 if ($safe || ($n >= $min && $n+$count+1 < $min && $CFG->dbtype==='mysql')) {
d6a49dab 906 $shift = $max + $n + 1000;
39f65595 907 execute_sql("UPDATE {$CFG->prefix}course
908 SET sortorder=sortorder+$shift
909 WHERE category=$categoryid", 0);
ba87a4da 910 }
911
39f65595 912 $courses = get_courses($categoryid, 'c.sortorder ASC', 'c.id,c.sortorder');
913 begin_sql();
ba87a4da 914 foreach ($courses as $course) {
915 if ($course->sortorder != $n ) { // save db traffic
916 set_field('course', 'sortorder', $n, 'id', $course->id);
917 }
918 $n++;
919 }
920 commit_sql();
921 }
02ebf404 922 }
d4419d55 923 set_field('course_categories', 'coursecount', $count, 'id', $categoryid);
8f0cd6ef 924
814748c9 925 // $n could need updating
926 $max = get_field_sql("SELECT MAX(sortorder) from {$CFG->prefix}course WHERE category=$categoryid");
927 if ($max > $n) {
928 $n = $max;
929 }
758b9a4d 930
6bc502cc 931 if ($categories = get_categories($categoryid)) {
932 foreach ($categories as $category) {
f41ef63e 933 $n = fix_course_sortorder($category->id, $n, $safe, $depth, $path);
6bc502cc 934 }
935 }
8f0cd6ef 936
39f65595 937 return $n+1;
02ebf404 938}
939
fbc21ae8 940
18a97fd8 941/**
fbc21ae8 942 * This function creates a default separated/connected scale
943 *
944 * This function creates a default separated/connected scale
945 * so there's something in the database. The locations of
946 * strings and files is a bit odd, but this is because we
947 * need to maintain backward compatibility with many different
948 * existing language translations and older sites.
949 *
950 * @uses $CFG
951 */
02ebf404 952function make_default_scale() {
02ebf404 953
954 global $CFG;
955
956 $defaultscale = NULL;
957 $defaultscale->courseid = 0;
958 $defaultscale->userid = 0;
d4419d55 959 $defaultscale->name = get_string('separateandconnected');
960 $defaultscale->scale = get_string('postrating1', 'forum').','.
961 get_string('postrating2', 'forum').','.
962 get_string('postrating3', 'forum');
02ebf404 963 $defaultscale->timemodified = time();
964
8f0cd6ef 965 /// Read in the big description from the file. Note this is not
02ebf404 966 /// HTML (despite the file extension) but Moodle format text.
d4419d55 967 $parentlang = get_string('parentlang');
ee6e91d4 968 if (is_readable($CFG->dataroot .'/lang/'. $CFG->lang .'/help/forum/ratings.html')) {
969 $file = file($CFG->dataroot .'/lang/'. $CFG->lang .'/help/forum/ratings.html');
970 } else if (is_readable($CFG->dirroot .'/lang/'. $CFG->lang .'/help/forum/ratings.html')) {
d4419d55 971 $file = file($CFG->dirroot .'/lang/'. $CFG->lang .'/help/forum/ratings.html');
ee6e91d4 972 } else if ($parentlang and is_readable($CFG->dataroot .'/lang/'. $parentlang .'/help/forum/ratings.html')) {
973 $file = file($CFG->dataroot .'/lang/'. $parentlang .'/help/forum/ratings.html');
d4419d55 974 } else if ($parentlang and is_readable($CFG->dirroot .'/lang/'. $parentlang .'/help/forum/ratings.html')) {
975 $file = file($CFG->dirroot .'/lang/'. $parentlang .'/help/forum/ratings.html');
ee6e91d4 976 } else if (is_readable($CFG->dirroot .'/lang/en_utf8/help/forum/ratings.html')) {
977 $file = file($CFG->dirroot .'/lang/en_utf8/help/forum/ratings.html');
02ebf404 978 } else {
d4419d55 979 $file = '';
02ebf404 980 }
981
d4419d55 982 $defaultscale->description = addslashes(implode('', $file));
02ebf404 983
d4419d55 984 if ($defaultscale->id = insert_record('scale', $defaultscale)) {
985 execute_sql('UPDATE '. $CFG->prefix .'forum SET scale = \''. $defaultscale->id .'\'', false);
02ebf404 986 }
987}
988
fbc21ae8 989
18a97fd8 990/**
fbc21ae8 991 * Returns a menu of all available scales from the site as well as the given course
992 *
993 * @uses $CFG
994 * @param int $courseid The id of the course as found in the 'course' table.
7290c7fa 995 * @return object
fbc21ae8 996 */
02ebf404 997function get_scales_menu($courseid=0) {
02ebf404 998
999 global $CFG;
8f0cd6ef 1000
1001 $sql = "SELECT id, name FROM {$CFG->prefix}scale
1002 WHERE courseid = '0' or courseid = '$courseid'
02ebf404 1003 ORDER BY courseid ASC, name ASC";
1004
d4419d55 1005 if ($scales = get_records_sql_menu($sql)) {
02ebf404 1006 return $scales;
1007 }
1008
1009 make_default_scale();
1010
d4419d55 1011 return get_records_sql_menu($sql);
02ebf404 1012}
1013
5baa0ad6 1014
1015
1016/**
1017 * Given a set of timezone records, put them in the database, replacing what is there
1018 *
1019 * @uses $CFG
1020 * @param array $timezones An array of timezone records
1021 */
1022function update_timezone_records($timezones) {
1023/// Given a set of timezone records, put them in the database
1024
1025 global $CFG;
1026
1027/// Clear out all the old stuff
1028 execute_sql('TRUNCATE TABLE '.$CFG->prefix.'timezone', false);
1029
1030/// Insert all the new stuff
1031 foreach ($timezones as $timezone) {
1032 insert_record('timezone', $timezone);
1033 }
1034}
1035
1036
df28d6c5 1037/// MODULE FUNCTIONS /////////////////////////////////////////////////
1038
18a97fd8 1039/**
fbc21ae8 1040 * Just gets a raw list of all modules in a course
1041 *
1042 * @uses $CFG
1043 * @param int $courseid The id of the course as found in the 'course' table.
7290c7fa 1044 * @return object
fbc21ae8 1045 */
9fa49e22 1046function get_course_mods($courseid) {
9fa49e22 1047 global $CFG;
1048
3a11c548 1049 if (empty($courseid)) {
1050 return false; // avoid warnings
1051 }
1052
7acaa63d 1053 return get_records_sql("SELECT cm.*, m.name as modname
8f0cd6ef 1054 FROM {$CFG->prefix}modules m,
7acaa63d 1055 {$CFG->prefix}course_modules cm
8f0cd6ef 1056 WHERE cm.course = '$courseid'
9fa49e22 1057 AND cm.module = m.id ");
1058}
1059
fbc21ae8 1060
18a97fd8 1061/**
f9d5371b 1062 * Given an id of a course module, finds the coursemodule description
fbc21ae8 1063 *
f9d5371b 1064 * @param string $modulename name of module type, eg. resource, assignment,...
1065 * @param int $cmid course module id (id in course_modules table)
1066 * @param int $courseid optional course id for extra validation
1067 * @return object course module instance with instance and module name
1068 */
1069function get_coursemodule_from_id($modulename, $cmid, $courseid=0) {
1070
1071 global $CFG;
1072
1073 $courseselect = ($courseid) ? "cm.course = '$courseid' AND " : '';
1074
1075 return get_record_sql("SELECT cm.*, m.name, md.name as modname
1076 FROM {$CFG->prefix}course_modules cm,
1077 {$CFG->prefix}modules md,
1078 {$CFG->prefix}$modulename m
1079 WHERE $courseselect
1080 cm.id = '$cmid' AND
1081 cm.instance = m.id AND
1082 md.name = '$modulename' AND
1083 md.id = cm.module");
1084}
1085
1086/**
1087 * Given an instance number of a module, finds the coursemodule description
1088 *
1089 * @param string $modulename name of module type, eg. resource, assignment,...
1090 * @param int $instance module instance number (id in resource, assignment etc. table)
1091 * @param int $courseid optional course id for extra validation
1092 * @return object course module instance with instance and module name
fbc21ae8 1093 */
b63c0ee5 1094function get_coursemodule_from_instance($modulename, $instance, $courseid=0) {
df28d6c5 1095
1096 global $CFG;
f9d5371b 1097
b63c0ee5 1098 $courseselect = ($courseid) ? "cm.course = '$courseid' AND " : '';
df28d6c5 1099
f9d5371b 1100 return get_record_sql("SELECT cm.*, m.name, md.name as modname
8f0cd6ef 1101 FROM {$CFG->prefix}course_modules cm,
1102 {$CFG->prefix}modules md,
1103 {$CFG->prefix}$modulename m
b63c0ee5 1104 WHERE $courseselect
8f0cd6ef 1105 cm.instance = m.id AND
1106 md.name = '$modulename' AND
df28d6c5 1107 md.id = cm.module AND
1108 m.id = '$instance'");
1109
1110}
1111
185cfb09 1112/**
1113 * Returns an array of all the active instances of a particular module in given courses, sorted in the order they are defined
1114 *
1115 * Returns an array of all the active instances of a particular
1116 * module in given courses, sorted in the order they are defined
1117 * in the course. Returns false on any errors.
1118 *
1119 * @uses $CFG
1120 * @param string $modulename The name of the module to get instances for
613bbd7c 1121 * @param array $courses This depends on an accurate $course->modinfo
1122 * @return array of instances
185cfb09 1123 */
1124function get_all_instances_in_courses($modulename,$courses) {
1125 global $CFG;
1126 if (empty($courses) || !is_array($courses) || count($courses) == 0) {
1127 return array();
1128 }
1129 if (!$rawmods = get_records_sql("SELECT cm.id as coursemodule, m.*,cw.section,cm.visible as visible,cm.groupmode, cm.course
1130 FROM {$CFG->prefix}course_modules cm,
1131 {$CFG->prefix}course_sections cw,
1132 {$CFG->prefix}modules md,
1133 {$CFG->prefix}$modulename m
1134 WHERE cm.course IN (".implode(',',array_keys($courses)).") AND
1135 cm.instance = m.id AND
1136 cm.section = cw.id AND
1137 md.name = '$modulename' AND
1138 md.id = cm.module")) {
1139 return array();
1140 }
1141
1142 $outputarray = array();
1143
1144 foreach ($courses as $course) {
1145 // Hide non-visible instances from students
1c45e42e 1146 if (has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course->id))) {
185cfb09 1147 $invisible = -1;
1148 } else {
1149 $invisible = 0;
1150 }
fea43a7f 1151
1152 /// Casting $course->modinfo to string prevents one notice when the field is null
1153 if (!$modinfo = unserialize((string)$course->modinfo)) {
185cfb09 1154 continue;
1155 }
1156 foreach ($modinfo as $mod) {
1157 if ($mod->mod == $modulename and $mod->visible > $invisible) {
1158 $instance = $rawmods[$mod->cm];
1159 if (!empty($mod->extra)) {
1160 $instance->extra = $mod->extra;
1161 }
1162 $outputarray[] = $instance;
1163 }
1164 }
1165 }
1166
1167 return $outputarray;
1168
1169}
fbc21ae8 1170
18a97fd8 1171/**
fbc21ae8 1172 * Returns an array of all the active instances of a particular module in a given course, sorted in the order they are defined
1173 *
1174 * Returns an array of all the active instances of a particular
1175 * module in a given course, sorted in the order they are defined
1176 * in the course. Returns false on any errors.
1177 *
1178 * @uses $CFG
1179 * @param string $modulename The name of the module to get instances for
1180 * @param object(course) $course This depends on an accurate $course->modinfo
fbc21ae8 1181 */
cccb016a 1182function get_all_instances_in_course($modulename, $course) {
df28d6c5 1183
1184 global $CFG;
1185
3cc8b355 1186 if (empty($course->modinfo)) {
1187 return array();
1188 }
1189
fea43a7f 1190 if (!$modinfo = unserialize((string)$course->modinfo)) {
cccb016a 1191 return array();
1acfbce5 1192 }
1193
404afe6b 1194 if (!$rawmods = get_records_sql("SELECT cm.id as coursemodule, m.*,cw.section,cm.visible as visible,cm.groupmode
8f0cd6ef 1195 FROM {$CFG->prefix}course_modules cm,
1196 {$CFG->prefix}course_sections cw,
1197 {$CFG->prefix}modules md,
1198 {$CFG->prefix}$modulename m
1199 WHERE cm.course = '$course->id' AND
1200 cm.instance = m.id AND
8f0cd6ef 1201 cm.section = cw.id AND
1202 md.name = '$modulename' AND
cccb016a 1203 md.id = cm.module")) {
1204 return array();
1205 }
1206
1207 // Hide non-visible instances from students
1c45e42e 1208 if (has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course->id))) {
cccb016a 1209 $invisible = -1;
1210 } else {
1211 $invisible = 0;
1212 }
1213
1214 foreach ($modinfo as $mod) {
1215 if ($mod->mod == $modulename and $mod->visible > $invisible) {
7f12f9cd 1216 $instance = $rawmods[$mod->cm];
1217 if (!empty($mod->extra)) {
1218 $instance->extra = $mod->extra;
1219 }
1220 $outputarray[] = $instance;
cccb016a 1221 }
1222 }
1223
1224 return $outputarray;
df28d6c5 1225
1226}
1227
9fa49e22 1228
18a97fd8 1229/**
fbc21ae8 1230 * Determine whether a module instance is visible within a course
1231 *
1232 * Given a valid module object with info about the id and course,
1233 * and the module's type (eg "forum") returns whether the object
1234 * is visible or not
1235 *
1236 * @uses $CFG
613bbd7c 1237 * @param $moduletype Name of the module eg 'forum'
1238 * @param $module Object which is the instance of the module
7290c7fa 1239 * @return bool
fbc21ae8 1240 */
580f2fbc 1241function instance_is_visible($moduletype, $module) {
580f2fbc 1242
1243 global $CFG;
1244
2b49ae96 1245 if (!empty($module->id)) {
1246 if ($records = get_records_sql("SELECT cm.instance, cm.visible
1247 FROM {$CFG->prefix}course_modules cm,
1248 {$CFG->prefix}modules m
1249 WHERE cm.course = '$module->course' AND
1250 cm.module = m.id AND
1251 m.name = '$moduletype' AND
1252 cm.instance = '$module->id'")) {
1253
1254 foreach ($records as $record) { // there should only be one - use the first one
1255 return $record->visible;
1256 }
580f2fbc 1257 }
1258 }
580f2fbc 1259 return true; // visible by default!
1260}
1261
a3fb1c45 1262
1263
1264
9fa49e22 1265/// LOG FUNCTIONS /////////////////////////////////////////////////////
1266
1267
18a97fd8 1268/**
fbc21ae8 1269 * Add an entry to the log table.
1270 *
1271 * Add an entry to the log table. These are "action" focussed rather
1272 * than web server hits, and provide a way to easily reconstruct what
1273 * any particular student has been doing.
1274 *
1275 * @uses $CFG
1276 * @uses $USER
1277 * @uses $db
1278 * @uses $REMOTE_ADDR
1279 * @uses SITEID
89dcb99d 1280 * @param int $courseid The course id
fbc21ae8 1281 * @param string $module The module name - e.g. forum, journal, resource, course, user etc
f7664880 1282 * @param string $action 'view', 'update', 'add' or 'delete', possibly followed by another word to clarify.
fbc21ae8 1283 * @param string $url The file and parameters used to see the results of the action
1284 * @param string $info Additional description information
1285 * @param string $cm The course_module->id if there is one
1286 * @param string $user If log regards $user other than $USER
1287 */
d4419d55 1288function add_to_log($courseid, $module, $action, $url='', $info='', $cm=0, $user=0) {
e8395a09 1289 // Note that this function intentionally does not follow the normal Moodle DB access idioms.
1290 // This is for a good reason: it is the most frequently used DB update function,
1291 // so it has been optimised for speed.
fcaff7ff 1292 global $db, $CFG, $USER;
9fa49e22 1293
7a5b1fc5 1294 if ($cm === '' || is_null($cm)) { // postgres won't translate empty string to its default
f78b3c34 1295 $cm = 0;
1296 }
1297
3d94772d 1298 if ($user) {
1299 $userid = $user;
1300 } else {
cb80265b 1301 if (!empty($USER->realuser)) { // Don't log
3d94772d 1302 return;
1303 }
d4419d55 1304 $userid = empty($USER->id) ? '0' : $USER->id;
9fa49e22 1305 }
1306
fcaff7ff 1307 $REMOTE_ADDR = getremoteaddr();
1308
9fa49e22 1309 $timenow = time();
1310 $info = addslashes($info);
10a760b9 1311 if (!empty($url)) { // could break doing html_entity_decode on an empty var.
1312 $url = html_entity_decode($url); // for php < 4.3.0 this is defined in moodlelib.php
1313 }
853df85e 1314
1315 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; $PERF->logwrites++;};
1316
d4419d55 1317 $result = $db->Execute('INSERT INTO '. $CFG->prefix .'log (time, userid, course, ip, module, cmid, action, url, info)
1318 VALUES (' . "'$timenow', '$userid', '$courseid', '$REMOTE_ADDR', '$module', '$cm', '$action', '$url', '$info')");
ebc3bd2b 1319
ea82d6b6 1320 if (!$result and debugging()) {
d4419d55 1321 echo '<p>Error: Could not insert a new entry to the Moodle log</p>'; // Don't throw an error
8f0cd6ef 1322 }
cb80265b 1323
1324/// Store lastaccess times for the current user
1325
1326 if (!empty($USER->id) && ($userid == $USER->id) ) {
1327 $db->Execute('UPDATE '. $CFG->prefix .'user
1328 SET lastip=\''. $REMOTE_ADDR .'\', lastaccess=\''. $timenow .'\'
1329 WHERE id = \''. $userid .'\' ');
1330 if ($courseid != SITEID && !empty($courseid)) {
853df85e 1331 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++;};
1c45e42e 1332
cb80265b 1333 if ($record = get_record('user_lastaccess', 'userid', $userid, 'courseid', $courseid)) {
1334 $record->timeaccess = $timenow;
1335 return update_record('user_lastaccess', $record);
1336 } else {
ae9e4c06 1337 $record = new object;
1338 $record->userid = $userid;
1339 $record->courseid = $courseid;
1340 $record->timeaccess = $timenow;
1341 return insert_record('user_lastaccess', $record);
114176a2 1342 }
3d94772d 1343 }
8f0cd6ef 1344 }
9fa49e22 1345}
1346
1347
18a97fd8 1348/**
fbc21ae8 1349 * Select all log records based on SQL criteria
1350 *
1351 * @uses $CFG
1352 * @param string $select SQL select criteria
1353 * @param string $order SQL order by clause to sort the records returned
1354 * @param string $limitfrom ?
1355 * @param int $limitnum ?
1356 * @param int $totalcount Passed in by reference.
7290c7fa 1357 * @return object
fbc21ae8 1358 * @todo Finish documenting this function
1359 */
d4419d55 1360function get_logs($select, $order='l.time DESC', $limitfrom='', $limitnum='', &$totalcount) {
9fa49e22 1361 global $CFG;
1362
519d369f 1363 if ($order) {
d4419d55 1364 $order = 'ORDER BY '. $order;
519d369f 1365 }
1366
fbc21ae8 1367 $selectsql = $CFG->prefix .'log l LEFT JOIN '. $CFG->prefix .'user u ON l.userid = u.id '. ((strlen($select) > 0) ? 'WHERE '. $select : '');
a2ddd957 1368 $countsql = $CFG->prefix.'log l '.((strlen($select) > 0) ? ' WHERE '. $select : '');
1369
1370 $totalcount = count_records_sql("SELECT COUNT(*) FROM $countsql");
519d369f 1371
d4419d55 1372 return get_records_sql('SELECT l.*, u.firstname, u.lastname, u.picture
93a89227 1373 FROM '. $selectsql .' '. $order, $limitfrom, $limitnum) ;
9fa49e22 1374}
1375
519d369f 1376
18a97fd8 1377/**
fbc21ae8 1378 * Select all log records for a given course and user
1379 *
1380 * @uses $CFG
2f87145b 1381 * @uses DAYSECS
fbc21ae8 1382 * @param int $userid The id of the user as found in the 'user' table.
1383 * @param int $courseid The id of the course as found in the 'course' table.
1384 * @param string $coursestart ?
1385 * @todo Finish documenting this function
1386 */
9fa49e22 1387function get_logs_usercourse($userid, $courseid, $coursestart) {
1388 global $CFG;
1389
da0c90c3 1390 if ($courseid) {
d4419d55 1391 $courseselect = ' AND course = \''. $courseid .'\' ';
2700d113 1392 } else {
1393 $courseselect = '';
da0c90c3 1394 }
1395
1604a0fc 1396 return get_records_sql("SELECT floor((time - $coursestart)/". DAYSECS .") as day, count(*) as num
8f0cd6ef 1397 FROM {$CFG->prefix}log
1398 WHERE userid = '$userid'
1604a0fc 1399 AND time > '$coursestart' $courseselect
9fa49e22 1400 GROUP BY day ");
1401}
1402
18a97fd8 1403/**
fbc21ae8 1404 * Select all log records for a given course, user, and day
1405 *
1406 * @uses $CFG
2f87145b 1407 * @uses HOURSECS
fbc21ae8 1408 * @param int $userid The id of the user as found in the 'user' table.
1409 * @param int $courseid The id of the course as found in the 'course' table.
1410 * @param string $daystart ?
7290c7fa 1411 * @return object
fbc21ae8 1412 * @todo Finish documenting this function
1413 */
9fa49e22 1414function get_logs_userday($userid, $courseid, $daystart) {
1415 global $CFG;
1416
7e4a6488 1417 if ($courseid) {
d4419d55 1418 $courseselect = ' AND course = \''. $courseid .'\' ';
2700d113 1419 } else {
1420 $courseselect = '';
7e4a6488 1421 }
1422
1604a0fc 1423 return get_records_sql("SELECT floor((time - $daystart)/". HOURSECS .") as hour, count(*) as num
9fa49e22 1424 FROM {$CFG->prefix}log
8f0cd6ef 1425 WHERE userid = '$userid'
1604a0fc 1426 AND time > '$daystart' $courseselect
9fa49e22 1427 GROUP BY hour ");
1428}
1429
b4bac9b6 1430/**
1431 * Returns an object with counts of failed login attempts
1432 *
8f0cd6ef 1433 * Returns information about failed login attempts. If the current user is
1434 * an admin, then two numbers are returned: the number of attempts and the
b4bac9b6 1435 * number of accounts. For non-admins, only the attempts on the given user
1436 * are shown.
1437 *
fbc21ae8 1438 * @param string $mode Either 'admin', 'teacher' or 'everybody'
1439 * @param string $username The username we are searching for
1440 * @param string $lastlogin The date from which we are searching
1441 * @return int
b4bac9b6 1442 */
b4bac9b6 1443function count_login_failures($mode, $username, $lastlogin) {
1444
d4419d55 1445 $select = 'module=\'login\' AND action=\'error\' AND time > '. $lastlogin;
b4bac9b6 1446
51792df0 1447 if (has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM, SITEID))) { // Return information about all accounts
b4bac9b6 1448 if ($count->attempts = count_records_select('log', $select)) {
1449 $count->accounts = count_records_select('log', $select, 'COUNT(DISTINCT info)');
1450 return $count;
1451 }
9407d456 1452 } else if ($mode == 'everybody' or ($mode == 'teacher' and isteacherinanycourse())) {
d4419d55 1453 if ($count->attempts = count_records_select('log', $select .' AND info = \''. $username .'\'')) {
b4bac9b6 1454 return $count;
1455 }
1456 }
1457 return NULL;
1458}
1459
1460
a3fb1c45 1461/// GENERAL HELPFUL THINGS ///////////////////////////////////
1462
18a97fd8 1463/**
fbc21ae8 1464 * Dump a given object's information in a PRE block.
1465 *
1466 * Mostly just used for debugging.
1467 *
1468 * @param mixed $object The data to be printed
fbc21ae8 1469 */
a3fb1c45 1470function print_object($object) {
a3fb1c45 1471
d4419d55 1472 echo '<pre>';
2b051f1c 1473 print_r($object);
d4419d55 1474 echo '</pre>';
a3fb1c45 1475}
1476
11840632 1477
3ec22e35 1478/**
1479 * Checks for pg or mysql > 4
1480 */
1481
1482function check_db_compat() {
1483 global $CFG,$db;
1484
1485 if ($CFG->dbtype == 'postgres7') {
1486 return true;
1487 }
1488
1489 if (!$rs = $db->Execute("SELECT version();")) {
1490 return false;
1491 }
1492
1493 if (intval($rs->fields[0]) <= 3) {
1494 return false;
1495 }
1496
1497 return true;
1498}
1499
0986271b 1500function course_parent_visible($course = null) {
fa145ae1 1501 global $CFG;
1502
418b4e5a 1503 if (empty($course)) {
1504 return true;
1505 }
1506 if (!empty($CFG->allowvisiblecoursesinhiddencategories)) {
1507 return true;
1508 }
0986271b 1509 return category_parent_visible($course->category);
1510}
1511
1512function category_parent_visible($parent = 0) {
824f1c40 1513
1514 static $visible;
1515
0986271b 1516 if (!$parent) {
1517 return true;
1518 }
824f1c40 1519
1520 if (empty($visible)) {
1521 $visible = array(); // initialize
1522 }
1523
1524 if (array_key_exists($parent,$visible)) {
1525 return $visible[$parent];
1526 }
1527
0986271b 1528 $category = get_record('course_categories', 'id', $parent);
1529 $list = explode('/', preg_replace('/^\/(.*)$/', '$1', $category->path));
1530 $list[] = $parent;
1531 $parents = get_records_list('course_categories', 'id', implode(',', $list), 'depth DESC');
824f1c40 1532 $v = true;
1533 foreach ($parents as $p) {
1534 if (!$p->visible) {
1535 $v = false;
0986271b 1536 }
1537 }
824f1c40 1538 $visible[$parent] = $v; // now cache it
1539 return $v;
0986271b 1540}
1541
9d5b689c 1542// vim:autoindent:expandtab:shiftwidth=4:tabstop=4:tw=140:
41f6ed56 1543?>