changed based on /lang/en/forum.php:
[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
7d8c2ec3 16/// GLOBAL CONSTANTS /////////////////////////////////////////////////////////
7cf1c7bd 17
7d8c2ec3 18if ($SITE = get_site()) {
7cf1c7bd 19 /**
fbc21ae8 20 * If $SITE global from {@link get_site()} is set then SITEID to $SITE->id, otherwise set to 1.
7cf1c7bd 21 */
7d8c2ec3 22 define('SITEID', $SITE->id);
23} else {
7cf1c7bd 24 /**
25 * @ignore
26 */
7d8c2ec3 27 define('SITEID', 1);
28}
222ac91b 29
df28d6c5 30/// FUNCTIONS FOR DATABASE HANDLING ////////////////////////////////
222ac91b 31
18a97fd8 32/**
7cf1c7bd 33 * Execute a given sql command string
34 *
35 * Completely general function - it just runs some SQL and reports success.
36 *
fbc21ae8 37 * @uses $db
38 * @param string $command The sql string you wish to be executed.
7290c7fa 39 * @param bool $feedback Set this argument to true if the results generated should be printed. Default is true.
7cf1c7bd 40 * @return string
41 */
df28d6c5 42function execute_sql($command, $feedback=true) {
43/// Completely general function - it just runs some SQL and reports success.
44
1b4fe47b 45 global $db, $CFG;
8f0cd6ef 46
1734f08e 47 $olddebug = $db->debug;
48
49 if (!$feedback) {
50 $db->debug = false;
51 }
853df85e 52
53 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
1734f08e 54
d4419d55 55 $result = $db->Execute($command);
df28d6c5 56
1734f08e 57 $db->debug = $olddebug;
58
df28d6c5 59 if ($result) {
60 if ($feedback) {
a8f68426 61 notify(get_string('success'), 'notifysuccess');
df28d6c5 62 }
63 return true;
64 } else {
65 if ($feedback) {
fbc21ae8 66 echo '<p><font color="red"><strong>'. get_string('error') .'</strong></font></p>';
df28d6c5 67 }
acd2279e 68 if (!empty($CFG->dblogerror)) {
31df0bb8 69 $debug=array_shift(debug_backtrace());
acd2279e 70 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $command");
71 }
df28d6c5 72 return false;
73 }
74}
1ed5abc8 75/**
76* on DBs that support it, switch to transaction mode and begin a transaction
77* you'll need to ensure you call commit_sql() or your changes *will* be lost
78* this is _very_ useful for massive updates
79*/
80function begin_sql() {
81/// Completely general function - it just runs some SQL and reports success.
82
83 global $CFG;
84 if ($CFG->dbtype === 'postgres7') {
85 return execute_sql('BEGIN', false);
86 }
87 return true;
88}
c54a5943 89/**
90* on DBs that support it, commit the transaction
91*/
92function rollback_sql() {
93/// Completely general function - it just runs some SQL and reports success.
94
95 global $CFG;
96 if ($CFG->dbtype === 'postgres7') {
97 return execute_sql('ROLLBACK', false);
98 }
99 return true;
100}
101
102
6b7f8df2 103
104/**
105 * returns db specific uppercase function
106 */
107function db_uppercase() {
108 global $CFG;
109 switch (strtolower($CFG->dbtype)) {
110
111 case "postgres7":
112 return "upper";
d4c54cd6 113
6b7f8df2 114 case "mysql":
115 default:
116 return "ucase";
d4c54cd6 117
6b7f8df2 118 }
119}
120
121/**
122 * returns db specific lowercase function
123 */
124function db_lowercase() {
125 global $CFG;
126 switch (strtolower($CFG->dbtype)) {
127
128 case "postgres7":
129 return "lower";
d4c54cd6 130
6b7f8df2 131 case "mysql":
132 default:
133 return "lcase";
d4c54cd6 134
6b7f8df2 135 }
136}
137
1ed5abc8 138/**
139* on DBs that support it, commit the transaction
140*/
141function commit_sql() {
142/// Completely general function - it just runs some SQL and reports success.
143
144 global $CFG;
145 if ($CFG->dbtype === 'postgres7') {
146 return execute_sql('COMMIT', false);
147 }
148 return true;
149}
df28d6c5 150
7cf1c7bd 151/**
152 * Run an arbitrary sequence of semicolon-delimited SQL commands
153 *
154 * Assumes that the input text (file or string) consists of
155 * a number of SQL statements ENDING WITH SEMICOLONS. The
156 * semicolons MUST be the last character in a line.
e5cf5750 157 * Lines that are blank or that start with "#" or "--" (postgres) are ignored.
7cf1c7bd 158 * Only tested with mysql dump files (mysqldump -p -d moodle)
159 *
fbc21ae8 160 * @uses $CFG
7cf1c7bd 161 * @param string $sqlfile The path where a file with sql commands can be found on the server.
162 * @param string $sqlstring If no path is supplied then a string with semicolon delimited sql
163 * commands can be supplied in this argument.
7290c7fa 164 * @return bool Returns true if databse was modified successfully.
7cf1c7bd 165 */
d4419d55 166function modify_database($sqlfile='', $sqlstring='') {
df28d6c5 167
168 global $CFG;
169
8f0cd6ef 170 $success = true; // Let's be optimistic
2b051f1c 171
172 if (!empty($sqlfile)) {
173 if (!is_readable($sqlfile)) {
174 $success = false;
d4419d55 175 echo '<p>Tried to modify database, but "'. $sqlfile .'" doesn\'t exist!</p>';
2b051f1c 176 return $success;
177 } else {
178 $lines = file($sqlfile);
179 }
180 } else {
a9676376 181 $sqlstring = trim($sqlstring);
182 if ($sqlstring{strlen($sqlstring)-1} != ";") {
183 $sqlstring .= ";"; // add it in if it's not there.
184 }
2b051f1c 185 $lines[] = $sqlstring;
186 }
187
d4419d55 188 $command = '';
2b051f1c 189
190 foreach ($lines as $line) {
191 $line = rtrim($line);
192 $length = strlen($line);
193
e5cf5750 194 if ($length and $line[0] <> '#' and $line[0].$line[1] <> '--') {
d4419d55 195 if (substr($line, $length-1, 1) == ';') {
2b051f1c 196 $line = substr($line, 0, $length-1); // strip ;
197 $command .= $line;
d4419d55 198 $command = str_replace('prefix_', $CFG->prefix, $command); // Table prefixes
2b051f1c 199 if (! execute_sql($command)) {
200 $success = false;
df28d6c5 201 }
d4419d55 202 $command = '';
2b051f1c 203 } else {
204 $command .= $line;
df28d6c5 205 }
206 }
df28d6c5 207 }
208
209 return $success;
2b051f1c 210
df28d6c5 211}
212
a3fb1c45 213/// FUNCTIONS TO MODIFY TABLES ////////////////////////////////////////////
214
18a97fd8 215/**
fbc21ae8 216 * Add a new field to a table, or modify an existing one (if oldfield is defined).
217 *
218 * @uses $CFG
219 * @uses $db
89dcb99d 220 * @param string $table ?
221 * @param string $oldfield ?
222 * @param string $field ?
7c81cc29 223 * @param string $type ?
89dcb99d 224 * @param string $size ?
225 * @param string $signed ?
226 * @param string $default ?
227 * @param string $null ?
fbc21ae8 228 * @todo Finish documenting this function
229 */
18a97fd8 230
d4419d55 231function table_column($table, $oldfield, $field, $type='integer', $size='10',
232 $signed='unsigned', $default='0', $null='not null', $after='') {
8a230a7d 233 global $CFG, $db;
a3fb1c45 234
235 switch (strtolower($CFG->dbtype)) {
236
d4419d55 237 case 'mysql':
238 case 'mysqlt':
a3fb1c45 239
240 switch (strtolower($type)) {
d4419d55 241 case 'text':
242 $type = 'TEXT';
243 $signed = '';
c2cb4545 244 break;
d4419d55 245 case 'integer':
246 $type = 'INTEGER('. $size .')';
a3fb1c45 247 break;
d4419d55 248 case 'varchar':
249 $type = 'VARCHAR('. $size .')';
250 $signed = '';
a3fb1c45 251 break;
2ecdff78 252 case 'char':
253 $type = 'CHAR('. $size .')';
254 $signed = '';
255 break;
a3fb1c45 256 }
257
258 if (!empty($oldfield)) {
d4419d55 259 $operation = 'CHANGE '. $oldfield .' '. $field;
a3fb1c45 260 } else {
d4419d55 261 $operation = 'ADD '. $field;
a3fb1c45 262 }
263
d4419d55 264 $default = 'DEFAULT \''. $default .'\'';
a3fb1c45 265
266 if (!empty($after)) {
d4419d55 267 $after = 'AFTER `'. $after .'`';
a3fb1c45 268 }
269
d4419d55 270 return execute_sql('ALTER TABLE '. $CFG->prefix . $table .' '. $operation .' '. $type .' '. $signed .' '. $default .' '. $null .' '. $after);
a3fb1c45 271
d4419d55 272 case 'postgres7': // From Petri Asikainen
8a230a7d 273 //Check db-version
274 $dbinfo = $db->ServerInfo();
29622339 275 $dbver = substr($dbinfo['version'],0,3);
8f0cd6ef 276
5a4d292b 277 //to prevent conflicts with reserved words
d4419d55 278 $realfield = '"'. $field .'"';
279 $field = '"'. $field .'_alter_column_tmp"';
280 $oldfield = '"'. $oldfield .'"';
5a4d292b 281
282 switch (strtolower($type)) {
9c851359 283 case 'tinyint':
d4419d55 284 case 'integer':
09d4c9a9 285 if ($size <= 4) {
d4419d55 286 $type = 'INT2';
8f0cd6ef 287 }
09d4c9a9 288 if ($size <= 10) {
d4419d55 289 $type = 'INT';
5a4d292b 290 }
09d4c9a9 291 if ($size > 10) {
d4419d55 292 $type = 'INT8';
5a4d292b 293 }
294 break;
d4419d55 295 case 'varchar':
296 $type = 'VARCHAR('. $size .')';
5a4d292b 297 break;
2ecdff78 298 case 'char':
299 $type = 'CHAR('. $size .')';
300 $signed = '';
301 break;
5a4d292b 302 }
303
d4419d55 304 $default = '\''. $default .'\'';
5a4d292b 305
306 //After is not implemented in postgesql
307 //if (!empty($after)) {
308 // $after = "AFTER '$after'";
309 //}
310
d05024a7 311 //Use transactions
d4419d55 312 execute_sql('BEGIN');
d05024a7 313
7c81cc29 314 //Always use temporary column
d4419d55 315 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ADD COLUMN '. $field .' '. $type);
d05024a7 316 //Add default values
d4419d55 317 execute_sql('UPDATE '. $CFG->prefix . $table .' SET '. $field .'='. $default);
8f0cd6ef 318
5a4d292b 319
d4419d55 320 if ($dbver >= '7.3') {
8a230a7d 321 // modifying 'not null' is posible before 7.3
322 //update default values to table
7c56e42d 323 if (strtoupper($null) == 'NOT NULL') {
d4419d55 324 execute_sql('UPDATE '. $CFG->prefix . $table .' SET '. $field .'='. $default .' WHERE '. $field .' IS NULL');
325 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' SET '. $null);
8a230a7d 326 } else {
d4419d55 327 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' DROP NOT NULL');
8a230a7d 328 }
5a4d292b 329 }
8a230a7d 330
d4419d55 331 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' SET DEFAULT '. $default);
8f0cd6ef 332
d4419d55 333 if ( $oldfield != '""' ) {
7c81cc29 334
335 // We are changing the type of a column. This may require doing some casts...
336 $casting = '';
337 $oldtype = column_type($table, $oldfield);
338 $newtype = column_type($table, $field);
339
340 // Do we need a cast?
341 if($newtype == 'N' && $oldtype == 'C') {
342 $casting = 'CAST(CAST('.$oldfield.' AS TEXT) AS REAL)';
343 }
9feb9a19 344 else if($newtype == 'I' && $oldtype == 'C') {
7c81cc29 345 $casting = 'CAST(CAST('.$oldfield.' AS TEXT) AS INTEGER)';
346 }
347 else {
348 $casting = $oldfield;
349 }
350
351 // Run the update query, casting as necessary
352 execute_sql('UPDATE '. $CFG->prefix . $table .' SET '. $field .' = '. $casting);
d4419d55 353 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' DROP COLUMN '. $oldfield);
d05024a7 354 }
5a4d292b 355
7cf1c7bd 356 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' RENAME COLUMN '. $field .' TO '. $realfield);
8f0cd6ef 357
d4419d55 358 return execute_sql('COMMIT');
a3fb1c45 359
360 default:
361 switch (strtolower($type)) {
d4419d55 362 case 'integer':
363 $type = 'INTEGER';
a3fb1c45 364 break;
d4419d55 365 case 'varchar':
366 $type = 'VARCHAR';
92230499 367 break;
a3fb1c45 368 }
369
d4419d55 370 $default = 'DEFAULT \''. $default .'\'';
a3fb1c45 371
372 if (!empty($after)) {
d4419d55 373 $after = 'AFTER '. $after;
a3fb1c45 374 }
375
376 if (!empty($oldfield)) {
d4419d55 377 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' RENAME COLUMN '. $oldfield .' '. $field);
a3fb1c45 378 } else {
d4419d55 379 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ADD COLUMN '. $field .' '. $type);
a3fb1c45 380 }
381
d4419d55 382 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' SET '. $null);
383 return execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' SET '. $default);
a3fb1c45 384 }
385}
386
7c81cc29 387/**
388 * Get the data type of a table column, using an ADOdb MetaType() call.
389 *
390 * @uses $CFG
391 * @uses $db
8a7d9d15 392 * @param string $table The name of the database table
393 * @param string $column The name of the field in the table
394 * @return string Field type or false if error
7c81cc29 395 */
396
397function column_type($table, $column) {
398 global $CFG, $db;
399
853df85e 400 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
401
d20f58e7 402 if(!$rs = $db->Execute('SELECT '.$column.' FROM '.$CFG->prefix.$table.' LIMIT 0')) {
7c81cc29 403 return false;
404 }
405
406 $field = $rs->FetchField(0);
407 return $rs->MetaType($field->type);
408}
a3fb1c45 409
410
411/// GENERIC FUNCTIONS TO CHECK AND COUNT RECORDS ////////////////////////////////////////
df28d6c5 412
18a97fd8 413/**
11840632 414 * Test whether a record exists in a table where all the given fields match the given values.
415 *
416 * The record to test is specified by giving up to three fields that must
417 * equal the corresponding values.
fbc21ae8 418 *
419 * @uses $CFG
11840632 420 * @param string $table The table to check.
421 * @param string $field1 the first field to check (optional).
422 * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
423 * @param string $field2 the second field to check (optional).
424 * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
425 * @param string $field3 the third field to check (optional).
426 * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
427 * @return bool true if a matching record exists, else false.
fbc21ae8 428 */
d4419d55 429function record_exists($table, $field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
df28d6c5 430
431 global $CFG;
432
11840632 433 $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
df28d6c5 434
d4419d55 435 return record_exists_sql('SELECT * FROM '. $CFG->prefix . $table .' '. $select .' LIMIT 1');
df28d6c5 436}
437
438
18a97fd8 439/**
eb856e3b 440 * Test whether a SQL SELECT statement returns any records.
441 *
442 * This function returns true if the SQL statement executes
443 * without any errors and returns at least one record.
444 *
445 * @param string $sql The SQL statement to execute.
446 * @return bool true if the SQL executes without errors and returns at least one record.
447 */
df28d6c5 448function record_exists_sql($sql) {
eb856e3b 449 $rs = get_recordset_sql($sql);
df28d6c5 450
eb856e3b 451 if ($rs && $rs->RecordCount() > 0) {
df28d6c5 452 return true;
453 } else {
454 return false;
455 }
eb856e3b 456}
df28d6c5 457
18a97fd8 458/**
11840632 459 * Count the records in a table where all the given fields match the given values.
fbc21ae8 460 *
461 * @uses $CFG
11840632 462 * @param string $table The table to query.
463 * @param string $field1 the first field to check (optional).
464 * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
465 * @param string $field2 the second field to check (optional).
466 * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
467 * @param string $field3 the third field to check (optional).
468 * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
fbc21ae8 469 * @return int The count of records returned from the specified criteria.
470 */
d4419d55 471function count_records($table, $field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
df28d6c5 472
473 global $CFG;
474
11840632 475 $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
df28d6c5 476
d4419d55 477 return count_records_sql('SELECT COUNT(*) FROM '. $CFG->prefix . $table .' '. $select);
df28d6c5 478}
479
18a97fd8 480/**
eb856e3b 481 * Count the records in a table which match a particular WHERE clause.
fbc21ae8 482 *
483 * @uses $CFG
484 * @param string $table The database table to be checked against.
eb856e3b 485 * @param string $select A fragment of SQL to be used in a WHERE clause in the SQL call.
fbc21ae8 486 * @param string $countitem The count string to be used in the SQL call. Default is COUNT(*).
487 * @return int The count of records returned from the specified criteria.
d0d56205 488 */
d4419d55 489function count_records_select($table, $select='', $countitem='COUNT(*)') {
9fa49e22 490
491 global $CFG;
492
d26d7ed0 493 if ($select) {
d4419d55 494 $select = 'WHERE '.$select;
d26d7ed0 495 }
496
d4419d55 497 return count_records_sql('SELECT '. $countitem .' FROM '. $CFG->prefix . $table .' '. $select);
9fa49e22 498}
499
18a97fd8 500/**
eb856e3b 501 * Get the result of a SQL SELECT COUNT(...) query.
502 *
503 * Given a query that counts rows, return that count. (In fact,
504 * given any query, return the first field of the first record
505 * returned. However, this method should only be used for the
506 * intended purpose.) If an error occurrs, 0 is returned.
fbc21ae8 507 *
508 * @uses $CFG
509 * @uses $db
510 * @param string $sql The SQL string you wish to be executed.
eb856e3b 511 * @return int the count. If an error occurrs, 0 is returned.
fbc21ae8 512 */
df28d6c5 513function count_records_sql($sql) {
eb856e3b 514 $rs = get_recordset_sql($sql);
515
516 if ($rs) {
517 return $rs->fields[0];
518 } else {
519 return 0;
9c7fee6c 520 }
df28d6c5 521}
522
a3fb1c45 523/// GENERIC FUNCTIONS TO GET, INSERT, OR UPDATE DATA ///////////////////////////////////
524
18a97fd8 525/**
fbc21ae8 526 * Get a single record as an object
527 *
528 * @uses $CFG
11840632 529 * @param string $table The table to select from.
530 * @param string $field1 the first field to check (optional).
531 * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
532 * @param string $field2 the second field to check (optional).
533 * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
534 * @param string $field3 the third field to check (optional).
535 * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
536 * @return mixed a fieldset object containing the first mathcing record, or false if none found.
fbc21ae8 537 */
d0d0cd9c 538function get_record($table, $field1, $value1, $field2='', $value2='', $field3='', $value3='', $fields='*') {
8f0cd6ef 539
11840632 540 global $CFG;
df28d6c5 541
11840632 542 $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
df28d6c5 543
d0d0cd9c 544 return get_record_sql('SELECT '.$fields.' FROM '. $CFG->prefix . $table .' '. $select);
df28d6c5 545}
546
18a97fd8 547/**
fbc21ae8 548 * Get a single record as an object using the specified SQL statement
549 *
550 * A LIMIT is normally added to only look for 1 record
8a7d9d15 551 * If debugging is OFF only the first record is returned even if there is
552 * more than one matching record!
fbc21ae8 553 *
554 * @uses $CFG
555 * @uses $db
556 * @param string $sql The SQL string you wish to be executed.
8a7d9d15 557 * @return Found record as object. False if not found or error
fbc21ae8 558 */
81532b92 559function get_record_sql($sql, $expectmultiple=false, $nolimit=false) {
df28d6c5 560
eb856e3b 561 global $CFG;
df28d6c5 562
e84a246a 563 if (isset($CFG->debug) && $CFG->debug > 7 && !$expectmultiple) { // Debugging mode - don't use limit
d4419d55 564 $limit = '';
81532b92 565 } else if ($nolimit) {
566 $limit = '';
4d7a3735 567 } else {
d4419d55 568 $limit = ' LIMIT 1'; // Workaround - limit to one record
4d7a3735 569 }
570
eb856e3b 571 if (!$rs = get_recordset_sql($sql . $limit)) {
572 return false;
7618a8eb 573 }
eb856e3b 574
575 $recordcount = $rs->RecordCount();
4d7a3735 576
eb856e3b 577 if ($recordcount == 0) { // Found no records
578 return false;
df28d6c5 579
eb856e3b 580 } else if ($recordcount == 1) { // Found one record
df28d6c5 581 return (object)$rs->fields;
4d7a3735 582
7618a8eb 583 } else { // Error: found more than one record
d4419d55 584 notify('Error: Turn off debugging to hide this error.');
585 notify($sql . $limit);
4d7a3735 586 if ($records = $rs->GetAssoc(true)) {
d4419d55 587 notify('Found more than one record in get_record_sql !');
4d7a3735 588 print_object($records);
4d7a3735 589 } else {
d4419d55 590 notify('Very strange error in get_record_sql !');
7618a8eb 591 print_object($rs);
4d7a3735 592 }
51b0e824 593 print_continue("$CFG->wwwroot/$CFG->admin/config.php");
df28d6c5 594 }
595}
596
18a97fd8 597/**
fbc21ae8 598 * Gets one record from a table, as an object
599 *
600 * @uses $CFG
601 * @param string $table The database table to be checked against.
602 * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
603 * @param string $fields A comma separated list of fields to be returned from the chosen table.
7290c7fa 604 * @return object|false Returns an array of found records (as objects) or false if no records or error occured.
d0d56205 605 */
d4419d55 606function get_record_select($table, $select='', $fields='*') {
18496c59 607
608 global $CFG;
609
610 if ($select) {
d4419d55 611 $select = 'WHERE '. $select;
18496c59 612 }
613
d4419d55 614 return get_record_sql('SELECT '. $fields .' FROM '. $CFG->prefix . $table .' '. $select);
18496c59 615}
616
0934b9de 617/**
618 * Get a number of records as an ADODB RecordSet.
619 *
620 * Selects records from the table $table.
621 *
622 * If specified, only records where the field $field has value $value are retured.
623 *
624 * If specified, the results will be sorted as specified by $sort. This
625 * is added to the SQL as "ORDER BY $sort". Example values of $sort
626 * mightbe "time ASC" or "time DESC".
627 *
628 * If $fields is specified, only those fields are returned.
629 * Use this wherever possible to reduce memory requirements.
630 *
631 * If you only want some of the records, specify $limitfrom and $limitnum.
632 * The query will skip the first $limitfrom records (according to the sort
633 * order) and then return the next $limitnum records. If either of $limitfrom
634 * or $limitnum is specified, both must be present.
635 *
636 * The return value is an ADODB RecordSet object
637 * @link http://phplens.com/adodb/reference.functions.adorecordset.html
638 * if the query succeeds. If an error occurrs, false is returned.
639 *
640 * @param string $table the table to query.
641 * @param string $field a field to check (optional).
642 * @param string $value the value the field must have (requred if field1 is given, else optional).
643 * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
644 * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
645 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
646 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
647 * @return mixed an ADODB RecordSet object, or false if an error occured.
648 */
649function get_recordset($table, $field='', $value='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
650
651 if ($field) {
652 $select = "$field = '$value'";
653 } else {
654 $select = '';
655 }
656
d53c5dfc 657 return get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
0934b9de 658}
659
660/**
661 * Get a number of records as an ADODB RecordSet.
662 *
663 * If given, $select is used as the SELECT parameter in the SQL query,
664 * otherwise all records from the table are returned.
665 *
666 * Other arguments and the return type as for @see function get_recordset.
667 *
668 * @uses $CFG
669 * @param string $table the table to query.
670 * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
671 * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
672 * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
673 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
674 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
675 * @return mixed an ADODB RecordSet object, or false if an error occured.
676 */
677function get_recordset_select($table, $select='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
678
679 global $CFG;
680
681 if ($select) {
682 $select = ' WHERE '. $select;
683 }
684
685 if ($limitfrom !== '') {
686 $limit = sql_paging_limit($limitfrom, $limitnum);
687 } else {
688 $limit = '';
689 }
690
691 if ($sort) {
692 $sort = ' ORDER BY '. $sort;
693 }
694
695 return get_recordset_sql('SELECT '. $fields .' FROM '. $CFG->prefix . $table . $select . $sort .' '. $limit);
696}
697
698/**
699 * Get a number of records as an ADODB RecordSet.
700 *
701 * Only records where $field takes one of the values $values are returned.
702 * $values should be a comma-separated list of values, for example "4,5,6,10"
703 * or "'foo','bar','baz'".
704 *
705 * Other arguments and the return type as for @see function get_recordset.
706 *
707 * @param string $table the table to query.
708 * @param string $field a field to check (optional).
709 * @param string $values the value the field must have (requred if field1 is given, else optional).
710 * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
711 * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
712 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
713 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
714 * @return mixed an ADODB RecordSet object, or false if an error occured.
715 */
716function get_recordset_list($table, $field='', $values='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
717
0934b9de 718 if ($field) {
719 $select = "$field IN ($values)";
720 } else {
721 $select = '';
722 }
723
18147bc2 724 return get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
0934b9de 725}
726
727/**
728 * Get a number of records as an ADODB RecordSet.
729 *
730 * $sql must be a complete SQL query.
731 *
732 * The return type is as for @see function get_recordset.
733 *
734 * @uses $CFG
735 * @uses $db
736 * @param string $sql the SQL select query to execute.
737 * @return mixed an ADODB RecordSet object, or false if an error occured.
738 */
739function get_recordset_sql($sql) {
740
741 global $CFG, $db;
742
743 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
744
745 if (!$rs = $db->Execute($sql)) {
746 if (isset($CFG->debug) and $CFG->debug > 7) {
747 notify($db->ErrorMsg() .'<br /><br />'. $sql);
748 }
749 if (!empty($CFG->dblogerror)) {
750 $debug=array_shift(debug_backtrace());
751 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
752 }
753 return false;
754 }
755
756 return $rs;
757}
18496c59 758
18a97fd8 759/**
9b277c0d 760 * Utility function used by the following 4 methods.
761 *
762 * @param object an ADODB RecordSet object.
763 * @return mixed mixed an array of objects, or false if an error occured or the RecordSet was empty.
764 */
765function recordset_to_array($rs) {
766 if ($rs && $rs->RecordCount() > 0) {
767 if ($records = $rs->GetAssoc(true)) {
768 foreach ($records as $key => $record) {
769 $objects[$key] = (object) $record;
770 }
771 return $objects;
772 } else {
773 return false;
774 }
775 } else {
776 return false;
777 }
778}
779
780/**
781 * Get a number of records as an array of objects.
fbc21ae8 782 *
9b277c0d 783 * Convenience call -- use only for small datasets.
784 * Consider using @see function get_recordset instead.
fbc21ae8 785 *
9b277c0d 786 * Arguments as for @see function get_recordset.
787 *
788 * If the query succeeds and returns at least one record, the
789 * return value is an array of objects, one object for each
790 * record found. The array key is the value from the first
791 * column of the result set. The object associated with that key
792 * has a member variable for each column of the results.
793 *
794 * @param string $table the table to query.
11840632 795 * @param string $field a field to check (optional).
796 * @param string $value the value the field must have (requred if field1 is given, else optional).
9b277c0d 797 * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
798 * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
799 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
800 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
801 * @return mixed an array of objects, or false if no records were found or an error occured.
fbc21ae8 802 */
d4419d55 803function get_records($table, $field='', $value='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
9b277c0d 804 $rs = get_recordset($table, $field, $value, $sort, $fields, $limitfrom, $limitnum);
805 return recordset_to_array($rs);
df28d6c5 806}
807
18a97fd8 808/**
9b277c0d 809 * Get a number of records as an array of objects.
fbc21ae8 810 *
9b277c0d 811 * Convenience call -- use only for small datasets.
812 * Consider using @see function get_recordset_select instead.
fbc21ae8 813 *
9b277c0d 814 * Arguments as for @see function get_recordset_select.
815 * Return value as for @see function get_records.
816 *
817 * @param string $table the table to query.
fbc21ae8 818 * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
9b277c0d 819 * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
820 * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
821 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
822 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
823 * @return mixed an array of objects, or false if no records were found or an error occured.
fbc21ae8 824 */
d4419d55 825function get_records_select($table, $select='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
9b277c0d 826 $rs = get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
827 return recordset_to_array($rs);
9fa49e22 828}
829
18a97fd8 830/**
9b277c0d 831 * Get a number of records as an array of objects.
fbc21ae8 832 *
9b277c0d 833 * Convenience call -- use only for small datasets.
834 * Consider using @see function get_recordset_list instead.
835 *
836 * Arguments as for @see function get_recordset_list.
837 * Return value as for @see function get_records.
fbc21ae8 838 *
fbc21ae8 839 * @param string $table The database table to be checked against.
8a7d9d15 840 * @param string $field The field to search
841 * @param string $values Comma separated list of possible value
842 * @param string $sort Sort order (as valid SQL sort parameter)
fbc21ae8 843 * @param string $fields A comma separated list of fields to be returned from the chosen table.
9b277c0d 844 * @return mixed an array of objects, or false if no records were found or an error occured.
fbc21ae8 845 */
9b277c0d 846function get_records_list($table, $field='', $values='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
847 $rs = get_recordset_list($table, $field, $values, $sort, $fields, $limitfrom, $limitnum);
848 return recordset_to_array($rs);
df28d6c5 849}
850
18a97fd8 851/**
9b277c0d 852 * Get a number of records as an array of objects.
fbc21ae8 853 *
9b277c0d 854 * Convenience call -- use only for small datasets.
855 * Consider using @see function get_recordset_sql instead.
fbc21ae8 856 *
9b277c0d 857 * Arguments as for @see function get_recordset_sql.
858 * Return value as for @see function get_records.
859 *
860 * @param string $sql the SQL select query to execute.
861 * @return mixed an array of objects, or false if no records were found or an error occured.
fbc21ae8 862 */
df28d6c5 863function get_records_sql($sql) {
9b277c0d 864 $rs = get_recordset_sql($sql);
865 return recordset_to_array($rs);
df28d6c5 866}
867
18a97fd8 868/**
26c0a587 869 * Utility function used by the following 3 methods.
870 *
871 * @param object an ADODB RecordSet object with two columns.
872 * @return mixed an associative array, or false if an error occured or the RecordSet was empty.
873 */
874function recordset_to_menu($rs) {
875 if ($rs && $rs->RecordCount() > 0) {
876 while (!$rs->EOF) {
877 $menu[$rs->fields[0]] = $rs->fields[1];
878 $rs->MoveNext();
879 }
880 return $menu;
881 } else {
882 return false;
883 }
884}
885
886/**
887 * Get the first two columns from a number of records as an associative array.
11840632 888 *
26c0a587 889 * Arguments as for @see function get_recordset.
890 *
891 * If no errors occur, and at least one records is found, the return value
892 * is an associative whose keys come from the first field of each record,
893 * and whose values are the corresponding second fields. If no records are found,
894 * or an error occurs, false is returned.
11840632 895 *
26c0a587 896 * @param string $table the table to query.
11840632 897 * @param string $field a field to check (optional).
898 * @param string $value the value the field must have (requred if field1 is given, else optional).
26c0a587 899 * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
900 * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
901 * @return mixed an associative array, or false if no records were found or an error occured.
11840632 902 */
d4419d55 903function get_records_menu($table, $field='', $value='', $sort='', $fields='*') {
26c0a587 904 $rs = get_recordset($table, $field, $value, $sort, $fields);
905 return recordset_to_menu($rs);
9fa49e22 906}
907
18a97fd8 908/**
26c0a587 909 * Get the first two columns from a number of records as an associative array.
910 *
911 * Arguments as for @see function get_recordset_select.
912 * Return value as for @see function get_records_menu.
fbc21ae8 913 *
fbc21ae8 914 * @param string $table The database table to be checked against.
915 * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
8a7d9d15 916 * @param string $sort Sort order (optional) - a valid SQL order parameter
fbc21ae8 917 * @param string $fields A comma separated list of fields to be returned from the chosen table.
26c0a587 918 * @return mixed an associative array, or false if no records were found or an error occured.
fbc21ae8 919 */
d4419d55 920function get_records_select_menu($table, $select='', $sort='', $fields='*') {
26c0a587 921 $rs = get_recordset_select($table, $select, $sort, $fields);
922 return recordset_to_menu($rs);
9fa49e22 923}
924
18a97fd8 925/**
26c0a587 926 * Get the first two columns from a number of records as an associative array.
fbc21ae8 927 *
26c0a587 928 * Arguments as for @see function get_recordset_sql.
929 * Return value as for @see function get_records_menu.
fbc21ae8 930 *
fbc21ae8 931 * @param string $sql The SQL string you wish to be executed.
26c0a587 932 * @return mixed an associative array, or false if no records were found or an error occured.
fbc21ae8 933 */
df28d6c5 934function get_records_sql_menu($sql) {
26c0a587 935 $rs = get_recordset_sql($sql);
936 return recordset_to_menu($rs);
df28d6c5 937}
938
18a97fd8 939/**
11840632 940 * Get a single value from a table row where all the given fields match the given values.
fbc21ae8 941 *
11840632 942 * @param string $table the table to query.
943 * @param string $return the field to return the value of.
944 * @param string $field1 the first field to check (optional).
945 * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
946 * @param string $field2 the second field to check (optional).
947 * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
948 * @param string $field3 the third field to check (optional).
949 * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
950 * @return mixed the specified value, or false if an error occured.
fbc21ae8 951 */
d4419d55 952function get_field($table, $return, $field1, $value1, $field2='', $value2='', $field3='', $value3='') {
eb856e3b 953
18147bc2 954 global $CFG;
11840632 955 $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
ec2a28a6 956
eb856e3b 957 $rs = get_recordset_sql('SELECT ' . $return . ' FROM ' . $CFG->prefix . $table . ' ' . $select);
853df85e 958
eb856e3b 959 if ($rs && $rs->RecordCount() == 1) {
d4419d55 960 return $rs->fields[$return];
df28d6c5 961 } else {
962 return false;
963 }
964}
965
b4bac9b6 966/**
eb856e3b 967 * Get a single value from a table.
fbc21ae8 968 *
eb856e3b 969 * @param string $sql an SQL statement expected to return a single value.
970 * @return mixed the specified value, or false if an error occured.
fbc21ae8 971 */
b4bac9b6 972function get_field_sql($sql) {
973
eb856e3b 974 $rs = get_recordset_sql($sql);
b4bac9b6 975
eb856e3b 976 if ($rs && $rs->RecordCount() == 1) {
b4bac9b6 977 return $rs->fields[0];
978 } else {
979 return false;
980 }
981}
982
061fb2e7 983/**
984 * Get an array of data from one or more fields from a database
985 * use to get a column, or a series of distinct values
986 *
987 * @uses $CFG
988 * @uses $db
989 * @param string $sql The SQL string you wish to be executed.
990 * @return mixed|false Returns the value return from the SQL statment or false if an error occured.
991 * @todo Finish documenting this function
992 */
993function get_fieldset_sql($sql) {
994
995 global $db, $CFG;
996
997 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
998
999 $rs = $db->Execute($sql);
1000 if (!$rs) {
1001 if (isset($CFG->debug) and $CFG->debug > 7) {
1002 notify($db->ErrorMsg() .'<br /><br />'. $sql);
1003 }
1004 if (!empty($CFG->dblogerror)) {
1005 $debug=array_shift(debug_backtrace());
1006 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
1007 }
1008 return false;
1009 }
1010
1011 if ( $rs->RecordCount() > 0 ) {
1012 $results = array();
1013 while (!$rs->EOF) {
1014 array_push($results, $rs->fields[0]);
1015 $rs->MoveNext();
1016 }
1017 return $results;
1018 } else {
1019 return false;
1020 }
1021}
1022
18a97fd8 1023/**
11840632 1024 * Set a single field in the table row where all the given fields match the given values.
fbc21ae8 1025 *
1026 * @uses $CFG
1027 * @uses $db
1028 * @param string $table The database table to be checked against.
11840632 1029 * @param string $newfield the field to set.
1030 * @param string $newvalue the value to set the field to.
1031 * @param string $field1 the first field to check (optional).
1032 * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
1033 * @param string $field2 the second field to check (optional).
1034 * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
1035 * @param string $field3 the third field to check (optional).
1036 * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
1037 * @return mixed An ADODB RecordSet object with the results from the SQL call or false.
fbc21ae8 1038 */
d4419d55 1039function set_field($table, $newfield, $newvalue, $field1, $value1, $field2='', $value2='', $field3='', $value3='') {
df28d6c5 1040
1041 global $db, $CFG;
1042
853df85e 1043 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
1044
11840632 1045 $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
ec2a28a6 1046
d4419d55 1047 return $db->Execute('UPDATE '. $CFG->prefix . $table .' SET '. $newfield .' = \''. $newvalue .'\' '. $select);
df28d6c5 1048}
1049
18a97fd8 1050/**
11840632 1051 * Delete the records from a table where all the given fields match the given values.
fbc21ae8 1052 *
1053 * @uses $CFG
1054 * @uses $db
11840632 1055 * @param string $table the table to delete from.
1056 * @param string $field1 the first field to check (optional).
1057 * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
1058 * @param string $field2 the second field to check (optional).
1059 * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
1060 * @param string $field3 the third field to check (optional).
1061 * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
1062 * @return mixed An ADODB RecordSet object with the results from the SQL call or false.
fbc21ae8 1063 */
d4419d55 1064function delete_records($table, $field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
df28d6c5 1065
1066 global $db, $CFG;
1067
853df85e 1068 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
1069
11840632 1070 $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
df28d6c5 1071
d4419d55 1072 return $db->Execute('DELETE FROM '. $CFG->prefix . $table .' '. $select);
df28d6c5 1073}
1074
18a97fd8 1075/**
fbc21ae8 1076 * Delete one or more records from a table
1077 *
1078 * @uses $CFG
1079 * @uses $db
1080 * @param string $table The database table to be checked against.
1081 * @param string $select A fragment of SQL to be used in a where clause in the SQL call (used to define the selection criteria).
7290c7fa 1082 * @return object A PHP standard object with the results from the SQL call.
fbc21ae8 1083 * @todo Verify return type.
1084 */
d4419d55 1085function delete_records_select($table, $select='') {
30f89d68 1086
1087 global $CFG, $db;
1088
853df85e 1089 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
1090
30f89d68 1091 if ($select) {
d4419d55 1092 $select = 'WHERE '.$select;
30f89d68 1093 }
1094
d4419d55 1095 return $db->Execute('DELETE FROM '. $CFG->prefix . $table .' '. $select);
30f89d68 1096}
1097
18a97fd8 1098/**
fbc21ae8 1099 * Insert a record into a table and return the "id" field if required
1100 *
1101 * If the return ID isn't required, then this just reports success as true/false.
1102 * $dataobject is an object containing needed data
1103 *
1104 * @uses $db
1105 * @uses $CFG
1106 * @param string $table The database table to be checked against.
f4cbf7f2 1107 * @param array $dataobject A data object with values for one or more fields in the record
7290c7fa 1108 * @param bool $returnid Should the id of the newly created record entry be returned? If this option is not requested then true/false is returned.
f4cbf7f2 1109 * @param string $primarykey The primary key of the table we are inserting into (almost always "id")
fbc21ae8 1110 */
fcf9c450 1111function insert_record($table, $dataobject, $returnid=true, $primarykey='id') {
8f0cd6ef 1112
0892f7bd 1113 global $db, $CFG;
a2bed059 1114 static $empty_rs_cache;
0892f7bd 1115
853df85e 1116 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
1117
ff4651e6 1118 /// In Moodle we always use auto-numbering fields for the primary key
1119 /// so let's unset it now before it causes any trouble later
1120 unset($dataobject->{$primarykey});
8f0cd6ef 1121
ff4651e6 1122 /// Get an empty recordset. Cache for multiple inserts.
a2bed059 1123 if (empty($empty_rs_cache[$table])) {
a2bed059 1124 /// Execute a dummy query to get an empty recordset
1125 if (!$empty_rs_cache[$table] = $db->Execute('SELECT * FROM '. $CFG->prefix . $table .' WHERE '. $primarykey .' = \'-1\'')) {
1126 return false;
1127 }
a2bed059 1128 }
2327ce8e 1129
a2bed059 1130 $rs = $empty_rs_cache[$table];
2327ce8e 1131
a9a9bdba 1132 /// Postgres doesn't have the concept of primary key built in
1133 /// and will return the OID which isn't what we want.
1134 /// The efficient and transaction-safe strategy is to
1135 /// move the sequence forward first, and make the insert
1136 /// with an explicit id.
ff4651e6 1137 if ( $CFG->dbtype === 'postgres7' && $returnid == true ) {
9921028a 1138 if ($nextval = (int)get_field_sql("SELECT NEXTVAL('{$CFG->prefix}{$table}_{$primarykey}_seq')")) {
a9a9bdba 1139 $dataobject->{$primarykey} = $nextval;
1140 }
1141 }
1142
d4469f2a 1143/// Get the correct SQL from adoDB
1144 if (!$insertSQL = $db->GetInsertSQL($rs, (array)$dataobject, true)) {
1145 return false;
1146 }
8f0cd6ef 1147
d4469f2a 1148/// Run the SQL statement
1149 if (!$rs = $db->Execute($insertSQL)) {
2eef791f 1150 if (isset($CFG->debug) and $CFG->debug > 7) {
d4419d55 1151 notify($db->ErrorMsg() .'<br /><br />'.$insertSQL);
9c7fee6c 1152 }
acd2279e 1153 if (!empty($CFG->dblogerror)) {
31df0bb8 1154 $debug=array_shift(debug_backtrace());
acd2279e 1155 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $insertSQL");
1156 }
0892f7bd 1157 return false;
1158 }
8f0cd6ef 1159
d4469f2a 1160/// If a return ID is not needed then just return true now
8f0cd6ef 1161 if (!$returnid) {
0892f7bd 1162 return true;
1163 }
1164
f4cbf7f2 1165/// We already know the record PK if it's been passed explicitly,
1166/// or if we've retrieved it from a sequence (Postgres).
2eab0668 1167 if (!empty($dataobject->{$primarykey})) {
a9a9bdba 1168 return $dataobject->{$primarykey};
1169 }
0892f7bd 1170
a9a9bdba 1171/// This only gets triggered with non-Postgres databases
1172/// however we have some postgres fallback in case we failed
1173/// to find the sequence.
1174 $id = $db->Insert_ID();
1175
1176 if ($CFG->dbtype === 'postgres7') {
1177 // try to get the primary key based on id
1178 if ( ($rs = $db->Execute('SELECT '. $primarykey .' FROM '. $CFG->prefix . $table .' WHERE oid = '. $id))
1179 && ($rs->RecordCount() == 1) ) {
1180 trigger_error("Retrieved $primarykey from oid on table $table because we could not find the sequence.");
1181 return (integer)$rs->fields[0];
1182 }
f4cbf7f2 1183 trigger_error('Failed to retrieve primary key after insert: SELECT '. $primarykey .
1184 ' FROM '. $CFG->prefix . $table .' WHERE oid = '. $id);
a9a9bdba 1185 return false;
0892f7bd 1186 }
a9a9bdba 1187
1188 return (integer)$id;
0892f7bd 1189}
1190
11a052a6 1191/**
1192 * Escape all dangerous characters in a data record
1193 *
1194 * $dataobject is an object containing needed data
1195 * Run over each field exectuting addslashes() function
1196 * to escape SQL unfriendly characters (e.g. quotes)
1197 * Handy when writing back data read from the database
1198 *
1199 * @param $dataobject Object containing the database record
1200 * @return object Same object with neccessary characters escaped
1201 */
1202function addslashes_object( $dataobject ) {
1203 $a = get_object_vars( $dataobject);
1204 foreach ($a as $key=>$value) {
1205 $a[$key] = addslashes( $value );
1206 }
1207 return (object)$a;
1208}
0892f7bd 1209
18a97fd8 1210/**
fbc21ae8 1211 * Update a record in a table
1212 *
1213 * $dataobject is an object containing needed data
1214 * Relies on $dataobject having a variable "id" to
1215 * specify the record to update
1216 *
1217 * @uses $CFG
1218 * @uses $db
1219 * @param string $table The database table to be checked against.
11a052a6 1220 * @param array $dataobject An object with contents equal to fieldname=>fieldvalue. Must have an entry for 'id' to map to the table specified.
7290c7fa 1221 * @return bool
fbc21ae8 1222 * @todo Finish documenting this function. Dataobject is actually an associateive array, correct?
1223 */
df28d6c5 1224function update_record($table, $dataobject) {
df28d6c5 1225
1226 global $db, $CFG;
1227
1228 if (! isset($dataobject->id) ) {
1229 return false;
1230 }
1231
1232 // Determine all the fields in the table
d4419d55 1233 if (!$columns = $db->MetaColumns($CFG->prefix . $table)) {
df28d6c5 1234 return false;
1235 }
1236 $data = (array)$dataobject;
1237
853df85e 1238 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
1239
df28d6c5 1240 // Pull out data matching these fields
1241 foreach ($columns as $column) {
d4419d55 1242 if ($column->name <> 'id' and isset($data[$column->name]) ) {
df28d6c5 1243 $ddd[$column->name] = $data[$column->name];
1244 }
1245 }
1246
1247 // Construct SQL queries
1248 $numddd = count($ddd);
1249 $count = 0;
d4419d55 1250 $update = '';
df28d6c5 1251
1252 foreach ($ddd as $key => $value) {
1253 $count++;
705a2aa4 1254 $update .= $key .' = \''. $value .'\''; // All incoming data is already quoted
df28d6c5 1255 if ($count < $numddd) {
d4419d55 1256 $update .= ', ';
df28d6c5 1257 }
1258 }
1259
d4419d55 1260 if ($rs = $db->Execute('UPDATE '. $CFG->prefix . $table .' SET '. $update .' WHERE id = \''. $dataobject->id .'\'')) {
df28d6c5 1261 return true;
1262 } else {
2eef791f 1263 if (isset($CFG->debug) and $CFG->debug > 7) {
d4419d55 1264 notify($db->ErrorMsg() .'<br /><br />UPDATE '. $CFG->prefix . $table .' SET '. $update .' WHERE id = \''. $dataobject->id .'\'');
9c7fee6c 1265 }
acd2279e 1266 if (!empty($CFG->dblogerror)) {
31df0bb8 1267 $debug=array_shift(debug_backtrace());
acd2279e 1268 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: UPDATE $CFG->prefix$table SET $update WHERE id = '$dataobject->id'");
1269 }
df28d6c5 1270 return false;
1271 }
1272}
1273
1274
df28d6c5 1275
1276
1277/// USER DATABASE ////////////////////////////////////////////////
1278
18a97fd8 1279/**
fbc21ae8 1280 * Does this username and password specify a valid admin user?
1281 *
1282 * @uses $CFG
1283 * @param string $username The name of the user to be tested for admin rights
1284 * @param string $md5password The password supplied by the user in md5 encrypted format.
7290c7fa 1285 * @return bool
fbc21ae8 1286 */
df28d6c5 1287function adminlogin($username, $md5password) {
df28d6c5 1288
1289 global $CFG;
1290
8f0cd6ef 1291 return record_exists_sql("SELECT u.id
1292 FROM {$CFG->prefix}user u,
1293 {$CFG->prefix}user_admins a
1294 WHERE u.id = a.userid
1295 AND u.username = '$username'
df28d6c5 1296 AND u.password = '$md5password'");
1297}
1298
18a97fd8 1299/**
fbc21ae8 1300 * Get the guest user information from the database
1301 *
1302 * @return object(user) An associative array with the details of the guest user account.
1303 * @todo Is object(user) a correct return type? Or is array the proper return type with a note that the contents include all details for a user.
1304 */
9fa49e22 1305function get_guest() {
7c12949d 1306 return get_complete_user_data('username', 'guest');
9fa49e22 1307}
1308
1309
18a97fd8 1310/**
fbc21ae8 1311 * Returns $user object of the main admin user
1312 *
1313 * @uses $CFG
1314 * @return object(admin) An associative array representing the admin user.
1315 * @todo Verify documentation of this function
1316 */
df28d6c5 1317function get_admin () {
df28d6c5 1318
1319 global $CFG;
1320
1321 if ( $admins = get_admins() ) {
1322 foreach ($admins as $admin) {
8f0cd6ef 1323 return $admin; // ie the first one
df28d6c5 1324 }
1325 } else {
1326 return false;
1327 }
1328}
1329
18a97fd8 1330/**
fbc21ae8 1331 * Returns list of all admins
1332 *
1333 * @uses $CFG
7290c7fa 1334 * @return object
fbc21ae8 1335 */
df28d6c5 1336function get_admins() {
df28d6c5 1337
1338 global $CFG;
1339
8f0cd6ef 1340 return get_records_sql("SELECT u.*, a.id as adminid
1341 FROM {$CFG->prefix}user u,
ebc3bd2b 1342 {$CFG->prefix}user_admins a
1343 WHERE a.userid = u.id
427fdf66 1344 ORDER BY a.id ASC");
df28d6c5 1345}
1346
18a97fd8 1347/**
fbc21ae8 1348 * Returns list of all creators
1349 *
1350 * @uses $CFG
7290c7fa 1351 * @return object
fbc21ae8 1352 */
1924074c 1353function get_creators() {
1924074c 1354
1355 global $CFG;
1356
1357 return get_records_sql("SELECT u.*
1358 FROM {$CFG->prefix}user u,
1359 {$CFG->prefix}user_coursecreators a
1360 WHERE a.userid = u.id
1361 ORDER BY u.id ASC");
1362}
df28d6c5 1363
b61efafb 1364function get_courses_in_metacourse($metacourseid) {
1365 global $CFG;
1366
5f37b628 1367 $sql = "SELECT c.id,c.shortname,c.fullname FROM {$CFG->prefix}course c, {$CFG->prefix}course_meta mc WHERE mc.parent_course = $metacourseid
5afa0de6 1368 AND mc.child_course = c.id ORDER BY c.shortname";
b61efafb 1369
1370 return get_records_sql($sql);
1371}
1372
1373function get_courses_notin_metacourse($metacourseid,$count=false) {
1374
1375 global $CFG;
1376
b61efafb 1377 if ($count) {
1378 $sql = "SELECT COUNT(c.id)";
c44d5d42 1379 } else {
b61efafb 1380 $sql = "SELECT c.id,c.shortname,c.fullname";
1381 }
178ccd11 1382
ffed6bf3 1383 $alreadycourses = get_courses_in_metacourse($metacourseid);
1384
c44d5d42 1385 $sql .= " FROM {$CFG->prefix}course c WHERE ".((!empty($alreadycourses)) ? "c.id NOT IN (".implode(',',array_keys($alreadycourses)).")
5afa0de6 1386 AND " : "")." c.id !=$metacourseid and c.id != ".SITEID." and c.metacourse != 1 ".((empty($count)) ? " ORDER BY c.shortname" : "");
1387
b61efafb 1388 return get_records_sql($sql);
1389}
1390
1391
18a97fd8 1392/**
fbc21ae8 1393 * Returns $user object of the main teacher for a course
1394 *
1395 * @uses $CFG
1396 * @param int $courseid The course in question.
89dcb99d 1397 * @return user|false A {@link $USER} record of the main teacher for the specified course or false if error.
fbc21ae8 1398 * @todo Finish documenting this function
1399 */
df28d6c5 1400function get_teacher($courseid) {
df28d6c5 1401
1402 global $CFG;
1403
d4419d55 1404 if ( $teachers = get_course_teachers($courseid, 't.authority ASC')) {
df28d6c5 1405 foreach ($teachers as $teacher) {
1406 if ($teacher->authority) {
1407 return $teacher; // the highest authority teacher
1408 }
1409 }
1410 } else {
1411 return false;
1412 }
1413}
1414
18a97fd8 1415/**
fbc21ae8 1416 * Searches logs to find all enrolments since a certain date
1417 *
1418 * used to print recent activity
1419 *
1420 * @uses $CFG
1421 * @param int $courseid The course in question.
7290c7fa 1422 * @return object|false {@link $USER} records or false if error.
fbc21ae8 1423 * @todo Finish documenting this function
1424 */
0a08581d 1425function get_recent_enrolments($courseid, $timestart) {
0a08581d 1426
1427 global $CFG;
1428
fbf5081c 1429 return get_records_sql("SELECT DISTINCT u.id, u.firstname, u.lastname, l.time
0a08581d 1430 FROM {$CFG->prefix}user u,
1431 {$CFG->prefix}user_students s,
1432 {$CFG->prefix}log l
8f0cd6ef 1433 WHERE l.time > '$timestart'
0a08581d 1434 AND l.course = '$courseid'
8f0cd6ef 1435 AND l.module = 'course'
0a08581d 1436 AND l.action = 'enrol'
1437 AND l.info = u.id
1438 AND u.id = s.userid
64cbb55c 1439 AND s.course = '$courseid'
0a08581d 1440 ORDER BY l.time ASC");
1441}
1442
18a97fd8 1443/**
fbc21ae8 1444 * Returns array of userinfo of all students in this course
1445 * or on this site if courseid is id of site
1446 *
1447 * @uses $CFG
1448 * @uses SITEID
1449 * @param int $courseid The course in question.
1450 * @param string $sort ?
1451 * @param string $dir ?
1452 * @param int $page ?
1453 * @param int $recordsperpage ?
1454 * @param string $firstinitial ?
1455 * @param string $lastinitial ?
1456 * @param ? $group ?
1457 * @param string $search ?
1458 * @param string $fields A comma separated list of fields to be returned from the chosen table.
1459 * @param string $exceptions ?
7290c7fa 1460 * @return object
fbc21ae8 1461 * @todo Finish documenting this function
1462 */
d4419d55 1463function get_course_students($courseid, $sort='s.timeaccess', $dir='', $page=0, $recordsperpage=99999,
1464 $firstinitial='', $lastinitial='', $group=NULL, $search='', $fields='', $exceptions='') {
df28d6c5 1465
1466 global $CFG;
8f0cd6ef 1467
1468 if ($courseid == SITEID and $CFG->allusersaresitestudents) {
2700d113 1469 // return users with confirmed, undeleted accounts who are not site teachers
1470 // the following is a mess because of different conventions in the different user functions
1471 $sort = str_replace('s.timeaccess', 'lastaccess', $sort); // site users can't be sorted by timeaccess
1472 $sort = str_replace('timeaccess', 'lastaccess', $sort); // site users can't be sorted by timeaccess
1473 $sort = str_replace('u.', '', $sort); // the get_user function doesn't use the u. prefix to fields
8f0cd6ef 1474 $fields = str_replace('u.', '', $fields);
2700d113 1475 if ($sort) {
d4419d55 1476 $sort = $sort .' '. $dir;
2700d113 1477 }
1478 // Now we have to make sure site teachers are excluded
1479 if ($teachers = get_records('user_teachers', 'course', SITEID)) {
1480 foreach ($teachers as $teacher) {
d4419d55 1481 $exceptions .= ','. $teacher->userid;
2700d113 1482 }
1483 $exceptions = ltrim($exceptions, ',');
1484 }
8f0cd6ef 1485 return get_users(true, $search, true, $exceptions, $sort, $firstinitial, $lastinitial,
2700d113 1486 $page, $recordsperpage, $fields ? $fields : '*');
1487 }
df28d6c5 1488
29daf3a0 1489 $limit = sql_paging_limit($page, $recordsperpage);
1490 $LIKE = sql_ilike();
1491 $fullname = sql_fullname('u.firstname','u.lastname');
a328425d 1492
0720313b 1493 $groupmembers = '';
f950af3c 1494
1495 // make sure it works on the site course
d4419d55 1496 $select = 's.course = \''. $courseid .'\' AND ';
222ac91b 1497 if ($courseid == SITEID) {
f950af3c 1498 $select = '';
1499 }
1500
d4419d55 1501 $select .= 's.userid = u.id AND u.deleted = \'0\' ';
73fc8c1a 1502
1284a926 1503 if (!$fields) {
1504 $fields = 'u.id, u.confirmed, u.username, u.firstname, u.lastname, '.
1505 'u.maildisplay, u.mailformat, u.maildigest, u.email, u.city, '.
1506 'u.country, u.picture, u.idnumber, u.department, u.institution, '.
1507 'u.emailstop, u.lang, u.timezone, s.timeaccess as lastaccess';
1508 }
1509
73fc8c1a 1510 if ($search) {
d4419d55 1511 $search = ' AND ('. $fullname .' '. $LIKE .'\'%'. $search .'%\' OR email '. $LIKE .'\'%'. $search .'%\') ';
73fc8c1a 1512 }
a328425d 1513
1514 if ($firstinitial) {
d4419d55 1515 $select .= ' AND u.firstname '. $LIKE .'\''. $firstinitial .'%\' ';
a328425d 1516 }
1517
1518 if ($lastinitial) {
d4419d55 1519 $select .= ' AND u.lastname '. $LIKE .'\''. $lastinitial .'%\' ';
4969ad74 1520 }
1521
3d35e6b7 1522 if ($group === 0) { /// Need something here to get all students not in a group
1523 return array();
1524
1525 } else if ($group !== NULL) {
d4419d55 1526 $groupmembers = ', '. $CFG->prefix .'groups_members gm ';
1527 $select .= ' AND u.id = gm.userid AND gm.groupid = \''. $group .'\'';
0720313b 1528 }
8f0cd6ef 1529
900df8b6 1530 if (!empty($exceptions)) {
d4419d55 1531 $select .= ' AND u.id NOT IN ('. $exceptions .')';
900df8b6 1532 }
0720313b 1533
8e4c9ef7 1534 if ($sort) {
d4419d55 1535 $sort = ' ORDER BY '. $sort .' ';
8e4c9ef7 1536 }
1537
2700d113 1538 $students = get_records_sql("SELECT $fields
488acd1b 1539 FROM {$CFG->prefix}user u,
1540 {$CFG->prefix}user_students s
1541 $groupmembers
73fc8c1a 1542 WHERE $select $search $sort $dir $limit");
2700d113 1543
1544 if ($courseid != SITEID) {
1545 return $students;
1546 }
8f0cd6ef 1547
2700d113 1548 // We are here because we need the students for the site.
1549 // These also include teachers on real courses minus those on the site
1550 if ($teachers = get_records('user_teachers', 'course', SITEID)) {
1551 foreach ($teachers as $teacher) {
d4419d55 1552 $exceptions .= ','. $teacher->userid;
2700d113 1553 }
1554 $exceptions = ltrim($exceptions, ',');
d4419d55 1555 $select .= ' AND u.id NOT IN ('. $exceptions .')';
2700d113 1556 }
1557 if (!$teachers = get_records_sql("SELECT $fields
1558 FROM {$CFG->prefix}user u,
1559 {$CFG->prefix}user_teachers s
1560 $groupmembers
1561 WHERE $select $search $sort $dir $limit")) {
1562 return $students;
1563 }
1564 if (!$students) {
1565 return $teachers;
1566 }
1567 return $teachers + $students;
df28d6c5 1568}
1569
fbc21ae8 1570
a328425d 1571/**
fbc21ae8 1572 * Counts the students in a given course (or site), or a subset of them
1573 *
1574 * @param object $course The course in question as a course object.
1575 * @param string $search ?
1576 * @param string $firstinitial ?
1577 * @param string $lastinitial ?
1578 * @param ? $group ?
1579 * @param string $exceptions ?
1580 * @return int
1581 * @todo Finish documenting this function
1582 */
d4419d55 1583function count_course_students($course, $search='', $firstinitial='', $lastinitial='', $group=NULL, $exceptions='') {
a328425d 1584
2700d113 1585 if ($students = get_course_students($course->id, '', '', 0, 999999, $firstinitial, $lastinitial, $group, $search, '', $exceptions)) {
1586 return count($students);
a328425d 1587 }
2700d113 1588 return 0;
a328425d 1589}
1590
1591
18a97fd8 1592/**
fbc21ae8 1593 * Returns list of all teachers in this course
1594 *
1595 * If $courseid matches the site id then this function
1596 * returns a list of all teachers for the site.
1597 *
1598 * @uses $CFG
1599 * @param int $courseid The course in question.
1600 * @param string $sort ?
1601 * @param string $exceptions ?
7290c7fa 1602 * @return object
fbc21ae8 1603 * @todo Finish documenting this function
1604 */
d4419d55 1605function get_course_teachers($courseid, $sort='t.authority ASC', $exceptions='') {
df28d6c5 1606
1607 global $CFG;
1608
900df8b6 1609 if (!empty($exceptions)) {
f6f319f8 1610 $exceptions = ' AND u.id NOT IN ('. $exceptions .') ';
1611 }
1612
1613 if (!empty($sort)) {
1614 $sort = ' ORDER by '.$sort;
900df8b6 1615 }
1616
8d241374 1617 return get_records_sql("SELECT u.id, u.username, u.firstname, u.lastname, u.maildisplay, u.mailformat, u.maildigest,
8f0cd6ef 1618 u.email, u.city, u.country, u.lastlogin, u.picture, u.lang, u.timezone,
86fb5ed5 1619 u.emailstop, t.authority,t.role,t.editall,t.timeaccess as lastaccess
8f0cd6ef 1620 FROM {$CFG->prefix}user u,
688d06f4 1621 {$CFG->prefix}user_teachers t
8f0cd6ef 1622 WHERE t.course = '$courseid' AND t.userid = u.id
f6f319f8 1623 AND u.deleted = '0' AND u.confirmed = '1' $exceptions $sort");
df28d6c5 1624}
1625
18a97fd8 1626/**
fbc21ae8 1627 * Returns all the users of a course: students and teachers
1628 *
1629 * @param int $courseid The course in question.
1630 * @param string $sort ?
1631 * @param string $exceptions ?
1632 * @param string $fields A comma separated list of fields to be returned from the chosen table.
7290c7fa 1633 * @return object
fbc21ae8 1634 * @todo Finish documenting this function
1635 */
d4419d55 1636function get_course_users($courseid, $sort='timeaccess DESC', $exceptions='', $fields='') {
353d0338 1637
900df8b6 1638 /// Using this method because the single SQL is too inefficient
1639 // Note that this has the effect that teachers and students are
1640 // sorted individually. Returns first all teachers, then all students
0720313b 1641
900df8b6 1642 if (!$teachers = get_course_teachers($courseid, $sort, $exceptions)) {
1643 $teachers = array();
1644 }
65ee9c16 1645 if (!$students = get_course_students($courseid, $sort, '', 0, 99999, '', '', NULL, '', $fields, $exceptions)) {
900df8b6 1646 $students = array();
1647 }
df28d6c5 1648
900df8b6 1649 return $teachers + $students;
0720313b 1650
900df8b6 1651}
0720313b 1652
900df8b6 1653/**
fbc21ae8 1654 * Search through course users
1655 *
1656 * If $coursid specifies the site course then this function searches
1657 * through all undeleted and confirmed users
1658 *
1659 * @uses $CFG
1660 * @uses SITEID
1661 * @param int $courseid The course in question.
1662 * @param int $groupid The group in question.
1663 * @param string $searchtext ?
1664 * @param string $sort ?
1665 * @param string $exceptions ?
7290c7fa 1666 * @return object
fbc21ae8 1667 * @todo Finish documenting this function
1668 */
900df8b6 1669function search_users($courseid, $groupid, $searchtext, $sort='', $exceptions='') {
1670 global $CFG;
0720313b 1671
29daf3a0 1672 $LIKE = sql_ilike();
1673 $fullname = sql_fullname('u.firstname', 'u.lastname');
8f0cd6ef 1674
900df8b6 1675 if (!empty($exceptions)) {
d4419d55 1676 $except = ' AND u.id NOT IN ('. $exceptions .') ';
900df8b6 1677 } else {
1678 $except = '';
1679 }
2700d113 1680
900df8b6 1681 if (!empty($sort)) {
d4419d55 1682 $order = ' ORDER BY '. $sort;
900df8b6 1683 } else {
1684 $order = '';
1685 }
8f0cd6ef 1686
d4419d55 1687 $select = 'u.deleted = \'0\' AND u.confirmed = \'1\'';
2700d113 1688
222ac91b 1689 if (!$courseid or $courseid == SITEID) {
2700d113 1690 return get_records_sql("SELECT u.id, u.firstname, u.lastname, u.email
1691 FROM {$CFG->prefix}user u
1692 WHERE $select
900df8b6 1693 AND ($fullname $LIKE '%$searchtext%' OR u.email $LIKE '%$searchtext%')
2700d113 1694 $except $order");
8f0cd6ef 1695 } else {
2700d113 1696
900df8b6 1697 if ($groupid) {
1698 return get_records_sql("SELECT u.id, u.firstname, u.lastname, u.email
8f0cd6ef 1699 FROM {$CFG->prefix}user u,
900df8b6 1700 {$CFG->prefix}groups_members g
2700d113 1701 WHERE $select AND g.groupid = '$groupid' AND g.userid = u.id
900df8b6 1702 AND ($fullname $LIKE '%$searchtext%' OR u.email $LIKE '%$searchtext%')
1703 $except $order");
1704 } else {
1705 if (!$teachers = get_records_sql("SELECT u.id, u.firstname, u.lastname, u.email
8f0cd6ef 1706 FROM {$CFG->prefix}user u,
900df8b6 1707 {$CFG->prefix}user_teachers s
2700d113 1708 WHERE $select AND s.course = '$courseid' AND s.userid = u.id
900df8b6 1709 AND ($fullname $LIKE '%$searchtext%' OR u.email $LIKE '%$searchtext%')
1710 $except $order")) {
1711 $teachers = array();
1712 }
1713 if (!$students = get_records_sql("SELECT u.id, u.firstname, u.lastname, u.email
8f0cd6ef 1714 FROM {$CFG->prefix}user u,
900df8b6 1715 {$CFG->prefix}user_students s
2700d113 1716 WHERE $select AND s.course = '$courseid' AND s.userid = u.id
900df8b6 1717 AND ($fullname $LIKE '%$searchtext%' OR u.email $LIKE '%$searchtext%')
1718 $except $order")) {
1719 $students = array();
1720 }
1721 return $teachers + $students;
1722 }
1723 }
df28d6c5 1724}
1725
2700d113 1726
18a97fd8 1727/**
fbc21ae8 1728 * Returns a list of all site users
1729 * Obsolete, just calls get_course_users(SITEID)
1730 *
1731 * @uses SITEID
c6d15803 1732 * @deprecated Use {@link get_course_users()} instead.
fbc21ae8 1733 * @param string $fields A comma separated list of fields to be returned from the chosen table.
7290c7fa 1734 * @return object|false {@link $USER} records or false if error.
fbc21ae8 1735 * @todo Finish documenting this function. The return type need to be better defined.
1736 */
d4419d55 1737function get_site_users($sort='u.lastaccess DESC', $fields='*', $exceptions='') {
2d0b30a0 1738
65ee9c16 1739 return get_course_users(SITEID, $sort, $exceptions, $fields);
2d0b30a0 1740}
1741
9fa49e22 1742
18a97fd8 1743/**
fbc21ae8 1744 * Returns a subset of users
1745 *
1746 * @uses $CFG
7290c7fa 1747 * @param bool $get If false then only a count of the records is returned
fbc21ae8 1748 * @param string $search A simple string to search for
7290c7fa 1749 * @param bool $confirmed A switch to allow/disallow unconfirmed users
fbc21ae8 1750 * @param array(int) $exceptions A list of IDs to ignore, eg 2,4,5,8,9,10
1751 * @param string $sort A SQL snippet for the sorting criteria to use
1752 * @param string $firstinitial ?
1753 * @param string $lastinitial ?
1754 * @param string $page ?
1755 * @param string $recordsperpage ?
1756 * @param string $fields A comma separated list of fields to be returned from the chosen table.
7290c7fa 1757 * @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 1758 * @todo Finish documenting this function. The return type needs to be better defined.
1759 */
d4419d55 1760function get_users($get=true, $search='', $confirmed=false, $exceptions='', $sort='firstname ASC',
1761 $firstinitial='', $lastinitial='', $page=0, $recordsperpage=99999, $fields='*') {
18a97fd8 1762
1763 global $CFG;
1764
29daf3a0 1765 $limit = sql_paging_limit($page, $recordsperpage);
1766 $LIKE = sql_ilike();
1767 $fullname = sql_fullname();
e384fb7b 1768
d4419d55 1769 $select = 'username <> \'guest\' AND deleted = 0';
488acd1b 1770
0044147e 1771 if (!empty($search)){
1772 $search = trim($search);
488acd1b 1773 $select .= " AND ($fullname $LIKE '%$search%' OR email $LIKE '%$search%') ";
e384fb7b 1774 }
1775
5a741655 1776 if ($confirmed) {
d4419d55 1777 $select .= ' AND confirmed = \'1\' ';
5a741655 1778 }
1779
1780 if ($exceptions) {
d4419d55 1781 $select .= ' AND id NOT IN ('. $exceptions .') ';
5a741655 1782 }
1783
488acd1b 1784 if ($firstinitial) {
d4419d55 1785 $select .= ' AND firstname '. $LIKE .' \''. $firstinitial .'%\'';
8f0cd6ef 1786 }
488acd1b 1787 if ($lastinitial) {
d4419d55 1788 $select .= ' AND lastname '. $LIKE .' \''. $lastinitial .'%\'';
8f0cd6ef 1789 }
488acd1b 1790
5a741655 1791 if ($sort and $get) {
d4419d55 1792 $sort = ' ORDER BY '. $sort .' ';
5a741655 1793 } else {
d4419d55 1794 $sort = '';
5a741655 1795 }
1796
1797 if ($get) {
d4419d55 1798 return get_records_select('user', $select .' '. $sort .' '. $limit, '', $fields);
5a741655 1799 } else {
d4419d55 1800 return count_records_select('user', $select .' '. $sort .' '. $limit);
5a741655 1801 }
9fa49e22 1802}
1803
5a741655 1804
18a97fd8 1805/**
fbc21ae8 1806 * shortdesc (optional)
1807 *
1808 * longdesc
1809 *
1810 * @uses $CFG
1811 * @param string $sort ?
1812 * @param string $dir ?
1813 * @param int $categoryid ?
1814 * @param int $categoryid ?
1815 * @param string $search ?
1816 * @param string $firstinitial ?
1817 * @param string $lastinitial ?
7290c7fa 1818 * @returnobject {@link $USER} records
fbc21ae8 1819 * @todo Finish documenting this function
1820 */
1821
d4419d55 1822function get_users_listing($sort='lastaccess', $dir='ASC', $page=0, $recordsperpage=99999,
7cf1c7bd 1823 $search='', $firstinitial='', $lastinitial='') {
488acd1b 1824
9fa49e22 1825 global $CFG;
31fefa63 1826
29daf3a0 1827 $limit = sql_paging_limit($page, $recordsperpage);
1828 $LIKE = sql_ilike();
1829 $fullname = sql_fullname();
c2a96d6b 1830
488acd1b 1831 $select = 'deleted <> 1';
1832
0044147e 1833 if (!empty($search)) {
1834 $search = trim($search);
488acd1b 1835 $select .= " AND ($fullname $LIKE '%$search%' OR email $LIKE '%$search%') ";
1836 }
1837
1838 if ($firstinitial) {
d4419d55 1839 $select .= ' AND firstname '. $LIKE .' \''. $firstinitial .'%\' ';
488acd1b 1840 }
1841
1842 if ($lastinitial) {
d4419d55 1843 $select .= ' AND lastname '. $LIKE .' \''. $lastinitial .'%\' ';
c750592a 1844 }
1845
488acd1b 1846 if ($sort) {
d4419d55 1847 $sort = ' ORDER BY '. $sort .' '. $dir;
488acd1b 1848 }
1849
1850/// warning: will return UNCONFIRMED USERS
ea5d48ee 1851 return get_records_sql("SELECT id, username, email, firstname, lastname, city, country, lastaccess, confirmed
8f0cd6ef 1852 FROM {$CFG->prefix}user
488acd1b 1853 WHERE $select $sort $limit ");
9fa49e22 1854
1855}
1856
488acd1b 1857
18a97fd8 1858/**
7290c7fa 1859 * Full list of users that have confirmed their accounts.
fbc21ae8 1860 *
1861 * @uses $CFG
7290c7fa 1862 * @return object
fbc21ae8 1863 */
9fa49e22 1864function get_users_confirmed() {
1865 global $CFG;
8f0cd6ef 1866 return get_records_sql("SELECT *
1867 FROM {$CFG->prefix}user
1868 WHERE confirmed = 1
9fa49e22 1869 AND deleted = 0
8f0cd6ef 1870 AND username <> 'guest'
9fa49e22 1871 AND username <> 'changeme'");
1872}
1873
1874
18a97fd8 1875/**
7290c7fa 1876 * Full list of users that have not yet confirmed their accounts.
fbc21ae8 1877 *
1878 * @uses $CFG
1879 * @param string $cutofftime ?
7290c7fa 1880 * @return object {@link $USER} records
fbc21ae8 1881 * @todo Finish documenting this function
1882 */
99988d1a 1883function get_users_unconfirmed($cutofftime=2000000000) {
9fa49e22 1884 global $CFG;
8f0cd6ef 1885 return get_records_sql("SELECT *
1886 FROM {$CFG->prefix}user
9fa49e22 1887 WHERE confirmed = 0
8f0cd6ef 1888 AND firstaccess > 0
9fa49e22 1889 AND firstaccess < '$cutofftime'");
1890}
1891
1892
fa22fd5f 1893/**
1894 * Full list of bogus accounts that are probably not ever going to be used
1895 *
1896 * @uses $CFG
1897 * @param string $cutofftime ?
1898 * @return object {@link $USER} records
1899 * @todo Finish documenting this function
1900 */
1901
1902function get_users_not_fully_set_up($cutofftime=2000000000) {
1903 global $CFG;
1904 return get_records_sql("SELECT *
1905 FROM {$CFG->prefix}user
1906 WHERE confirmed = 1
1907 AND lastaccess > 0
1908 AND lastaccess < '$cutofftime'
1909 AND deleted = 0
1910 AND (lastname = '' OR firstname = '' OR email = '')");
1911}
1912
1913
18a97fd8 1914/**
fbc21ae8 1915 * shortdesc (optional)
1916 *
1917 * longdesc
1918 *
1919 * @uses $CFG
1920 * @param string $cutofftime ?
7290c7fa 1921 * @return object {@link $USER} records
fbc21ae8 1922 * @todo Finish documenting this function
1923 */
9fa49e22 1924function get_users_longtimenosee($cutofftime) {
1925 global $CFG;
937ae59c 1926 return get_records_sql("SELECT DISTINCT *
1927 FROM {$CFG->prefix}user_students
8f0cd6ef 1928 WHERE timeaccess > '0'
937ae59c 1929 AND timeaccess < '$cutofftime' ");
9fa49e22 1930}
1931
f374fb10 1932/**
fbc21ae8 1933 * Returns an array of group objects that the user is a member of
1934 * in the given course. If userid isn't specified, then return a
1935 * list of all groups in the course.
1936 *
1937 * @uses $CFG
89dcb99d 1938 * @param int $courseid The id of the course in question.
fbc21ae8 1939 * @param int $userid The id of the user in question as found in the 'user' table 'id' field.
7290c7fa 1940 * @return object
fbc21ae8 1941 */
f374fb10 1942function get_groups($courseid, $userid=0) {
1943 global $CFG;
1944
1945 if ($userid) {
d4419d55 1946 $dbselect = ', '. $CFG->prefix .'groups_members m';
1947 $userselect = 'AND m.groupid = g.id AND m.userid = \''. $userid .'\'';
2d439c9d 1948 } else {
1949 $dbselect = '';
1950 $userselect = '';
f374fb10 1951 }
1952
1953 return get_records_sql("SELECT DISTINCT g.*
2d439c9d 1954 FROM {$CFG->prefix}groups g $dbselect
f374fb10 1955 WHERE g.courseid = '$courseid' $userselect ");
1956}
1957
1958
1959/**
fbc21ae8 1960 * Returns an array of user objects
1961 *
1962 * @uses $CFG
1963 * @param int $groupid The group in question.
1964 * @param string $sort ?
1965 * @param string $exceptions ?
7290c7fa 1966 * @return object
fbc21ae8 1967 * @todo Finish documenting this function
1968 */
49668367 1969function get_group_users($groupid, $sort='u.lastaccess DESC', $exceptions='', $fields='u.*') {
f374fb10 1970 global $CFG;
900df8b6 1971 if (!empty($exceptions)) {
d4419d55 1972 $except = ' AND u.id NOT IN ('. $exceptions .') ';
900df8b6 1973 } else {
1974 $except = '';
1975 }
c1147b7e 1976 // in postgres, you can't have things in sort that aren't in the select, so...
1977 $extrafield = str_replace('ASC','',$sort);
d5efb299 1978 $extrafield = str_replace('DESC','',$extrafield);
c1147b7e 1979 $extrafield = trim($extrafield);
1980 if (!empty($extrafield)) {
1981 $extrafield = ','.$extrafield;
1982 }
1983 return get_records_sql("SELECT DISTINCT $fields $extrafield
f374fb10 1984 FROM {$CFG->prefix}user u,
8f0cd6ef 1985 {$CFG->prefix}groups_members m
f374fb10 1986 WHERE m.groupid = '$groupid'
900df8b6 1987 AND m.userid = u.id $except
2c4263c4 1988 ORDER BY $sort");
f374fb10 1989}
1990
1991/**
fbc21ae8 1992 * An efficient way of finding all the users who aren't in groups yet
1993 *
1994 * Currently unimplemented.
1995 * @uses $CFG
1996 * @param int $courseid The course in question.
7290c7fa 1997 * @return object
fbc21ae8 1998 */
f374fb10 1999function get_users_not_in_group($courseid) {
2000 global $CFG;
2001
2002 return array(); /// XXX TO BE DONE
2003}
2004
60b025d1 2005/**
fbc21ae8 2006 * Returns an array of user objects
2007 *
2008 * @uses $CFG
fa22fd5f 2009 * @param int $groupid The group(s) in question.
9b10b65b 2010 * @param string $sort How to sort the results
fa22fd5f 2011 * @return object (changed to groupids)
fbc21ae8 2012 */
fa22fd5f 2013function get_group_students($groupids, $sort='u.lastaccess DESC') {
2014
60b025d1 2015 global $CFG;
fa22fd5f 2016
2017 if (is_array($groupids)){
2018 $groups = $groupids;
2019 $groupstr = '(m.groupid = '.array_shift($groups);
2020 foreach ($groups as $index => $value){
2021 $groupstr .= ' OR m.groupid = '.$value;
2022 }
2023 $groupstr .= ')';
2024 }
2025 else {
2026 $groupstr = 'm.groupid = '.$groupids;
2027 }
2028
60b025d1 2029 return get_records_sql("SELECT DISTINCT u.*
2030 FROM {$CFG->prefix}user u,
2031 {$CFG->prefix}groups_members m,
2032 {$CFG->prefix}groups g,
2033 {$CFG->prefix}user_students s
fa22fd5f 2034 WHERE $groupstr
8f0cd6ef 2035 AND m.userid = u.id
2036 AND m.groupid = g.id
60b025d1 2037 AND g.courseid = s.course
2038 AND s.userid = u.id
2039 ORDER BY $sort");
2040}
2041
9b10b65b 2042/**
2043 * Returns list of all the teachers who can access a group
2044 *
2045 * @uses $CFG
2046 * @param int $courseid The course in question.
2047 * @param int $groupid The group in question.
7290c7fa 2048 * @return object
9b10b65b 2049 */
2050function get_group_teachers($courseid, $groupid) {
2051/// Returns a list of all the teachers who can access a group
2052 if ($teachers = get_course_teachers($courseid)) {
2053 foreach ($teachers as $key => $teacher) {
2054 if ($teacher->editall) { // These can access anything
2055 continue;
2056 }
2057 if (($teacher->authority > 0) and ismember($groupid, $teacher->id)) { // Specific group teachers
2058 continue;
2059 }
9a4d45ae 2060 unset($teachers[$key]);
9b10b65b 2061 }
2062 }
2063 return $teachers;
2064}
2065
2066
f374fb10 2067/**
fbc21ae8 2068 * Returns the user's group in a particular course
2069 *
2070 * @uses $CFG
2071 * @param int $courseid The course in question.
2072 * @param int $userid The id of the user as found in the 'user' table.
fa22fd5f 2073 * @param int $groupid The id of the group the user is in.
7290c7fa 2074 * @return object
fbc21ae8 2075 * @todo Finish documenting this function
2076 */
f374fb10 2077function user_group($courseid, $userid) {
2078 global $CFG;
2079
fa22fd5f 2080 return get_records_sql("SELECT g.*
0da33e07 2081 FROM {$CFG->prefix}groups g,
2082 {$CFG->prefix}groups_members m
f374fb10 2083 WHERE g.courseid = '$courseid'
2084 AND g.id = m.groupid
fa22fd5f 2085 AND m.userid = '$userid'
2086 ORDER BY name ASC");
f374fb10 2087}
2088
2089
9fa49e22 2090
02ebf404 2091
2092/// OTHER SITE AND COURSE FUNCTIONS /////////////////////////////////////////////
2093
2094
18a97fd8 2095/**
fbc21ae8 2096 * Returns $course object of the top-level site.
2097 *
89dcb99d 2098 * @return course A {@link $COURSE} object for the site
2099 * @todo Finish documenting this function.
fbc21ae8 2100 */
c44d5d42 2101function get_site() {
2102
2103 global $SITE;
2104
2105 if (!empty($SITE->id)) { // We already have a global to use, so return that
2106 return $SITE;
2107 }
02ebf404 2108
c44d5d42 2109 if ($course = get_record('course', 'category', 0)) {
02ebf404 2110 return $course;
2111 } else {
2112 return false;
2113 }
2114}
2115
18a97fd8 2116/**
6315b1c8 2117* Returns list of courses, for whole site, or category
2118*
2119* Returns list of courses, for whole site, or category
2120*
2121* @param type description
2122*
2123* Important: Using c.* for fields is extremely expensive because
2124* we are using distinct. You almost _NEVER_ need all the fields
2125* in such a large SELECT
2126*/
2127function get_courses($categoryid="all", $sort="c.sortorder ASC", $fields="c.*") {
02ebf404 2128
8ef9cb56 2129 global $USER, $CFG;
6315b1c8 2130
2131 $categoryselect = "";
2132 if ($categoryid != "all" && is_numeric($categoryid)) {
2133 $categoryselect = "c.category = '$categoryid'";
2134 }
2135
2136 $teachertable = "";
2137 $visiblecourses = "";
2138 $sqland = "";
2139 if (!empty($categoryselect)) {
2140 $sqland = "AND ";
2141 }
2142 if (!empty($USER->id)) { // May need to check they are a teacher
2143 if (!iscreator()) {
2144 $visiblecourses = "$sqland ((c.visible > 0) OR t.userid = '$USER->id')";
2145 $teachertable = "LEFT JOIN {$CFG->prefix}user_teachers t ON t.course = c.id";
2146 }
2147 } else {
2148 $visiblecourses = "$sqland c.visible > 0";
8ef9cb56 2149 }
2150
6315b1c8 2151 if ($categoryselect or $visiblecourses) {
2152 $selectsql = "{$CFG->prefix}course c $teachertable WHERE $categoryselect $visiblecourses";
14f32609 2153 } else {
6315b1c8 2154 $selectsql = "{$CFG->prefix}course c $teachertable";
14f32609 2155 }
2156
5b66416f 2157 $extrafield = str_replace('ASC','',$sort);
2158 $extrafield = str_replace('DESC','',$extrafield);
2159 $extrafield = trim($extrafield);
2160 if (!empty($extrafield)) {
2161 $extrafield = ','.$extrafield;
2162 }
2163 return get_records_sql("SELECT ".((!empty($teachertable)) ? " DISTINCT " : "")." $fields $extrafield FROM $selectsql ".((!empty($sort)) ? "ORDER BY $sort" : ""));
8130b77b 2164}
2165
8130b77b 2166
6315b1c8 2167/**
2168* Returns list of courses, for whole site, or category
2169*
2170* Similar to get_courses, but allows paging
2171*
2172* @param type description
2173*
2174* Important: Using c.* for fields is extremely expensive because
2175* we are using distinct. You almost _NEVER_ need all the fields
2176* in such a large SELECT
2177*/
2178function get_courses_page($categoryid="all", $sort="c.sortorder ASC", $fields="c.*",
2179 &$totalcount, $limitfrom="", $limitnum="") {
c7fe5c6f 2180
8130b77b 2181 global $USER, $CFG;
2182
6315b1c8 2183 $categoryselect = "";
b565bbdf 2184 if ($categoryid != "all" && is_numeric($categoryid)) {
6315b1c8 2185 $categoryselect = "c.category = '$categoryid'";
8130b77b 2186 }
2187
6315b1c8 2188 $teachertable = "";
2189 $visiblecourses = "";
2190 $sqland = "";
2191 if (!empty($categoryselect)) {
2192 $sqland = "AND ";
c7fe5c6f 2193 }
2d2da684 2194 if (!empty($USER) and !empty($USER->id)) { // May need to check they are a teacher
6315b1c8 2195 if (!iscreator()) {
2196 $visiblecourses = "$sqland ((c.visible > 0) OR t.userid = '$USER->id')";
2197 $teachertable = "LEFT JOIN {$CFG->prefix}user_teachers t ON t.course=c.id";
2198 }
8130b77b 2199 } else {
6315b1c8 2200 $visiblecourses = "$sqland c.visible > 0";
8130b77b 2201 }
2202
6315b1c8 2203 if ($limitfrom !== "") {
29daf3a0 2204 $limit = sql_paging_limit($limitfrom, $limitnum);
6315b1c8 2205 } else {
2206 $limit = "";
02ebf404 2207 }
8ef9cb56 2208
6315b1c8 2209 $selectsql = "{$CFG->prefix}course c $teachertable WHERE $categoryselect $visiblecourses";
8ef9cb56 2210
6315b1c8 2211 $totalcount = count_records_sql("SELECT COUNT(DISTINCT c.id) FROM $selectsql");
8ef9cb56 2212
6315b1c8 2213 return get_records_sql("SELECT DISTINCT $fields FROM $selectsql ".((!empty($sort)) ? "ORDER BY $sort" : "")." $limit");
02ebf404 2214}
2215
2216
18a97fd8 2217/**
7290c7fa 2218 * List of courses that a user is a member of.
fbc21ae8 2219 *
2220 * @uses $CFG
7290c7fa 2221 * @param int $userid The user of interest
fbc21ae8 2222 * @param string $sort ?
7290c7fa 2223 * @return object {@link $COURSE} records
fbc21ae8 2224 */
d4419d55 2225function get_my_courses($userid, $sort='visible DESC,sortorder ASC') {
2f3499b7 2226
7fb0fec7 2227 global $CFG, $USER;
02ebf404 2228
2f3499b7 2229 $course = array();
2230
d4419d55 2231 if ($students = get_records('user_students', 'userid', $userid, '', 'id, course')) {
2f3499b7 2232 foreach ($students as $student) {
2233 $course[$student->course] = $student->course;
2234 }
2235 }
fbcbd77c 2236
2237 if (count($course) > 0) {
2238 if ($courses = get_records_list('course', 'id', implode(',', $course))) {
2239 foreach ($courses as $k => $c) {
2240 if (empty($USER->admin) && (!$c->visible || !course_parent_visible($c))) {
2241 unset($course[$c->id]);
2242 }
2243 }
7fb0fec7 2244 }
2245 }
d4419d55 2246 if ($teachers = get_records('user_teachers', 'userid', $userid, '', 'id, course')) {
2f3499b7 2247 foreach ($teachers as $teacher) {
2248 $course[$teacher->course] = $teacher->course;
2249 }
2250 }
2251 if (empty($course)) {
2252 return $course;
2253 }
2254
2255 $courseids = implode(',', $course);
2256
d4419d55 2257 return get_records_list('course', 'id', $courseids, $sort);
2f3499b7 2258
2259// The following is correct but VERY slow with large datasets
2260//
8f0cd6ef 2261// return get_records_sql("SELECT c.*
2262// FROM {$CFG->prefix}course c,
2263// {$CFG->prefix}user_students s,
2264// {$CFG->prefix}user_teachers t
2f3499b7 2265// WHERE (s.userid = '$userid' AND s.course = c.id)
2266// OR (t.userid = '$userid' AND t.course = c.id)
8f0cd6ef 2267// GROUP BY c.id
2f3499b7 2268// ORDER BY $sort");
02ebf404 2269}
2270
2271
18a97fd8 2272/**
7290c7fa 2273 * A list of courses that match a search
fbc21ae8 2274 *
2275 * @uses $CFG
2276 * @param array $searchterms ?
2277 * @param string $sort ?
2278 * @param int $page ?
2279 * @param int $recordsperpage ?
2280 * @param int $totalcount Passed in by reference. ?
7290c7fa 2281 * @return object {@link $COURSE} records
fbc21ae8 2282 * @todo Finish documenting this function
2283 */
d4419d55 2284function get_courses_search($searchterms, $sort='fullname ASC', $page=0, $recordsperpage=50, &$totalcount) {
02ebf404 2285
2286 global $CFG;
2287
29daf3a0 2288 $limit = sql_paging_limit($page, $recordsperpage);
02ebf404 2289
18a97fd8 2290 //to allow case-insensitive search for postgesql
d4419d55 2291 if ($CFG->dbtype == 'postgres7') {
2292 $LIKE = 'ILIKE';
2293 $NOTLIKE = 'NOT ILIKE'; // case-insensitive
2294 $REGEXP = '~*';
2295 $NOTREGEXP = '!~*';
02ebf404 2296 } else {
d4419d55 2297 $LIKE = 'LIKE';
2298 $NOTLIKE = 'NOT LIKE';
2299 $REGEXP = 'REGEXP';
2300 $NOTREGEXP = 'NOT REGEXP';
02ebf404 2301 }
2302
d4419d55 2303 $fullnamesearch = '';
2304 $summarysearch = '';
02ebf404 2305
02ebf404 2306 foreach ($searchterms as $searchterm) {
2307 if ($fullnamesearch) {
d4419d55 2308 $fullnamesearch .= ' AND ';
02ebf404 2309 }
02ebf404 2310 if ($summarysearch) {
d4419d55 2311 $summarysearch .= ' AND ';
02ebf404 2312 }
a8b56716 2313
d4419d55 2314 if (substr($searchterm,0,1) == '+') {
a8b56716 2315 $searchterm = substr($searchterm,1);
2316 $summarysearch .= " summary $REGEXP '(^|[^a-zA-Z0-9])$searchterm([^a-zA-Z0-9]|$)' ";
2317 $fullnamesearch .= " fullname $REGEXP '(^|[^a-zA-Z0-9])$searchterm([^a-zA-Z0-9]|$)' ";
2318 } else if (substr($searchterm,0,1) == "-") {
2319 $searchterm = substr($searchterm,1);
2320 $summarysearch .= " summary $NOTREGEXP '(^|[^a-zA-Z0-9])$searchterm([^a-zA-Z0-9]|$)' ";
2321 $fullnamesearch .= " fullname $NOTREGEXP '(^|[^a-zA-Z0-9])$searchterm([^a-zA-Z0-9]|$)' ";
2322 } else {
d4419d55 2323 $summarysearch .= ' summary '. $LIKE .'\'%'. $searchterm .'%\' ';
2324 $fullnamesearch .= ' fullname '. $LIKE .'\'%'. $searchterm .'%\' ';
a8b56716 2325 }
2326
02ebf404 2327 }
2328
d4419d55 2329 $selectsql = $CFG->prefix .'course WHERE ('. $fullnamesearch .' OR '. $summarysearch .') AND category > \'0\'';
a8b56716 2330
d4419d55 2331 $totalcount = count_records_sql('SELECT COUNT(*) FROM '. $selectsql);
02ebf404 2332
d4419d55 2333 $courses = get_records_sql('SELECT * FROM '. $selectsql .' ORDER BY '. $sort .' '. $limit);
02ebf404 2334
2335 if ($courses) { /// Remove unavailable courses from the list
2336 foreach ($courses as $key => $course) {
7fb0fec7 2337 if (!$course->visible || !course_parent_visible($course)) {
02ebf404 2338 if (!isteacher($course->id)) {
2339 unset($courses[$key]);
a8b56716 2340 $totalcount--;
02ebf404 2341 }
2342 }
2343 }
2344 }
2345
2346 return $courses;
2347}
2348
2349
18a97fd8 2350/**
fbc21ae8 2351 * Returns a sorted list of categories
2352 *
2353 * @param string $parent ?
2354 * @param string $sort ?
2355 * @return ?
2356 * @todo Finish documenting this function
2357 */
d4419d55 2358function get_categories($parent='none', $sort='sortorder ASC') {
02ebf404 2359
814748c9 2360 if ($parent === 'none') {
d4419d55 2361 $categories = get_records('course_categories', '', '', $sort);
02ebf404 2362 } else {
d4419d55 2363 $categories = get_records('course_categories', 'parent', $parent, $sort);
02ebf404 2364 }
2365 if ($categories) { /// Remove unavailable categories from the list
3af6e1db 2366 $creator = iscreator();
02ebf404 2367 foreach ($categories as $key => $category) {
7fb0fec7 2368 if (!$category->visible || !category_parent_visible($category->parent)) {
3af6e1db 2369 if (!$creator) {
02ebf404 2370 unset($categories[$key]);
2371 }
2372 }
2373 }
2374 }
2375 return $categories;
2376}
2377
2378
18a97fd8 2379/**
ba87a4da 2380* This recursive function makes sure that the courseorder is consecutive
2381*
2382* @param type description
2383*
2384* $n is the starting point, offered only for compatilibity -- will be ignored!
2385* $safe (bool) prevents it from assuming category-sortorder is unique, used to upgrade
2386* safely from 1.4 to 1.5
2387*/
f41ef63e 2388function fix_course_sortorder($categoryid=0, $n=0, $safe=0, $depth=0, $path='') {
2389
ba87a4da 2390 global $CFG;
8f0cd6ef 2391
02ebf404 2392 $count = 0;
ba87a4da 2393
f41ef63e 2394 $catgap = 1000; // "standard" category gap
2395 $tolerance = 200; // how "close" categories can get
2396
2397 if ($categoryid > 0){
2398 // update depth and path
2399 $cat = get_record('course_categories', 'id', $categoryid);
2400 if ($cat->parent == 0) {
2401 $depth = 0;
2402 $path = '';
2403 } else if ($depth == 0 ) { // doesn't make sense; get from DB
2404 // this is only called if the $depth parameter looks dodgy
2405 $parent = get_record('course_categories', 'id', $cat->parent);
2406 $path = $parent->path;
2407 $depth = $parent->depth;
2408 }
2409 $path = $path . '/' . $categoryid;
2410 $depth = $depth + 1;
ba87a4da 2411
f41ef63e 2412 set_field('course_categories', 'path', addslashes($path), 'id', $categoryid);
2413 set_field('course_categories', 'depth', $depth, 'id', $categoryid);
2414 }
39f65595 2415
2416 // get some basic info about courses in the category
ba87a4da 2417 $info = get_record_sql('SELECT MIN(sortorder) AS min,
2418 MAX(sortorder) AS max,
f41ef63e 2419 COUNT(sortorder) AS count
ba87a4da 2420 FROM ' . $CFG->prefix . 'course
2421 WHERE category=' . $categoryid);
2422 if (is_object($info)) { // no courses?
2423 $max = $info->max;
2424 $count = $info->count;
2425 $min = $info->min;
2426 unset($info);
2427 }
2428
814748c9 2429 if ($categoryid > 0 && $n==0) { // only passed category so don't shift it
2430 $n = $min;
2431 }
2432
39f65595 2433 // $hasgap flag indicates whether there's a gap in the sequence
2434 $hasgap = false;
2435 if ($max-$min+1 != $count) {
2436 $hasgap = true;
2437 }
2438
2439 // $mustshift indicates whether the sequence must be shifted to
2440 // meet its range
2441 $mustshift = false;
2442 if ($min < $n+$tolerance || $min > $n+$tolerance+$catgap ) {
2443 $mustshift = true;
2444 }
2445
ba87a4da 2446 // actually sort only if there are courses,
2447 // and we meet one ofthe triggers:
2448 // - safe flag
2449 // - they are not in a continuos block
2450 // - they are too close to the 'bottom'
39f65595 2451 if ($count && ( $safe || $hasgap || $mustshift ) ) {
2452 // special, optimized case where all we need is to shift
2453 if ( $mustshift && !$safe && !$hasgap) {
2454 $shift = $n + $catgap - $min;
2455 // UPDATE course SET sortorder=sortorder+$shift
2456 execute_sql("UPDATE {$CFG->prefix}course
2457 SET sortorder=sortorder+$shift
2458 WHERE category=$categoryid", 0);
2459 $n = $n + $catgap + $count;
2460
2461 } else { // do it slowly
2462 $n = $n + $catgap;
2463 // if the new sequence overlaps the current sequence, lack of transactions
2464 // will stop us -- shift things aside for a moment...
94afadb3 2465 if ($safe || ($n >= $min && $n+$count+1 < $min && $CFG->dbtype==='mysql')) {
d6a49dab 2466 $shift = $max + $n + 1000;
39f65595 2467 execute_sql("UPDATE {$CFG->prefix}course
2468 SET sortorder=sortorder+$shift
2469 WHERE category=$categoryid", 0);
ba87a4da 2470 }
2471
39f65595 2472 $courses = get_courses($categoryid, 'c.sortorder ASC', 'c.id,c.sortorder');
2473 begin_sql();
ba87a4da 2474 foreach ($courses as $course) {
2475 if ($course->sortorder != $n ) { // save db traffic
2476 set_field('course', 'sortorder', $n, 'id', $course->id);
2477 }
2478 $n++;
2479 }
2480 commit_sql();
2481 }
02ebf404 2482 }
d4419d55 2483 set_field('course_categories', 'coursecount', $count, 'id', $categoryid);
8f0cd6ef 2484
814748c9 2485 // $n could need updating
2486 $max = get_field_sql("SELECT MAX(sortorder) from {$CFG->prefix}course WHERE category=$categoryid");
2487 if ($max > $n) {
2488 $n = $max;
2489 }
758b9a4d 2490
6bc502cc 2491 if ($categories = get_categories($categoryid)) {
2492 foreach ($categories as $category) {
f41ef63e 2493 $n = fix_course_sortorder($category->id, $n, $safe, $depth, $path);
6bc502cc 2494 }
2495 }
8f0cd6ef 2496
39f65595 2497 return $n+1;
02ebf404 2498}
2499
fbc21ae8 2500
18a97fd8 2501/**
fbc21ae8 2502 * This function creates a default separated/connected scale
2503 *
2504 * This function creates a default separated/connected scale
2505 * so there's something in the database. The locations of
2506 * strings and files is a bit odd, but this is because we
2507 * need to maintain backward compatibility with many different
2508 * existing language translations and older sites.
2509 *
2510 * @uses $CFG
2511 */
02ebf404 2512function make_default_scale() {
02ebf404 2513
2514 global $CFG;
2515
2516 $defaultscale = NULL;
2517 $defaultscale->courseid = 0;
2518 $defaultscale->userid = 0;
d4419d55 2519 $defaultscale->name = get_string('separateandconnected');
2520 $defaultscale->scale = get_string('postrating1', 'forum').','.
2521 get_string('postrating2', 'forum').','.
2522 get_string('postrating3', 'forum');
02ebf404 2523 $defaultscale->timemodified = time();
2524
8f0cd6ef 2525 /// Read in the big description from the file. Note this is not
02ebf404 2526 /// HTML (despite the file extension) but Moodle format text.
d4419d55 2527 $parentlang = get_string('parentlang');
2528 if (is_readable($CFG->dirroot .'/lang/'. $CFG->lang .'/help/forum/ratings.html')) {
2529 $file = file($CFG->dirroot .'/lang/'. $CFG->lang .'/help/forum/ratings.html');
2530 } else if ($parentlang and is_readable($CFG->dirroot .'/lang/'. $parentlang .'/help/forum/ratings.html')) {
2531 $file = file($CFG->dirroot .'/lang/'. $parentlang .'/help/forum/ratings.html');
2532 } else if (is_readable($CFG->dirroot .'/lang/en/help/forum/ratings.html')) {
2533 $file = file($CFG->dirroot .'/lang/en/help/forum/ratings.html');
02ebf404 2534 } else {
d4419d55 2535 $file = '';
02ebf404 2536 }
2537
d4419d55 2538 $defaultscale->description = addslashes(implode('', $file));
02ebf404 2539
d4419d55 2540 if ($defaultscale->id = insert_record('scale', $defaultscale)) {
2541 execute_sql('UPDATE '. $CFG->prefix .'forum SET scale = \''. $defaultscale->id .'\'', false);
02ebf404 2542 }
2543}
2544
fbc21ae8 2545
18a97fd8 2546/**
fbc21ae8 2547 * Returns a menu of all available scales from the site as well as the given course
2548 *
2549 * @uses $CFG
2550 * @param int $courseid The id of the course as found in the 'course' table.
7290c7fa 2551 * @return object
fbc21ae8 2552 */
02ebf404 2553function get_scales_menu($courseid=0) {
02ebf404 2554
2555 global $CFG;
8f0cd6ef 2556
2557 $sql = "SELECT id, name FROM {$CFG->prefix}scale
2558 WHERE courseid = '0' or courseid = '$courseid'
02ebf404 2559 ORDER BY courseid ASC, name ASC";
2560
d4419d55 2561 if ($scales = get_records_sql_menu($sql)) {
02ebf404 2562 return $scales;
2563 }
2564
2565 make_default_scale();
2566
d4419d55 2567 return get_records_sql_menu($sql);
02ebf404 2568}
2569
5baa0ad6 2570
2571
2572/**
2573 * Given a set of timezone records, put them in the database, replacing what is there
2574 *
2575 * @uses $CFG
2576 * @param array $timezones An array of timezone records
2577 */
2578function update_timezone_records($timezones) {
2579/// Given a set of timezone records, put them in the database
2580
2581 global $CFG;
2582
2583/// Clear out all the old stuff
2584 execute_sql('TRUNCATE TABLE '.$CFG->prefix.'timezone', false);
2585
2586/// Insert all the new stuff
2587 foreach ($timezones as $timezone) {
2588 insert_record('timezone', $timezone);
2589 }
2590}
2591
2592
df28d6c5 2593/// MODULE FUNCTIONS /////////////////////////////////////////////////
2594
18a97fd8 2595/**
fbc21ae8 2596 * Just gets a raw list of all modules in a course
2597 *
2598 * @uses $CFG
2599 * @param int $courseid The id of the course as found in the 'course' table.
7290c7fa 2600 * @return object
fbc21ae8 2601 * @todo Finish documenting this function
2602 */
9fa49e22 2603function get_course_mods($courseid) {
9fa49e22 2604 global $CFG;
2605
7acaa63d 2606 return get_records_sql("SELECT cm.*, m.name as modname
8f0cd6ef 2607 FROM {$CFG->prefix}modules m,
7acaa63d 2608 {$CFG->prefix}course_modules cm
8f0cd6ef 2609 WHERE cm.course = '$courseid'
9fa49e22 2610 AND cm.module = m.id ");
2611}
2612
fbc21ae8 2613
18a97fd8 2614/**
fbc21ae8 2615 * Given an instance of a module, finds the coursemodule description
2616 *
2617 * @uses $CFG
2618 * @param string $modulename ?
2619 * @param string $instance ?
2620 * @param int $courseid The id of the course as found in the 'course' table.
7290c7fa 2621 * @return object
fbc21ae8 2622 * @todo Finish documenting this function
2623 */
b63c0ee5 2624function get_coursemodule_from_instance($modulename, $instance, $courseid=0) {
df28d6c5 2625
2626 global $CFG;
b63c0ee5 2627
2628 $courseselect = ($courseid) ? "cm.course = '$courseid' AND " : '';
df28d6c5 2629
2630 return get_record_sql("SELECT cm.*, m.name
8f0cd6ef 2631 FROM {$CFG->prefix}course_modules cm,
2632 {$CFG->prefix}modules md,
2633 {$CFG->prefix}$modulename m
b63c0ee5 2634 WHERE $courseselect
8f0cd6ef 2635 cm.instance = m.id AND
2636 md.name = '$modulename' AND
df28d6c5 2637 md.id = cm.module AND
2638 m.id = '$instance'");
2639
2640}
2641
185cfb09 2642/**
2643 * Returns an array of all the active instances of a particular module in given courses, sorted in the order they are defined
2644 *
2645 * Returns an array of all the active instances of a particular
2646 * module in given courses, sorted in the order they are defined
2647 * in the course. Returns false on any errors.
2648 *
2649 * @uses $CFG
2650 * @param string $modulename The name of the module to get instances for
2651 * @param array(courses) $courses This depends on an accurate $course->modinfo
2652 * @todo Finish documenting this function. Is a course object to be documented as object(course) or array(course) since a coures object is really just an associative array, not a php object?
2653 */
2654function get_all_instances_in_courses($modulename,$courses) {
2655 global $CFG;
2656 if (empty($courses) || !is_array($courses) || count($courses) == 0) {
2657 return array();
2658 }
2659 if (!$rawmods = get_records_sql("SELECT cm.id as coursemodule, m.*,cw.section,cm.visible as visible,cm.groupmode, cm.course
2660 FROM {$CFG->prefix}course_modules cm,
2661 {$CFG->prefix}course_sections cw,
2662 {$CFG->prefix}modules md,
2663 {$CFG->prefix}$modulename m
2664 WHERE cm.course IN (".implode(',',array_keys($courses)).") AND
2665 cm.instance = m.id AND
2666 cm.section = cw.id AND
2667 md.name = '$modulename' AND
2668 md.id = cm.module")) {
2669 return array();
2670 }
2671
2672 $outputarray = array();
2673
2674 foreach ($courses as $course) {
2675 // Hide non-visible instances from students
2676 if (isteacher($course->id)) {
2677 $invisible = -1;
2678 } else {
2679 $invisible = 0;
2680 }
2681
2682 if (!$modinfo = unserialize($course->modinfo)) {
2683 continue;
2684 }
2685 foreach ($modinfo as $mod) {
2686 if ($mod->mod == $modulename and $mod->visible > $invisible) {
2687 $instance = $rawmods[$mod->cm];
2688 if (!empty($mod->extra)) {
2689 $instance->extra = $mod->extra;
2690 }
2691 $outputarray[] = $instance;
2692 }
2693 }
2694 }
2695
2696 return $outputarray;
2697
2698}
fbc21ae8 2699
18a97fd8 2700/**
fbc21ae8 2701 * Returns an array of all the active instances of a particular module in a given course, sorted in the order they are defined
2702 *
2703 * Returns an array of all the active instances of a particular
2704 * module in a given course, sorted in the order they are defined
2705 * in the course. Returns false on any errors.
2706 *
2707 * @uses $CFG
2708 * @param string $modulename The name of the module to get instances for
2709 * @param object(course) $course This depends on an accurate $course->modinfo
2710 * @todo Finish documenting this function. Is a course object to be documented as object(course) or array(course) since a coures object is really just an associative array, not a php object?
2711 */
cccb016a 2712function get_all_instances_in_course($modulename, $course) {
df28d6c5 2713
2714 global $CFG;
2715
cccb016a 2716 if (!$modinfo = unserialize($course->modinfo)) {
2717 return array();
1acfbce5 2718 }
2719
404afe6b 2720 if (!$rawmods = get_records_sql("SELECT cm.id as coursemodule, m.*,cw.section,cm.visible as visible,cm.groupmode
8f0cd6ef 2721 FROM {$CFG->prefix}course_modules cm,
2722 {$CFG->prefix}course_sections cw,
2723 {$CFG->prefix}modules md,
2724 {$CFG->prefix}$modulename m
2725 WHERE cm.course = '$course->id' AND
2726 cm.instance = m.id AND
8f0cd6ef 2727 cm.section = cw.id AND
2728 md.name = '$modulename' AND
cccb016a 2729 md.id = cm.module")) {
2730 return array();
2731 }
2732
2733 // Hide non-visible instances from students
2734 if (isteacher($course->id)) {
2735 $invisible = -1;
2736 } else {
2737 $invisible = 0;
2738 }
2739
2740 foreach ($modinfo as $mod) {
2741 if ($mod->mod == $modulename and $mod->visible > $invisible) {
7f12f9cd 2742 $instance = $rawmods[$mod->cm];
2743 if (!empty($mod->extra)) {
2744 $instance->extra = $mod->extra;
2745 }
2746 $outputarray[] = $instance;
cccb016a 2747 }
2748 }
2749
2750 return $outputarray;
df28d6c5 2751
2752}
2753
9fa49e22 2754
18a97fd8 2755/**
fbc21ae8 2756 * Determine whether a module instance is visible within a course
2757 *
2758 * Given a valid module object with info about the id and course,
2759 * and the module's type (eg "forum") returns whether the object
2760 * is visible or not
2761 *
2762 * @uses $CFG
2763 * @param $moduletype ?
2764 * @param $module ?
7290c7fa 2765 * @return bool
fbc21ae8 2766 * @todo Finish documenting this function
2767 */
580f2fbc 2768function instance_is_visible($moduletype, $module) {
580f2fbc 2769
2770 global $CFG;
2771
2b49ae96 2772 if (!empty($module->id)) {
2773 if ($records = get_records_sql("SELECT cm.instance, cm.visible
2774 FROM {$CFG->prefix}course_modules cm,
2775 {$CFG->prefix}modules m
2776 WHERE cm.course = '$module->course' AND
2777 cm.module = m.id AND
2778 m.name = '$moduletype' AND
2779 cm.instance = '$module->id'")) {
2780
2781 foreach ($records as $record) { // there should only be one - use the first one
2782 return $record->visible;
2783 }
580f2fbc 2784 }
2785 }
580f2fbc 2786 return true; // visible by default!
2787}
2788
a3fb1c45 2789
2790
2791
9fa49e22 2792/// LOG FUNCTIONS /////////////////////////////////////////////////////
2793
2794
18a97fd8 2795/**
fbc21ae8 2796 * Add an entry to the log table.
2797 *
2798 * Add an entry to the log table. These are "action" focussed rather
2799 * than web server hits, and provide a way to easily reconstruct what
2800 * any particular student has been doing.
2801 *
2802 * @uses $CFG
2803 * @uses $USER
2804 * @uses $db
2805 * @uses $REMOTE_ADDR
2806 * @uses SITEID
89dcb99d 2807 * @param int $courseid The course id
fbc21ae8 2808 * @param string $module The module name - e.g. forum, journal, resource, course, user etc
2809 * @param string $action View, edit, post (often but not always the same as the file.php)
2810 * @param string $url The file and parameters used to see the results of the action
2811 * @param string $info Additional description information
2812 * @param string $cm The course_module->id if there is one
2813 * @param string $user If log regards $user other than $USER
2814 */
d4419d55 2815function add_to_log($courseid, $module, $action, $url='', $info='', $cm=0, $user=0) {
9fa49e22 2816
fcaff7ff 2817 global $db, $CFG, $USER;
9fa49e22 2818
7a5b1fc5 2819 if ($cm === '' || is_null($cm)) { // postgres won't translate empty string to its default
f78b3c34 2820 $cm = 0;
2821 }
2822
3d94772d 2823 if ($user) {
2824 $userid = $user;
2825 } else {
2826 if (isset($USER->realuser)) { // Don't log
2827 return;
2828 }
d4419d55 2829 $userid = empty($USER->id) ? '0' : $USER->id;
9fa49e22 2830 }
2831
fcaff7ff 2832 $REMOTE_ADDR = getremoteaddr();
2833
9fa49e22 2834 $timenow = time();
2835 $info = addslashes($info);
10a760b9 2836 if (!empty($url)) { // could break doing html_entity_decode on an empty var.
2837 $url = html_entity_decode($url); // for php < 4.3.0 this is defined in moodlelib.php
2838 }
853df85e 2839
2840 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; $PERF->logwrites++;};
2841
d4419d55 2842 $result = $db->Execute('INSERT INTO '. $CFG->prefix .'log (time, userid, course, ip, module, cmid, action, url, info)
2843 VALUES (' . "'$timenow', '$userid', '$courseid', '$REMOTE_ADDR', '$module', '$cm', '$action', '$url', '$info')");
ebc3bd2b 2844
ce78926d 2845 if (!$result and ($CFG->debug > 7)) {
d4419d55 2846 echo '<p>Error: Could not insert a new entry to the Moodle log</p>'; // Don't throw an error
8f0cd6ef 2847 }
f78b3c34 2848 if ( isset($USER) && (empty($user) || $user==$USER->id) ) {
114176a2 2849 $db->Execute('UPDATE '. $CFG->prefix .'user SET lastIP=\''. $REMOTE_ADDR .'\', lastaccess=\''. $timenow .'\'
c2aa460f 2850 WHERE id = \''. $userid .'\' ');
e3a23213 2851 if ($courseid != SITEID && !empty($courseid)) { // logins etc dont't have a courseid and isteacher will break without it.
853df85e 2852 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++;};
114176a2 2853 if (isstudent($courseid)) {
2854 $db->Execute('UPDATE '. $CFG->prefix .'user_students SET timeaccess = \''. $timenow .'\' '.
2855 'WHERE course = \''. $courseid .'\' AND userid = \''. $userid .'\'');
2856 } else if (isteacher($courseid, false, false)) {
2857 $db->Execute('UPDATE '. $CFG->prefix .'user_teachers SET timeaccess = \''. $timenow .'\' '.
2858 'WHERE course = \''. $courseid .'\' AND userid = \''. $userid .'\'');
2859 }
3d94772d 2860 }
8f0cd6ef 2861 }
9fa49e22 2862}
2863
2864
18a97fd8 2865/**
fbc21ae8 2866 * Select all log records based on SQL criteria
2867 *
2868 * @uses $CFG
2869 * @param string $select SQL select criteria
2870 * @param string $order SQL order by clause to sort the records returned
2871 * @param string $limitfrom ?
2872 * @param int $limitnum ?
2873 * @param int $totalcount Passed in by reference.
7290c7fa 2874 * @return object
fbc21ae8 2875 * @todo Finish documenting this function
2876 */
d4419d55 2877function get_logs($select, $order='l.time DESC', $limitfrom='', $limitnum='', &$totalcount) {
9fa49e22 2878 global $CFG;
2879
d4419d55 2880 if ($limitfrom !== '') {
29daf3a0 2881 $limit = sql_paging_limit($limitfrom, $limitnum);
519d369f 2882 } else {
d4419d55 2883 $limit = '';
519d369f 2884 }
2885
2886 if ($order) {
d4419d55 2887 $order = 'ORDER BY '. $order;
519d369f 2888 }
2889
fbc21ae8 2890 $selectsql = $CFG->prefix .'log l LEFT JOIN '. $CFG->prefix .'user u ON l.userid = u.id '. ((strlen($select) > 0) ? 'WHERE '. $select : '');
a2ddd957 2891 $countsql = $CFG->prefix.'log l '.((strlen($select) > 0) ? ' WHERE '. $select : '');
2892
2893 $totalcount = count_records_sql("SELECT COUNT(*) FROM $countsql");
519d369f 2894
d4419d55 2895 return get_records_sql('SELECT l.*, u.firstname, u.lastname, u.picture
2896 FROM '. $selectsql .' '. $order .' '. $limit);
9fa49e22 2897}
2898
519d369f 2899
18a97fd8 2900/**
fbc21ae8 2901 * Select all log records for a given course and user
2902 *
2903 * @uses $CFG
2f87145b 2904 * @uses DAYSECS
fbc21ae8 2905 * @param int $userid The id of the user as found in the 'user' table.
2906 * @param int $courseid The id of the course as found in the 'course' table.
2907 * @param string $coursestart ?
2908 * @todo Finish documenting this function
2909 */
9fa49e22 2910function get_logs_usercourse($userid, $courseid, $coursestart) {
2911 global $CFG;
2912
da0c90c3 2913 if ($courseid) {
d4419d55 2914 $courseselect = ' AND course = \''. $courseid .'\' ';
2700d113 2915 } else {
2916 $courseselect = '';
da0c90c3 2917 }
2918
1604a0fc 2919 return get_records_sql("SELECT floor((time - $coursestart)/". DAYSECS .") as day, count(*) as num
8f0cd6ef 2920 FROM {$CFG->prefix}log
2921 WHERE userid = '$userid'
1604a0fc 2922 AND time > '$coursestart' $courseselect
9fa49e22 2923 GROUP BY day ");
2924}
2925
18a97fd8 2926/**
fbc21ae8 2927 * Select all log records for a given course, user, and day
2928 *
2929 * @uses $CFG
2f87145b 2930 * @uses HOURSECS
fbc21ae8 2931 * @param int $userid The id of the user as found in the 'user' table.
2932 * @param int $courseid The id of the course as found in the 'course' table.
2933 * @param string $daystart ?
7290c7fa 2934 * @return object
fbc21ae8 2935 * @todo Finish documenting this function
2936 */
9fa49e22 2937function get_logs_userday($userid, $courseid, $daystart) {
2938 global $CFG;
2939
7e4a6488 2940 if ($courseid) {
d4419d55 2941 $courseselect = ' AND course = \''. $courseid .'\' ';
2700d113 2942 } else {
2943 $courseselect = '';
7e4a6488 2944 }
2945
1604a0fc 2946 return get_records_sql("SELECT floor((time - $daystart)/". HOURSECS .") as hour, count(*) as num
9fa49e22 2947 FROM {$CFG->prefix}log
8f0cd6ef 2948 WHERE userid = '$userid'
1604a0fc 2949 AND time > '$daystart' $courseselect
9fa49e22 2950 GROUP BY hour ");
2951}
2952
b4bac9b6 2953/**
2954 * Returns an object with counts of failed login attempts
2955 *
8f0cd6ef 2956 * Returns information about failed login attempts. If the current user is
2957 * an admin, then two numbers are returned: the number of attempts and the
b4bac9b6 2958 * number of accounts. For non-admins, only the attempts on the given user
2959 * are shown.
2960 *
fbc21ae8 2961 * @param string $mode Either 'admin', 'teacher' or 'everybody'
2962 * @param string $username The username we are searching for
2963 * @param string $lastlogin The date from which we are searching
2964 * @return int
b4bac9b6 2965 */
b4bac9b6 2966function count_login_failures($mode, $username, $lastlogin) {
2967
d4419d55 2968 $select = 'module=\'login\' AND action=\'error\' AND time > '. $lastlogin;
b4bac9b6 2969
2970 if (isadmin()) { // Return information about all accounts
2971 if ($count->attempts = count_records_select('log', $select)) {
2972 $count->accounts = count_records_select('log', $select, 'COUNT(DISTINCT info)');
2973 return $count;
2974 }
9407d456 2975 } else if ($mode == 'everybody' or ($mode == 'teacher' and isteacherinanycourse())) {
d4419d55 2976 if ($count->attempts = count_records_select('log', $select .' AND info = \''. $username .'\'')) {
b4bac9b6 2977 return $count;
2978 }
2979 }
2980 return NULL;
2981}
2982
2983
a3fb1c45 2984/// GENERAL HELPFUL THINGS ///////////////////////////////////
2985
18a97fd8 2986/**
fbc21ae8 2987 * Dump a given object's information in a PRE block.
2988 *
2989 * Mostly just used for debugging.
2990 *
2991 * @param mixed $object The data to be printed
2992 * @todo add example usage and example output
2993 */
a3fb1c45 2994function print_object($object) {
a3fb1c45 2995
d4419d55 2996 echo '<pre>';
2b051f1c 2997 print_r($object);
d4419d55 2998 echo '</pre>';
a3fb1c45 2999}
3000
29daf3a0 3001/**
3002 * Returns the proper SQL to do paging
3003 *
7290c7fa 3004 * @uses $CFG
3005 * @param string $page Offset page number
3006 * @param string $recordsperpage Number of records per page
3007 * @return string
29daf3a0 3008 */
3009function sql_paging_limit($page, $recordsperpage) {
3010 global $CFG;
3011
3012 switch ($CFG->dbtype) {
3013 case 'postgres7':
3014 return 'LIMIT '. $recordsperpage .' OFFSET '. $page;
3015 default:
3016 return 'LIMIT '. $page .','. $recordsperpage;
3017 }
3018}
3019
3020/**
3021 * Returns the proper SQL to do LIKE in a case-insensitive way
3022 *
7290c7fa 3023 * @uses $CFG
3024 * @return string
29daf3a0 3025 */
3026function sql_ilike() {
3027 global $CFG;
a3fb1c45 3028
29daf3a0 3029 switch ($CFG->dbtype) {
3030 case 'mysql':
3031 return 'LIKE';
3032 default:
3033 return 'ILIKE';
3034 }
3035}
3036
3037
3038/**
3039 * Returns the proper SQL to do LIKE in a case-insensitive way
3040 *
7290c7fa 3041 * @uses $CFG
3042 * @param string $firstname User's first name
3043 * @param string $lastname User's last name
3044 * @return string
29daf3a0 3045 */
7290c7fa 3046function sql_fullname($firstname='firstname', $lastname='lastname') {
29daf3a0 3047 global $CFG;
3048
3049 switch ($CFG->dbtype) {
3050 case 'mysql':
7290c7fa 3051 return ' CONCAT('. $firstname .'," ",'. $lastname .') ';
29daf3a0 3052 case 'postgres7':
7290c7fa 3053 return " ". $firstname ."||' '||". $lastname ." ";
29daf3a0 3054 default:
7290c7fa 3055 return ' '. $firstname .'||" "||'. $lastname .' ';
29daf3a0 3056 }
3057}
9fa49e22 3058
a0413b58 3059/**
3060 * Returns the proper SQL to do IS NULL
7290c7fa 3061 * @uses $CFG
3062 * @param string $fieldname The field to add IS NULL to
3063 * @return string
a0413b58 3064 */
3065function sql_isnull($fieldname) {
3066 global $CFG;
3067
3068 switch ($CFG->dbtype) {
3069 case 'mysql':
3070 return $fieldname.' IS NULL';
3071 default:
3072 return $fieldname.' IS NULL';
3073 }
3074}
3075
11840632 3076/**
3077 * Prepare a SQL WHERE clause to select records where the given fields match the given values.
3078 *
3079 * Prepares a where clause of the form
3080 * WHERE field1 = value1 AND field2 = value2 AND field3 = value3
3081 * except that you need only specify as many arguments (zero to three) as you need.
3082 *
3083 * @param string $field1 the first field to check (optional).
3084 * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
3085 * @param string $field2 the second field to check (optional).
3086 * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
3087 * @param string $field3 the third field to check (optional).
3088 * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
3089 */
3090function where_clause($field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
3091 if ($field1) {
3092 $select = "WHERE $field1 = '$value1'";
3093 if ($field2) {
3094 $select .= " AND $field2 = '$value2'";
3095 if ($field3) {
3096 $select .= " AND $field3 = '$value3'";
3097 }
3098 }
3099 } else {
3100 $select = '';
3101 }
3102 return $select;
3103}
3104
3ec22e35 3105/**
3106 * Checks for pg or mysql > 4
3107 */
3108
3109function check_db_compat() {
3110 global $CFG,$db;
3111
3112 if ($CFG->dbtype == 'postgres7') {
3113 return true;
3114 }
3115
3116 if (!$rs = $db->Execute("SELECT version();")) {
3117 return false;
3118 }
3119
3120 if (intval($rs->fields[0]) <= 3) {
3121 return false;
3122 }
3123
3124 return true;
3125}
3126
0986271b 3127function course_parent_visible($course = null) {
3128 return category_parent_visible($course->category);
3129}
3130
3131function category_parent_visible($parent = 0) {
3132 if (!$parent) {
3133 return true;
3134 }
3135 $category = get_record('course_categories', 'id', $parent);
3136 $list = explode('/', preg_replace('/^\/(.*)$/', '$1', $category->path));
3137 $list[] = $parent;
3138 $parents = get_records_list('course_categories', 'id', implode(',', $list), 'depth DESC');
3139 foreach ($parents as $parent) {
3140 if (!$parent->visible) {
3141 return false;
3142 }
3143 }
3144 return true;
3145}
3146
9d5b689c 3147// vim:autoindent:expandtab:shiftwidth=4:tabstop=4:tw=140:
f6f319f8 3148?>