/** * Update a record in a table * * $dataobject is an object containing needed data * Relies on $dataobject having a variable "id" to * specify the record to update * * @uses $CFG * @uses $db * @param string $table The database table to be checked against. * @param object $dataobject An object with contents equal to fieldname=>fieldvalue. Must have an entry for 'id' to map to the table specified. * @return bool */ function update_record($table, $dataobject) { global $db, $CFG; if (!isset($dataobject->id)) { return false; } /// Check we are handling a proper $dataobject if (is_array($dataobject)) { debugging('Warning. Wrong call to update_record(). $dataobject must be an object. array found instead', DEBUG_DEVELOPER); $dataobject = (object) $dataobject; } // Remove this record from record cache since it will change if (!empty($CFG->rcache)) { // no === here! breaks upgrade rcache_unset($table, $dataobject->id); } /// Temporary hack as part of phasing out all access to obsolete user tables XXX if (!empty($CFG->rolesactive)) { if (in_array($table, array('user_students', 'user_teachers', 'user_coursecreators', 'user_admins'))) { if (debugging()) { var_dump(debug_backtrace()); } error('This SQL relies on obsolete tables (' . $table . ')! Your code must be fixed by a developer.'); } } /// Begin DIRTY HACK if ($CFG->dbfamily == 'oracle') { oracle_dirty_hack($table, $dataobject); // Convert object to the correct "empty" values for Oracle DB } /// End DIRTY HACK /// Under Oracle, MSSQL and PostgreSQL we have our own update record process /// detect all the clob/blob fields and delete them from the record being updated /// saving them into $foundclobs and $foundblobs [$fieldname]->contents /// They will be updated later if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') && !empty($dataobject->id)) { /// Detect lobs $foundclobs = array(); $foundblobs = array(); db_detect_lobs($table, $dataobject, $foundclobs, $foundblobs, true); } // Determine all the fields in the table if (!($columns = $db->MetaColumns($CFG->prefix . $table))) { return false; } $data = (array) $dataobject; if (defined('MDL_PERFDB')) { global $PERF; $PERF->dbqueries++; } // Pull out data matching these fields $ddd = array(); foreach ($columns as $column) { if ($column->name != 'id' and array_key_exists($column->name, $data)) { $ddd[$column->name] = $data[$column->name]; } } // Construct SQL queries $numddd = count($ddd); $count = 0; $update = ''; /// Only if we have fields to be updated (this will prevent both wrong updates + /// updates of only LOBs in Oracle if ($numddd) { foreach ($ddd as $key => $value) { $count++; if ($value === NULL) { $update .= $key . ' = NULL'; // previously NULLs were not updated } else { $update .= $key . ' = \'' . $value . '\''; // All incoming data is already quoted } if ($count < $numddd) { $update .= ', '; } } if (!($rs = $db->Execute('UPDATE ' . $CFG->prefix . $table . ' SET ' . $update . ' WHERE id = \'' . $dataobject->id . '\''))) { debugging($db->ErrorMsg() . '<br /><br />UPDATE ' . $CFG->prefix . $table . ' SET ' . s($update) . ' WHERE id = \'' . $dataobject->id . '\''); if (!empty($CFG->dblogerror)) { $debug = array_shift(debug_backtrace()); error_log("SQL " . $db->ErrorMsg() . " in {$debug['file']} on line {$debug['line']}. STATEMENT: UPDATE {$CFG->prefix}{$table} SET {$update} WHERE id = '{$dataobject->id}'"); } return false; } } /// Under Oracle, MSSQL and PostgreSQL, finally, update all the Clobs and Blobs present in the record /// if we know we have some of them in the query if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') && !empty($dataobject->id) && (!empty($foundclobs) || !empty($foundblobs))) { if (!db_update_lobs($table, $dataobject->id, $foundclobs, $foundblobs)) { return false; //Some error happened while updating LOBs } } return true; }
/** * Update a record in a table * * $dataobject is an object containing needed data * Relies on $dataobject having a variable "id" to * specify the record to update * * @uses $CFG * @uses $db * @param string $table The database table to be checked against. * @param object $dataobject An object with contents equal to fieldname=>fieldvalue. Must have an entry for 'id' to map to the table specified. * @return bool */ function update_record($table, $dataobject) { global $db, $CFG; // integer value in id propery required if (empty($dataobject->id)) { return false; } $dataobject->id = (int) $dataobject->id; /// Check we are handling a proper $dataobject if (is_array($dataobject)) { debugging('Warning. Wrong call to update_record(). $dataobject must be an object. array found instead', DEBUG_DEVELOPER); $dataobject = (object) $dataobject; } else { if (is_object($dataobject)) { // make sure there are no properties or private methods because we cast to array later, // at the same time this undos the object references so that PHP 5 works the same as PHP 4, // the main reason for this is BC after the dirty magic hack introduction if ($properties = get_object_vars($dataobject)) { $dataobject = (object) $properties; } unset($properties); } } /// Extra protection against SQL injections foreach ((array) $dataobject as $k => $v) { $dataobject->{$k} = sql_magic_quotes_hack($v); } // Remove this record from record cache since it will change if (!empty($CFG->rcache)) { // no === here! breaks upgrade rcache_unset($table, $dataobject->id); } /// Temporary hack as part of phasing out all access to obsolete user tables XXX if (!empty($CFG->rolesactive)) { if (in_array($table, array('user_students', 'user_teachers', 'user_coursecreators', 'user_admins'))) { if (debugging()) { var_dump(debug_backtrace()); } error('This SQL relies on obsolete tables (' . $table . ')! Your code must be fixed by a developer.'); } } /// Begin DIRTY HACK if ($CFG->dbfamily == 'oracle') { oracle_dirty_hack($table, $dataobject); // Convert object to the correct "empty" values for Oracle DB } /// End DIRTY HACK /// Under Oracle, MSSQL and PostgreSQL we have our own update record process /// detect all the clob/blob fields and delete them from the record being updated /// saving them into $foundclobs and $foundblobs [$fieldname]->contents /// They will be updated later if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') && !empty($dataobject->id)) { /// Detect lobs $foundclobs = array(); $foundblobs = array(); db_detect_lobs($table, $dataobject, $foundclobs, $foundblobs, true); } // Determine all the fields in the table if (!($columns = $db->MetaColumns($CFG->prefix . $table))) { return false; } $data = (array) $dataobject; if (defined('MDL_PERFDB')) { global $PERF; $PERF->dbqueries++; } // Pull out data matching these fields $update = array(); foreach ($columns as $column) { if ($column->name == 'id') { continue; } if (array_key_exists($column->name, $data)) { $key = $column->name; $value = $data[$key]; if (is_null($value)) { $update[] = "{$key} = NULL"; // previously NULLs were not updated } else { if (is_bool($value)) { $value = (int) $value; $update[] = "{$key} = {$value}"; // lets keep pg happy, '' is not correct smallint MDL-13038 } else { $update[] = "{$key} = '{$value}'"; // All incoming data is already quoted } } } } /// Only if we have fields to be updated (this will prevent both wrong updates + /// updates of only LOBs in Oracle if ($update) { $query = "UPDATE {$CFG->prefix}{$table} SET " . implode(',', $update) . " WHERE id = {$dataobject->id}"; if (!($rs = $db->Execute($query))) { debugging($db->ErrorMsg() . '<br /><br />' . s($query)); if (!empty($CFG->dblogerror)) { $debug = array_shift(debug_backtrace()); error_log("SQL " . $db->ErrorMsg() . " in {$debug['file']} on line {$debug['line']}. STATEMENT: {$query}"); } return false; } } /// Under Oracle, MSSQL and PostgreSQL, finally, update all the Clobs and Blobs present in the record /// if we know we have some of them in the query if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') && !empty($dataobject->id) && (!empty($foundclobs) || !empty($foundblobs))) { if (!db_update_lobs($table, $dataobject->id, $foundclobs, $foundblobs)) { return false; //Some error happened while updating LOBs } } return true; }
/** * Store user last access times - called when use enters a course or site * * Note: we use ADOdb code directly in this function to save some CPU * cycles here and there. They are simple operations not needing any * of the postprocessing performed by dmllib.php * * @param int $courseid, empty means site * @return void */ function user_accesstime_log($courseid = 0) { global $USER, $CFG, $PERF, $db; if (!isloggedin() or !empty($USER->realuser)) { // no access tracking return; } if (empty($courseid)) { $courseid = SITEID; } $timenow = time(); /// Store site lastaccess time for the current user if ($timenow - $USER->lastaccess > LASTACCESS_UPDATE_SECS) { /// Update $USER->lastaccess for next checks $USER->lastaccess = $timenow; if (defined('MDL_PERFDB')) { global $PERF; $PERF->dbqueries++; } $remoteaddr = getremoteaddr(); if ($db->Execute("UPDATE {$CFG->prefix}user\n SET lastip = '{$remoteaddr}', lastaccess = {$timenow}\n WHERE id = {$USER->id}")) { } else { debugging('Error: Could not update global user lastaccess information'); // Don't throw an error } /// Remove this record from record cache since it will change if (!empty($CFG->rcache)) { rcache_unset('user', $USER->id); } } if ($courseid == SITEID) { /// no user_lastaccess for frontpage return; } /// Store course lastaccess times for the current user if (empty($USER->currentcourseaccess[$courseid]) or $timenow - $USER->currentcourseaccess[$courseid] > LASTACCESS_UPDATE_SECS) { if (defined('MDL_PERFDB')) { global $PERF; $PERF->dbqueries++; } $exists = false; // To detect if the user_lastaccess record exists or no if ($rs = $db->Execute("SELECT timeaccess\n FROM {$CFG->prefix}user_lastaccess\n WHERE userid = {$USER->id} AND courseid = {$courseid}")) { if (!$rs->EOF) { $exists = true; $lastaccess = reset($rs->fields); if ($timenow - $lastaccess < LASTACCESS_UPDATE_SECS) { /// no need to update now, it was updated recently in concurrent login ;-) $rs->Close(); return; } } $rs->Close(); } /// Update course lastaccess for next checks $USER->currentcourseaccess[$courseid] = $timenow; if (defined('MDL_PERFDB')) { global $PERF; $PERF->dbqueries++; } if ($exists) { // user_lastaccess record exists, update it if ($db->Execute("UPDATE {$CFG->prefix}user_lastaccess\n SET timeaccess = {$timenow}\n WHERE userid = {$USER->id} AND courseid = {$courseid}")) { } else { debugging('Error: Could not update course user lastacess information'); // Don't throw an error } } else { // user lastaccess record doesn't exist, insert it if ($db->Execute("INSERT INTO {$CFG->prefix}user_lastaccess\n (userid, courseid, timeaccess)\n VALUES ({$USER->id}, {$courseid}, {$timenow})")) { } else { debugging('Error: Could not insert course user lastaccess information'); // Don't throw an error } } } }
/** * Add an entry to the log table. * * Add an entry to the log table. These are "action" focussed rather * than web server hits, and provide a way to easily reconstruct what * any particular student has been doing. * * @uses $CFG * @uses $USER * @uses $db * @uses $REMOTE_ADDR * @uses SITEID * @param int $courseid The course id * @param string $module The module name - e.g. forum, journal, resource, course, user etc * @param string $action 'view', 'update', 'add' or 'delete', possibly followed by another word to clarify. * @param string $url The file and parameters used to see the results of the action * @param string $info Additional description information * @param string $cm The course_module->id if there is one * @param string $user If log regards $user other than $USER */ function add_to_log($courseid, $module, $action, $url = '', $info = '', $cm = 0, $user = 0) { // Note that this function intentionally does not follow the normal Moodle DB access idioms. // This is for a good reason: it is the most frequently used DB update function, // so it has been optimised for speed. global $db, $CFG, $USER; if ($cm === '' || is_null($cm)) { // postgres won't translate empty string to its default $cm = 0; } if ($user) { $userid = $user; } else { if (!empty($USER->realuser)) { // Don't log return; } $userid = empty($USER->id) ? '0' : $USER->id; } $REMOTE_ADDR = getremoteaddr(); $timenow = time(); $info = addslashes($info); if (!empty($url)) { // could break doing html_entity_decode on an empty var. $url = html_entity_decode($url); // for php < 4.3.0 this is defined in moodlelib.php } if (defined('MDL_PERFDB')) { global $PERF; $PERF->dbqueries++; $PERF->logwrites++; } if ($CFG->type = 'oci8po') { if (empty($info)) { $info = ' '; } } $result = $db->Execute('INSERT INTO ' . $CFG->prefix . 'log (time, userid, course, ip, module, cmid, action, url, info) VALUES (' . "'{$timenow}', '{$userid}', '{$courseid}', '{$REMOTE_ADDR}', '{$module}', '{$cm}', '{$action}', '{$url}', '{$info}')"); if (!$result and debugging()) { echo '<p>Error: Could not insert a new entry to the Moodle log</p>'; // Don't throw an error } /// Store lastaccess times for the current user, do not use in cron and other commandline scripts if (!empty($USER->id) && $userid == $USER->id && !defined('FULLME')) { $db->Execute('UPDATE ' . $CFG->prefix . 'user SET lastip=\'' . $REMOTE_ADDR . '\', lastaccess=\'' . $timenow . '\' WHERE id = \'' . $userid . '\' '); /// Remove this record from record cache since it will change if (!empty($CFG->rcache)) { rcache_unset('user', $userid); } if ($courseid != SITEID && !empty($courseid)) { if (defined('MDL_PERFDB')) { global $PERF; $PERF->dbqueries++; } if ($record = get_record('user_lastaccess', 'userid', $userid, 'courseid', $courseid)) { $record->timeaccess = $timenow; return update_record('user_lastaccess', $record); } else { $record = new object(); $record->userid = $userid; $record->courseid = $courseid; $record->timeaccess = $timenow; return insert_record('user_lastaccess', $record); } } } }