/** * Redirect all events to this log manager, but only if this * log manager is actually used. * * @param \core\event\base $event */ public static function store(\core\event\base $event) { $logmanager = get_log_manager(); if (get_class($logmanager) === 'tool_log\\log\\manager') { /** @var \tool_log\log\manager $logmanager */ $logmanager->process($event); } }
/** * Do the job. * Throw exceptions on errors (the job will be retried). */ public function execute() { global $DB; $manager = get_log_manager(); $store = new store($manager); $events = $DB->get_records('logstore_xapi_log'); $store->process_events($events); $DB->delete_records_list('logstore_xapi_log', 'id', array_keys($events)); mtrace("Sent learning records to LRS."); }
/** * Add an entry to the legacy log table. * * @deprecated since 2.7 use new events instead * * @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 int $cm The course_module->id if there is one * @param int|stdClass $user If log regards $user other than $USER * @return void */ function add_to_log($courseid, $module, $action, $url = '', $info = '', $cm = 0, $user = 0) { debugging('add_to_log() has been deprecated, please rewrite your code to the new events API', DEBUG_DEVELOPER); // This is a nasty hack that allows us to put all the legacy stuff into legacy storage, // this way we may move all the legacy settings there too. $manager = get_log_manager(); if (method_exists($manager, 'legacy_add_to_log')) { $manager->legacy_add_to_log($courseid, $module, $action, $url, $info, $cm, $user); } }
/** * oauth_add_to_log is a quick hack to avoid add_to_log debugging */ function oauth_add_to_log($courseid, $module, $action, $url = '', $info = '', $cm = 0, $user = 0) { if (function_exists('get_log_manager')) { $manager = get_log_manager(); $manager->legacy_add_to_log($courseid, $module, $action, $url, $info, $cm, $user); } else { if (function_exists('add_to_log')) { add_to_log($courseid, $module, $action, $url, $info, $cm, $user); } } }
/** * Test report_log_supports_logstore. */ public function test_report_participation_supports_logstore() { $logmanager = get_log_manager(); $allstores = \core_component::get_plugin_list_with_class('logstore', 'log\\store'); $supportedstores = array('logstore_legacy' => '\\logstore_legacy\\log\\store', 'logstore_standard' => '\\logstore_standard\\log\\store'); // Make sure all supported stores are installed. $expectedstores = array_keys(array_intersect($allstores, $supportedstores)); $stores = $logmanager->get_supported_logstores('report_outline'); $stores = array_keys($stores); foreach ($expectedstores as $expectedstore) { $this->assertContains($expectedstore, $stores); } }
/** * Returns log table name of preferred reader, if leagcy then return empty string. * * @return string table name */ function report_participation_get_log_table_name() { // Get prefered sql_internal_table_reader reader (if enabled). $logmanager = get_log_manager(); $readers = $logmanager->get_readers(); $logtable = ''; // Get preferred reader. if (!empty($readers)) { foreach ($readers as $readerpluginname => $reader) { // If legacy reader is preferred reader. if ($readerpluginname == 'logstore_legacy') { break; } // If sql_internal_table_reader is preferred reader. if ($reader instanceof \core\log\sql_internal_table_reader) { $logtable = $reader->get_internal_log_table_name(); break; } } } return $logtable; }
/** * Returns an array of the commonly used log variables by the outline report. * * @return array the array of variables used */ function report_outline_get_common_log_variables() { global $DB; static $uselegacyreader; static $useinternalreader; static $minloginternalreader; static $logtable = null; if (isset($uselegacyreader) && isset($useinternalreader) && isset($minloginternalreader)) { return array($uselegacyreader, $useinternalreader, $minloginternalreader, $logtable); } $uselegacyreader = false; // Flag to determine if we should use the legacy reader. $useinternalreader = false; // Flag to determine if we should use the internal reader. $minloginternalreader = 0; // Set this to 0 for now. // Get list of readers. $logmanager = get_log_manager(); $readers = $logmanager->get_readers(); // Get preferred reader. if (!empty($readers)) { foreach ($readers as $readerpluginname => $reader) { // If legacy reader is preferred reader. if ($readerpluginname == 'logstore_legacy') { $uselegacyreader = true; break; } // If sql_internal_reader is preferred reader. if ($reader instanceof \core\log\sql_internal_reader) { $useinternalreader = true; $logtable = $reader->get_internal_log_table_name(); $minloginternalreader = $DB->get_field_sql('SELECT min(timecreated) FROM {' . $logtable . '}'); break; } } } return array($uselegacyreader, $useinternalreader, $minloginternalreader, $logtable); }
public function test_get_log_manager() { global $CFG; $this->resetAfterTest(); $manager = get_log_manager(); $this->assertInstanceOf('core\\log\\manager', $manager); $stores = $manager->get_readers(); $this->assertInternalType('array', $stores); $this->assertCount(0, $stores); $this->assertFileExists("{$CFG->dirroot}/{$CFG->admin}/tool/log/store/standard/version.php"); $this->assertFileExists("{$CFG->dirroot}/{$CFG->admin}/tool/log/store/legacy/version.php"); set_config('enabled_stores', 'logstore_standard,logstore_legacy', 'tool_log'); $manager = get_log_manager(true); $this->assertInstanceOf('core\\log\\manager', $manager); $stores = $manager->get_readers(); $this->assertInternalType('array', $stores); $this->assertCount(2, $stores); foreach ($stores as $key => $store) { $this->assertInternalType('string', $key); $this->assertInstanceOf('core\\log\\sql_select_reader', $store); } $stores = $manager->get_readers('core\\log\\sql_internal_reader'); $this->assertInternalType('array', $stores); $this->assertCount(1, $stores); foreach ($stores as $key => $store) { $this->assertInternalType('string', $key); $this->assertSame('logstore_standard', $key); $this->assertInstanceOf('core\\log\\sql_internal_reader', $store); } $stores = $manager->get_readers('core\\log\\sql_select_reader'); $this->assertInternalType('array', $stores); $this->assertCount(2, $stores); foreach ($stores as $key => $store) { $this->assertInternalType('string', $key); $this->assertInstanceOf('core\\log\\sql_select_reader', $store); } }
/** * Trigger event. */ public final function trigger() { global $CFG; if ($this->restored) { throw new \coding_exception('Can not trigger restored event'); } if ($this->triggered or $this->dispatched) { throw new \coding_exception('Can not trigger event twice'); } $this->validate_before_trigger(); $this->triggered = true; if (isset($CFG->loglifetime) and $CFG->loglifetime != -1) { if ($data = $this->get_legacy_logdata()) { $manager = get_log_manager(); if (method_exists($manager, 'legacy_add_to_log')) { if (is_array($data[0])) { // Some events require several entries in 'log' table. foreach ($data as $d) { call_user_func_array(array($manager, 'legacy_add_to_log'), $d); } } else { call_user_func_array(array($manager, 'legacy_add_to_log'), $data); } } } } if (PHPUNIT_TEST and \phpunit_util::is_redirecting_events()) { $this->dispatched = true; \phpunit_util::event_triggered($this); return; } \core\event\manager::dispatch($this); $this->dispatched = true; if ($legacyeventname = static::get_legacy_eventname()) { events_trigger_legacy($legacyeventname, $this->get_legacy_eventdata()); } }
/** * Fills the temporary stats tables with new data * * This function is meant to be called to get a new day of data * * @param int timestamp of the start time of logs view * @param int timestamp of the end time of logs view * @return bool success (true) or failure(false) */ function stats_temp_table_fill($timestart, $timeend) { global $DB; // First decide from where we want the data. $params = array('timestart' => $timestart, 'timeend' => $timeend, 'participating' => \core\event\base::LEVEL_PARTICIPATING, 'teaching' => \core\event\base::LEVEL_TEACHING, 'loginevent1' => '\\core\\event\\user_loggedin', 'loginevent2' => '\\core\\event\\user_loggedin'); $filled = false; $manager = get_log_manager(); $stores = $manager->get_readers(); foreach ($stores as $store) { if ($store instanceof \core\log\sql_internal_table_reader) { $logtable = $store->get_internal_log_table_name(); if (!$logtable) { continue; } $sql = "SELECT COUNT('x')\n FROM {{$logtable}}\n WHERE timecreated >= :timestart AND timecreated < :timeend"; if (!$DB->get_field_sql($sql, $params)) { continue; } // Let's fake the old records using new log data. // We want only data relevant to educational process // done by real users. $sql = "INSERT INTO {temp_log1} (userid, course, action)\n\n SELECT userid,\n CASE\n WHEN courseid IS NULL THEN " . SITEID . "\n WHEN courseid = 0 THEN " . SITEID . "\n ELSE courseid\n END,\n CASE\n WHEN eventname = :loginevent1 THEN 'login'\n WHEN crud = 'r' THEN 'view'\n ELSE 'update'\n END\n FROM {{$logtable}}\n WHERE timecreated >= :timestart AND timecreated < :timeend\n AND (origin = 'web' OR origin = 'ws')\n AND (edulevel = :participating OR edulevel = :teaching OR eventname = :loginevent2)"; $DB->execute($sql, $params); $filled = true; } } if (!$filled) { // Fallback to legacy data. $sql = "INSERT INTO {temp_log1} (userid, course, action)\n\n SELECT userid, course, action\n FROM {log}\n WHERE time >= :timestart AND time < :timeend"; $DB->execute($sql, $params); } $sql = 'INSERT INTO {temp_log2} (userid, course, action) SELECT userid, course, action FROM {temp_log1}'; $DB->execute($sql); // We have just loaded all the temp tables, collect statistics for that. $DB->update_temp_table_stats(); return true; }
function __construct() { $this->logger = get_log_manager()->get_readers()['logstore_standard']; }
/** * Get the time the user has spent in the course * * @param int $userid User ID (default= $USER->id) * @return int the total time spent in seconds */ public function get_course_time($user = null) { global $CFG, $USER; if (empty($user)) { $userid = $USER->id; } else { if (is_object($user)) { $userid = $user->id; } else { $userid = $user; } } $manager = get_log_manager(); $selectreaders = $manager->get_readers('\\core\\log\\sql_reader'); $reader = reset($selectreaders); //This can take a log time to process, but it's accurate // it's can be done by get only first and last log entry creation time, // but it's far more inaccurate, could have an option to choose. set_time_limit(0); $totaltime = 0; $sql = "action = 'viewed' AND target = 'course' AND courseid = :courseid AND userid = :userid"; if ($logs = $reader->get_events_select($sql, array('courseid' => $this->get_course()->id, 'userid' => $userid), 'timecreated ASC', '', '')) { foreach ($logs as $log) { if (empty($login)) { // For the first time $login is not set so the first log is also the first login $login = $log->timecreated; $lasthit = $log->timecreated; } $delay = $log->timecreated - $lasthit; if (!($delay > $CFG->sessiontimeout)) { // The difference between the last log and the current log is more than // the timeout //Register session value so that we have found a new session! $totaltime += $delay; } // Now the actual log became the previous log for the next cycle $lasthit = $log->timecreated; } } return $totaltime / 60; }
/** * Test logmanager::get_supported_reports returns all reports that require this store. */ public function test_get_supported_reports() { $logmanager = get_log_manager(); $allreports = \core_component::get_plugin_list('report'); $supportedreports = array('report_log' => '/report/log', 'report_loglive' => '/report/loglive', 'report_outline' => '/report/outline', 'report_participation' => '/report/participation', 'report_stats' => '/report/stats'); // Make sure all supported reports are installed. $expectedreports = array_keys(array_intersect_key($allreports, $supportedreports)); $reports = $logmanager->get_supported_reports('logstore_standard'); $reports = array_keys($reports); foreach ($expectedreports as $expectedreport) { $this->assertContains($expectedreport, $reports); } }
function local_my_get_logstore_info() { $logmanager = get_log_manager(); $readers = $logmanager->get_readers('\\core\\log\\sql_select_reader'); $reader = reset($readers); if (empty($reader)) { return false; // No log reader found. } $logstoreinfo = new StdClass(); if ($reader instanceof \logstore_standard\log\store) { $logstoreinfo->table = 'logstore_standard_log'; $logstoreinfo->courseparam = 'courseid'; $logstoreinfo->timeparam = 'timecreated'; } else { if ($reader instanceof \logstore_legacy\log\store) { $logstoreinfo->table = 'log'; $logstoreinfo->courseparam = 'course'; $logstoreinfo->timeparam = 'time'; } else { return; } } return $logstoreinfo; }
protected function process_log($data) { global $DB; $data = (object) $data; $data->time = $this->apply_date_offset($data->time); $data->userid = $this->get_mappingid('user', $data->userid); $data->course = $this->get_courseid(); $data->cmid = $this->task->get_moduleid(); // For any reason user wasn't remapped ok, stop processing this if (empty($data->userid)) { return; } // Everything ready, let's delegate to the restore_logs_processor // Set some fixed values that will save tons of DB requests $values = array('course' => $this->get_courseid(), 'course_module' => $this->task->get_moduleid(), $this->task->get_modulename() => $this->task->get_activityid()); // Get instance and process log record $data = restore_logs_processor::get_instance($this->task, $values)->process_log_record($data); // If we have data, insert it, else something went wrong in the restore_logs_processor if ($data) { if (empty($data->url)) { $data->url = ''; } if (empty($data->info)) { $data->info = ''; } // Store the data in the legacy log table if we are still using it. $manager = get_log_manager(); if (method_exists($manager, 'legacy_add_to_log')) { $manager->legacy_add_to_log($data->course, $data->module, $data->action, $data->url, $data->info, $data->cmid, $data->userid); } } }
/** * Can the current user delete this course? * Course creators have exception, * 1 day after the creation they can sill delete the course. * @param int $courseid * @return boolean */ function can_delete_course($courseid) { global $USER; $context = context_course::instance($courseid); if (has_capability('moodle/course:delete', $context)) { return true; } // hack: now try to find out if creator created this course recently (1 day) if (!has_capability('moodle/course:create', $context)) { return false; } $since = time() - 60 * 60 * 24; $course = get_course($courseid); if ($course->timecreated < $since) { return false; // Return if the course was not created in last 24 hours. } $logmanger = get_log_manager(); $readers = $logmanger->get_readers('\\core\\log\\sql_reader'); $reader = reset($readers); if (empty($reader)) { return false; // No log reader found. } // A proper reader. $select = "userid = :userid AND courseid = :courseid AND eventname = :eventname AND timecreated > :since"; $params = array('userid' => $USER->id, 'since' => $since, 'courseid' => $course->id, 'eventname' => '\\core\\event\\course_created'); return (bool) $reader->get_events_select_count($select, $params); }
/** * Test sql_reader::get_events_select_iterator. * @return void */ public function test_events_traversable() { global $DB; $this->resetAfterTest(); $this->preventResetByRollback(); $this->setAdminUser(); set_config('enabled_stores', 'logstore_standard', 'tool_log'); $manager = get_log_manager(true); $stores = $manager->get_readers(); $store = $stores['logstore_standard']; $events = $store->get_events_select_iterator('', array(), '', 0, 0); $this->assertFalse($events->valid()); // Here it should be already closed, but we should be allowed to // over-close it without exception. $events->close(); $user = $this->getDataGenerator()->create_user(); for ($i = 0; $i < 1000; $i++) { \core\event\user_created::create_from_userid($user->id)->trigger(); } $store->flush(); // Check some various sizes get the right number of elements. $this->assertEquals(1, iterator_count($store->get_events_select_iterator('', array(), '', 0, 1))); $this->assertEquals(2, iterator_count($store->get_events_select_iterator('', array(), '', 0, 2))); $iterator = $store->get_events_select_iterator('', array(), '', 0, 500); $this->assertInstanceOf('\\core\\event\\base', $iterator->current()); $this->assertEquals(500, iterator_count($iterator)); $iterator->close(); // Look for non-linear memory usage for the iterator version. $mem = memory_get_usage(); $events = $store->get_events_select('', array(), '', 0, 0); $arraymemusage = memory_get_usage() - $mem; $mem = memory_get_usage(); $eventsit = $store->get_events_select_iterator('', array(), '', 0, 0); $eventsit->close(); $itmemusage = memory_get_usage() - $mem; $this->assertInstanceOf('\\Traversable', $eventsit); $this->assertLessThan($arraymemusage / 10, $itmemusage); set_config('enabled_stores', '', 'tool_log'); get_log_manager(true); }
/** * Get a list of enabled sql_reader objects/name * * @param bool $nameonly if true only reader names will be returned. * @return array core\log\sql_reader object or name. */ public function get_readers($nameonly = false) { if (!isset($this->logmanager)) { $this->logmanager = get_log_manager(); } $readers = $this->logmanager->get_readers('core\\log\\sql_reader'); if ($nameonly) { foreach ($readers as $pluginname => $reader) { $readers[$pluginname] = $reader->get_name(); } } return $readers; }
} $plugins[$plugin] = $strpluginname; } core_collator::asort($plugins); $like = $DB->sql_like('plugin', '?', true, true, false, '|'); $params = array('report|_%'); $installed = $DB->get_records_select('config_plugins', "{$like} AND name = 'version'", $params); $versions = array(); foreach ($installed as $config) { $name = preg_replace('/^report_/', '', $config->plugin); $versions[$name] = $config->value; if (!isset($plugins[$name])) { $plugins[$name] = $name; } } $logmanager = get_log_manager(); foreach ($plugins as $plugin => $name) { $uninstall = ''; if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('report_' . $plugin, 'manage')) { $uninstall = html_writer::link($uninstallurl, $struninstall); } $stores = $logmanager->get_supported_logstores('report_' . $plugin); if ($stores === false) { $supportedstores = get_string('logstorenotrequired', 'admin'); } else { if (!empty($stores)) { $supportedstores = implode(', ', $stores); } else { $supportedstores = get_string('nosupportedlogstore', 'admin'); } }
/** * Select all log records for a given course, user, and day * * @param int $userid The id of the user as found in the 'user' table. * @param int $courseid The id of the course as found in the 'course' table. * @param string $daystart unix timestamp of the start of the day for which the logs needs to be retrived * @param string $logreader log reader to use. * @return array */ function report_log_userday($userid, $courseid, $daystart, $logreader = '') { global $DB; $logmanager = get_log_manager(); $readers = $logmanager->get_readers(); if (empty($logreader)) { $reader = reset($readers); } else { $reader = $readers[$logreader]; } // If reader is not a sql_internal_table_reader and not legacy store then return. if (!$reader instanceof \core\log\sql_internal_table_reader && !$reader instanceof logstore_legacy\log\store) { return array(); } $daystart = (int) $daystart; // Note: unfortunately pg complains if you use name parameter or column alias in GROUP BY. if ($reader instanceof logstore_legacy\log\store) { $logtable = 'log'; $timefield = 'time'; $coursefield = 'course'; // Anonymous actions are never logged in legacy log. $nonanonymous = ''; } else { $logtable = $reader->get_internal_log_table_name(); $timefield = 'timecreated'; $coursefield = 'courseid'; $nonanonymous = 'AND anonymous = 0'; } $params = array('userid' => $userid); $courseselect = ''; if ($courseid) { $courseselect = "AND {$coursefield} = :courseid"; $params['courseid'] = $courseid; } return $DB->get_records_sql("SELECT FLOOR(({$timefield} - {$daystart})/" . HOURSECS . ") AS hour, COUNT(*) AS num\n FROM {" . $logtable . "}\n WHERE userid = :userid\n AND {$timefield} > {$daystart} {$courseselect} {$nonanonymous}\n GROUP BY FLOOR(({$timefield} - {$daystart})/" . HOURSECS . ") ", $params); }
protected static function init_log_status() { global $CFG; if (self::$uselegacy !== null) { return; } if ($CFG->branch < 27) { self::$uselegacy = true; return; } $manager = get_log_manager(); $allreaders = $manager->get_readers(); if (isset($allreaders['logstore_legacy'])) { self::$uselegacy = true; } else { self::$uselegacy = false; } if ($CFG->branch < 29) { $selectreaders = $manager->get_readers('\\core\\log\\sql_select_reader'); } else { $selectreaders = $manager->get_readers('\\core\\log\\sql_reader'); } if ($selectreaders) { self::$reader = reset($selectreaders); } }
/** * Checked if a user has attempted/viewed/etc. an activity/resource * * @param array $modules The modules used in the course * @param stdClass $config The blocks configuration settings * @param array $events The possible events that can occur for modules * @param int $userid The user's id * @param int $instance The instance of the block * @return array an describing the user's attempts based on module+instance identifiers */ function block_progress_attempts($modules, $config, $events, $userid, $course) { global $DB; $attempts = array(); $modernlogging = false; $cachingused = false; // Get readers for 2.7 onwards. if (function_exists('get_log_manager')) { $modernlogging = true; $logmanager = get_log_manager(); $readers = $logmanager->get_readers(); $numreaders = count($readers); } // Get cache store if caching is working 2.4 onwards. if (class_exists('cache')) { $cachingused = true; $cachedlogs = cache::make('block_progress', 'cachedlogs'); $cachedlogviews = $cachedlogs->get($userid); if (empty($cachedlogviews)) { $cachedlogviews = array(); } $cachedlogsupdated = false; } foreach ($events as $event) { $module = $modules[$event['type']]; $uniqueid = $event['type'] . $event['id']; $parameters = array('courseid' => $course, 'courseid1' => $course, 'userid' => $userid, 'userid1' => $userid, 'eventid' => $event['id'], 'eventid1' => $event['id'], 'cmid' => $event['cm']->id, 'cmid1' => $event['cm']->id); // Check for passing grades as unattempted, passed or failed. if (isset($config->{'action_' . $uniqueid}) && $config->{'action_' . $uniqueid} == 'passed') { $query = $module['actions'][$config->{'action_' . $uniqueid}]; $graderesult = $DB->get_record_sql($query, $parameters); if ($graderesult === false || $graderesult->finalgrade === null) { $attempts[$uniqueid] = false; } else { $attempts[$uniqueid] = $graderesult->finalgrade >= $graderesult->gradepass ? true : 'failed'; } } else { if (isset($config->{'action_' . $uniqueid}) && $config->{'action_' . $uniqueid} == 'viewed') { $attempts[$uniqueid] = false; // Check if the value is cached. if ($cachingused && array_key_exists($uniqueid, $cachedlogviews) && $cachedlogviews[$uniqueid]) { $attempts[$uniqueid] = true; } else { if ($modernlogging) { foreach ($readers as $logstore => $reader) { if ($reader instanceof \core\log\sql_internal_table_reader || $reader instanceof \core\log\sql_internal_reader) { $logtable = '{' . $reader->get_internal_log_table_name() . '}'; $query = preg_replace('/\\{log\\}/', $logtable, $module['actions']['viewed']['sql_internal_reader']); } else { if ($reader instanceof logstore_legacy\log\store) { $query = $module['actions']['viewed']['logstore_legacy']; } else { // No logs available. continue; } } $attempts[$uniqueid] = $DB->record_exists_sql($query, $parameters) ? true : false; if ($attempts[$uniqueid]) { $cachedlogviews[$uniqueid] = true; $cachedlogsupdated = true; break; } } } else { $query = $module['actions']['viewed']['logstore_legacy']; $attempts[$uniqueid] = $DB->record_exists_sql($query, $parameters) ? true : false; if ($cachingused && $attempts[$uniqueid]) { $cachedlogviews[$uniqueid] = true; $cachedlogsupdated = true; } } } } else { // If activity completion is used, check completions table. if (isset($config->{'action_' . $uniqueid}) && $config->{'action_' . $uniqueid} == 'activity_completion') { $query = 'SELECT id FROM {course_modules_completion} WHERE userid = :userid AND coursemoduleid = :cmid AND completionstate >= 1'; } else { $action = isset($config->{'action_' . $uniqueid}) ? $config->{'action_' . $uniqueid} : $module['defaultAction']; $query = $module['actions'][$action]; } // Check if the user has attempted the module. $attempts[$uniqueid] = $DB->record_exists_sql($query, $parameters) ? true : false; } } } // Update log cache if new values were added. if ($cachingused && $cachedlogsupdated) { $cachedlogs->set($userid, $cachedlogviews); } return $attempts; }
/** * Get the time the user has spent in the course * * @param int $courseid * @return int the total time spent in seconds */ function certificate_get_course_time($courseid) { global $CFG, $DB, $USER; $logmanager = get_log_manager(); $readers = $logmanager->get_readers(); $enabledreaders = get_config('tool_log', 'enabled_stores'); $enabledreaders = explode(',', $enabledreaders); // Go through all the readers until we find one that we can use. foreach ($enabledreaders as $enabledreader) { $reader = $readers[$enabledreader]; if ($reader instanceof \logstore_legacy\log\store) { $logtable = 'log'; $coursefield = 'course'; $timefield = 'time'; break; } else { if ($reader instanceof \core\log\sql_internal_table_reader) { $logtable = $reader->get_internal_log_table_name(); $coursefield = 'courseid'; $timefield = 'timecreated'; break; } } } // If we didn't find a reader then return 0. if (!isset($logtable)) { return 0; } $sql = "SELECT id, {$timefield}\n FROM {{$logtable}}\n WHERE userid = :userid\n AND {$coursefield} = :courseid\n ORDER BY {$timefield} ASC"; $params = array('userid' => $USER->id, 'courseid' => $courseid); $totaltime = 0; if ($logs = $DB->get_recordset_sql($sql, $params)) { foreach ($logs as $log) { if (!isset($login)) { // For the first time $login is not set so the first log is also the first login $login = $log->{$timefield}; $lasthit = $log->{$timefield}; $totaltime = 0; } $delay = $log->{$timefield} - $lasthit; if ($delay > $CFG->sessiontimeout * 60) { // The difference between the last log and the current log is more than // the timeout Register session value so that we have found a session! $login = $log->{$timefield}; } else { $totaltime += $delay; } // Now the actual log became the previous log for the next cycle $lasthit = $log->{$timefield}; } return $totaltime; } return 0; }
/** * get_rawdata * * @param int $startdate * @param int $enddate * @access protected * @return array array of risk values, keyed on userid */ protected function get_rawdata($startdate, $enddate) { global $DB; $sessions = array(); // set the sql based on log reader(s) available $params = array(); $params['courseid_legacy'] = $params['courseid_standard'] = $this->courseid; $params['startdate_legacy'] = $params['startdate_standard'] = $startdate; $params['enddate_legacy'] = $params['enddate_standard'] = $enddate; $sql = array(); $logmanager = get_log_manager(); $readers = $logmanager->get_readers(); foreach ($readers as $reader) { if ($reader instanceof \logstore_legacy\log\store) { $sql['legacy'] = 'SELECT id, userid, time FROM {log} WHERE course = :courseid_legacy AND time >= :startdate_legacy AND time <= :enddate_legacy'; } else { if ($reader instanceof \logstore_standard\log\store) { $sql['standard'] = 'SELECT id, userid, timecreated AS time FROM {logstore_standard_log} WHERE courseid = :courseid_standard AND timecreated >= :startdate_standard AND timecreated <= :enddate_standard'; } } } $query_sql = 'SELECT c.id, c.userid, c.time FROM (' . implode(' UNION ', $sql) . ') c ORDER BY time ASC'; // read logs $logs = $DB->get_recordset_sql($query_sql, $params); if ($logs) { // Need to calculate sessions, sessions are defined by time between consequtive logs not exceeding setting. foreach ($logs as $log) { $increment = false; $week = date('W', $log->time); if (!isset($sessions[$log->userid])) { $sessions[$log->userid] = array('total' => 0, 'weeks' => array(), 'pastweek' => 0, 'lengths' => array(), 'start' => 0); } if (!isset($sessions[$log->userid]['lastlogin'])) { $increment = true; } else { if ($log->time - $this->config['session_length'] > $sessions[$log->userid]['lastlogin']) { $increment = true; } } if ($increment) { if ($sessions[$log->userid]['start'] > 0) { $sessions[$log->userid]['lengths'][] = $sessions[$log->userid]['lastlogin'] - $sessions[$log->userid]['start']; } $sessions[$log->userid]['total']++; $sessions[$log->userid]['start'] = $log->time; if (!isset($sessions[$log->userid]['weeks'][$week])) { $sessions[$log->userid]['weeks'][$week] = 0; } $sessions[$log->userid]['weeks'][$week]++; if ($log->time > $enddate - WEEKSECS) { // Session in past week. $sessions[$log->userid]['pastweek']++; } } $sessions[$log->userid]['lastlogin'] = $log->time; } $logs->close(); } return $sessions; }
/** * Test the temporary table creation and deletion. * * @depends test_statslib_temp_table_create_and_drop */ public function test_statslib_temp_table_fill() { global $CFG, $DB, $USER; $dataset = $this->load_xml_data_file(__DIR__ . "/fixtures/statslib-test09.xml"); $this->prepare_db($dataset[0], array('log')); $start = self::DAY - get_timezone_offset($CFG->timezone); $end = $start + 24 * 3600; stats_temp_table_create(); stats_temp_table_fill($start, $end); $this->assertEquals(1, $DB->count_records('temp_log1')); $this->assertEquals(1, $DB->count_records('temp_log2')); stats_temp_table_drop(); // New log stores. $this->preventResetByRollback(); stats_temp_table_create(); $course = $this->getDataGenerator()->create_course(); $context = context_course::instance($course->id); $fcontext = context_course::instance(SITEID); $user = $this->getDataGenerator()->create_user(); $this->setUser($user); $this->assertFileExists("{$CFG->dirroot}/{$CFG->admin}/tool/log/store/standard/version.php"); set_config('enabled_stores', 'logstore_standard', 'tool_log'); set_config('buffersize', 0, 'logstore_standard'); set_config('logguests', 1, 'logstore_standard'); get_log_manager(true); $DB->delete_records('logstore_standard_log'); \core_tests\event\create_executed::create(array('context' => $fcontext, 'courseid' => SITEID))->trigger(); \core_tests\event\read_executed::create(array('context' => $context, 'courseid' => $course->id))->trigger(); \core_tests\event\update_executed::create(array('context' => context_system::instance()))->trigger(); \core_tests\event\delete_executed::create(array('context' => context_system::instance()))->trigger(); \core\event\user_loggedin::create(array('userid' => $USER->id, 'objectid' => $USER->id, 'other' => array('username' => $USER->username)))->trigger(); $DB->set_field('logstore_standard_log', 'timecreated', 10); $this->assertEquals(5, $DB->count_records('logstore_standard_log')); \core_tests\event\delete_executed::create(array('context' => context_system::instance()))->trigger(); \core_tests\event\delete_executed::create(array('context' => context_system::instance()))->trigger(); // Fake the origin of events. $DB->set_field('logstore_standard_log', 'origin', 'web', array()); $this->assertEquals(7, $DB->count_records('logstore_standard_log')); stats_temp_table_fill(9, 11); $logs1 = $DB->get_records('temp_log1'); $logs2 = $DB->get_records('temp_log2'); $this->assertCount(5, $logs1); $this->assertCount(5, $logs2); // The order of records in the temp tables is not guaranteed... $viewcount = 0; $updatecount = 0; $logincount = 0; foreach ($logs1 as $log) { if ($log->course == $course->id) { $this->assertEquals('view', $log->action); $viewcount++; } else { $this->assertTrue(in_array($log->action, array('update', 'login'))); if ($log->action === 'update') { $updatecount++; } else { $logincount++; } $this->assertEquals(SITEID, $log->course); } $this->assertEquals($user->id, $log->userid); } $this->assertEquals(1, $viewcount); $this->assertEquals(3, $updatecount); $this->assertEquals(1, $logincount); set_config('enabled_stores', '', 'tool_log'); get_log_manager(true); stats_temp_table_drop(); }
public static function observer(\core\event\base $event) { global $DB; $unlocked_content = false; if (!block_game_content_unlock_helper::is_student($event->userid)) { return; } $uss = $DB->get_records_sql("SELECT * FROM {content_unlock_system} WHERE deleted = ? AND " . $DB->sql_compare_text('conditions') . " = " . $DB->sql_compare_text('?'), array('deleted' => 0, 'conditions' => $event->eventname)); foreach ($uss as $unlocksystem) { $ccm = get_course_and_cm_from_cmid($unlocksystem->coursemoduleid); if ($event->courseid != $ccm[0]->id) { continue; } if (!(content_unlock_satisfies_conditions($unlocksystem->restrictions, $event->courseid, $event->userid) && content_unlock_satisfies_advanced_conditions($unlocksystem, $event) && content_unlock_satisfies_block_conditions($unlocksystem, $event->courseid, $event->userid))) { continue; } $blockcontextid = $DB->get_field('block_instances', 'parentcontextid', array('id' => $unlocksystem->blockinstanceid)); if (!$blockcontextid) { continue; } $blockcontext = context::instance_by_id($blockcontextid); $context = context::instance_by_id($event->contextid); if (strpos($context->path, $blockcontext->path) !== 0 && $blockcontext->instanceid != SITEID) { continue; } $sql = "SELECT count(c.id)\n\t\t\t\tFROM {content_unlock_log} c\n\t\t\t\t\tINNER JOIN {logstore_standard_log} l ON c.logid = l.id\n\t\t\t\tWHERE l.userid = :userid\n\t\t\t\t\tAND c.unlocksystemid = :unlocksystemid"; $params['userid'] = $event->userid; $params['unlocksystemid'] = $unlocksystem->id; if ($DB->count_records_sql($sql, $params) > 0) { continue; } $manager = get_log_manager(); $selectreaders = $manager->get_readers('\\core\\log\\sql_reader'); if ($selectreaders) { $reader = reset($selectreaders); } $selectwhere = "eventname = :eventname\n\t\t\t\tAND component = :component\n\t\t\t\tAND action = :action\n\t\t\t\tAND target = :target\n\t\t\t\tAND crud = :crud\n\t\t\t\tAND edulevel = :edulevel\n\t\t\t\tAND contextid = :contextid\n\t\t\t\tAND contextlevel = :contextlevel\n\t\t\t\tAND contextinstanceid = :contextinstanceid\n\t\t\t\tAND userid = :userid \n\t\t\t\tAND anonymous = :anonymous\n\t\t\t\tAND timecreated = :timecreated"; $params['eventname'] = $event->eventname; $params['component'] = $event->component; $params['action'] = $event->action; $params['target'] = $event->target; $params['crud'] = $event->crud; $params['edulevel'] = $event->edulevel; $params['contextid'] = $event->contextid; $params['contextlevel'] = $event->contextlevel; $params['contextinstanceid'] = $event->contextinstanceid; $params['userid'] = $event->userid; $params['anonymous'] = $event->anonymous; $params['timecreated'] = $event->timecreated; $logid = $reader->get_events_select($selectwhere, $params, '', 0, 0); $logid = array_keys($logid)[0]; $record = new stdClass(); $record->logid = $logid; $record->unlocksystemid = $unlocksystem->id; $DB->insert_record('content_unlock_log', $record); $unlocked_content = true; if ($unlocksystem->mode == 0) { if ($unlocksystem->coursemodulevisibility == 1) { set_section_visible($event->courseid, $ccm[1]->sectionnum, 1); } set_coursemodule_visible($unlocksystem->coursemoduleid, $unlocksystem->coursemodulevisibility); } else { groups_add_member($unlocksystem->groupid, $event->userid); } } if ($unlocked_content) { $params = array('context' => $context); $event = \block_game_content_unlock\event\content_unlocked::create($params); $event->trigger(); } }
/** * Do the job. * Throw exceptions on errors (the job will be retried). */ public function execute() { global $CFG, $DB; if (empty($CFG->notifyloginfailures)) { return; } $recip = get_users_from_config($CFG->notifyloginfailures, 'moodle/site:config'); if (empty($CFG->lastnotifyfailure)) { $CFG->lastnotifyfailure = 0; } // If it has been less than an hour, or if there are no recipients, don't execute. if (time() - HOURSECS < $CFG->lastnotifyfailure || !is_array($recip) || count($recip) <= 0) { return; } // We need to deal with the threshold stuff first. if (empty($CFG->notifyloginthreshold)) { $CFG->notifyloginthreshold = 10; // Default to something sensible. } // Get all the IPs with more than notifyloginthreshold failures since lastnotifyfailure // and insert them into the cache_flags temp table. $logmang = get_log_manager(); $readers = $logmang->get_readers('\\core\\log\\sql_internal_reader'); $reader = reset($readers); $readername = key($readers); if (empty($reader) || empty($readername)) { // No readers, no processing. return true; } $logtable = $reader->get_internal_log_table_name(); $sql = "SELECT ip, COUNT(*)\n FROM {" . $logtable . "}\n WHERE eventname = ?\n AND timecreated > ?\n GROUP BY ip\n HAVING COUNT(*) >= ?"; $params = array('\\core\\event\\user_login_failed', $CFG->lastnotifyfailure, $CFG->notifyloginthreshold); $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $iprec) { if (!empty($iprec->ip)) { set_cache_flag('login_failure_by_ip', $iprec->ip, '1', 0); } } $rs->close(); // Get all the INFOs with more than notifyloginthreshold failures since lastnotifyfailure // and insert them into the cache_flags temp table. $sql = "SELECT userid, count(*)\n FROM {" . $logtable . "}\n WHERE eventname = ?\n AND timecreated > ?\n GROUP BY userid\n HAVING count(*) >= ?"; $params = array('\\core\\event\\user_login_failed', $CFG->lastnotifyfailure, $CFG->notifyloginthreshold); $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $inforec) { if (!empty($inforec->info)) { set_cache_flag('login_failure_by_id', $inforec->userid, '1', 0); } } $rs->close(); // Now, select all the login error logged records belonging to the ips and infos // since lastnotifyfailure, that we have stored in the cache_flags table. $sql = "SELECT * FROM (\n SELECT l.*, u.username\n FROM {" . $logtable . "} l\n JOIN {cache_flags} cf ON l.ip = cf.name\n LEFT JOIN {user} u ON l.userid = u.id\n WHERE l.eventname = ?\n AND l.timecreated > ?\n AND cf.flagtype = 'login_failure_by_ip'\n UNION ALL\n SELECT l.*, u.username\n FROM {" . $logtable . "} l\n JOIN {cache_flags} cf ON l.userid = " . $DB->sql_cast_char2int('cf.name') . "\n LEFT JOIN {user} u ON l.userid = u.id\n WHERE l.eventname = ?\n AND l.timecreated > ?\n AND cf.flagtype = 'login_failure_by_info') t\n ORDER BY t.timecreated DESC"; $params = array('\\core\\event\\user_login_failed', $CFG->lastnotifyfailure, '\\core\\event\\user_login_failed', $CFG->lastnotifyfailure); // Init some variables. $count = 0; $messages = ''; // Iterate over the logs recordset. $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $log) { $a = new \stdClass(); $a->time = userdate($log->timecreated); if (empty($log->username)) { // Entries with no valid username. We get attempted username from the event's other field. $other = unserialize($log->other); $a->info = empty($other['username']) ? '' : $other['username']; } else { $a->info = $log->username; } $a->ip = $log->ip; $messages .= get_string('notifyloginfailuresmessage', '', $a) . "\n"; $count++; } $rs->close(); // If we have something useful to report. if ($count > 0) { $site = get_site(); $subject = get_string('notifyloginfailuressubject', '', format_string($site->fullname)); // Calculate the complete body of notification (start + messages + end). $params = array('id' => 0, 'modid' => 'site_errors', 'chooselog' => '1', 'logreader' => $readername); $url = new \moodle_url('/report/log/index.php', $params); $body = get_string('notifyloginfailuresmessagestart', '', $CFG->wwwroot) . ($CFG->lastnotifyfailure != 0 ? '(' . userdate($CFG->lastnotifyfailure) . ')' : '') . "\n\n" . $messages . "\n\n" . get_string('notifyloginfailuresmessageend', '', $url->out(false) . ' ') . "\n\n"; // For each destination, send mail. mtrace('Emailing admins about ' . $count . ' failed login attempts'); foreach ($recip as $admin) { // Emailing the admins directly rather than putting these through the messaging system. email_to_user($admin, \core_user::get_support_user(), $subject, $body); } } // Update lastnotifyfailure with current time. set_config('lastnotifyfailure', time()); // Finally, delete all the temp records we have created in cache_flags. $DB->delete_records_select('cache_flags', "flagtype IN ('login_failure_by_ip', 'login_failure_by_info')"); }
/** * Reset contents of all database tables to initial values, reset caches, etc. * * Note: this is relatively slow (cca 2 seconds for pg and 7 for mysql) - please use with care! * * @static * @param bool $detectchanges * true - changes in global state and database are reported as errors * false - no errors reported * null - only critical problems are reported as errors * @return void */ public static function reset_all_data($detectchanges = false) { global $DB, $CFG, $USER, $SITE, $COURSE, $PAGE, $OUTPUT, $SESSION, $FULLME; // Stop any message redirection. self::stop_message_redirection(); // Stop any message redirection. self::stop_event_redirection(); // Start a new email redirection. // This will clear any existing phpmailer redirection. // We redirect all phpmailer output to this message sink which is // called instead of phpmailer actually sending the message. self::start_phpmailer_redirection(); // We used to call gc_collect_cycles here to ensure desctructors were called between tests. // This accounted for 25% of the total time running phpunit - so we removed it. // Show any unhandled debugging messages, the runbare() could already reset it. self::display_debugging_messages(); self::reset_debugging(); // reset global $DB in case somebody mocked it $DB = self::get_global_backup('DB'); if ($DB->is_transaction_started()) { // we can not reset inside transaction $DB->force_transaction_rollback(); } $resetdb = self::reset_database(); $localename = self::get_locale_name(); $warnings = array(); if ($detectchanges === true) { if ($resetdb) { $warnings[] = 'Warning: unexpected database modification, resetting DB state'; } $oldcfg = self::get_global_backup('CFG'); $oldsite = self::get_global_backup('SITE'); foreach ($CFG as $k => $v) { if (!property_exists($oldcfg, $k)) { $warnings[] = 'Warning: unexpected new $CFG->' . $k . ' value'; } else { if ($oldcfg->{$k} !== $CFG->{$k}) { $warnings[] = 'Warning: unexpected change of $CFG->' . $k . ' value'; } } unset($oldcfg->{$k}); } if ($oldcfg) { foreach ($oldcfg as $k => $v) { $warnings[] = 'Warning: unexpected removal of $CFG->' . $k; } } if ($USER->id != 0) { $warnings[] = 'Warning: unexpected change of $USER'; } if ($COURSE->id != $oldsite->id) { $warnings[] = 'Warning: unexpected change of $COURSE'; } if ($FULLME !== self::get_global_backup('FULLME')) { $warnings[] = 'Warning: unexpected change of $FULLME'; } if (setlocale(LC_TIME, 0) !== $localename) { $warnings[] = 'Warning: unexpected change of locale'; } } if (ini_get('max_execution_time') != 0) { // This is special warning for all resets because we do not want any // libraries to mess with timeouts unintentionally. // Our PHPUnit integration is not supposed to change it either. if ($detectchanges !== false) { $warnings[] = 'Warning: max_execution_time was changed to ' . ini_get('max_execution_time'); } set_time_limit(0); } // restore original globals $_SERVER = self::get_global_backup('_SERVER'); $CFG = self::get_global_backup('CFG'); $SITE = self::get_global_backup('SITE'); $FULLME = self::get_global_backup('FULLME'); $_GET = array(); $_POST = array(); $_FILES = array(); $_REQUEST = array(); $COURSE = $SITE; // reinitialise following globals $OUTPUT = new bootstrap_renderer(); $PAGE = new moodle_page(); $FULLME = null; $ME = null; $SCRIPT = null; // Empty sessison and set fresh new not-logged-in user. \core\session\manager::init_empty_session(); // reset all static caches \core\event\manager::phpunit_reset(); accesslib_clear_all_caches(true); get_string_manager()->reset_caches(true); reset_text_filters_cache(true); events_get_handlers('reset'); core_text::reset_caches(); get_message_processors(false, true, true); filter_manager::reset_caches(); core_filetypes::reset_caches(); \core_search\manager::clear_static(); core_user::reset_caches(); if (class_exists('core_media_manager', false)) { core_media_manager::reset_caches(); } // Reset static unit test options. if (class_exists('\\availability_date\\condition', false)) { \availability_date\condition::set_current_time_for_test(0); } // Reset internal users. core_user::reset_internal_users(); //TODO MDL-25290: add more resets here and probably refactor them to new core function // Reset course and module caches. if (class_exists('format_base')) { // If file containing class is not loaded, there is no cache there anyway. format_base::reset_course_cache(0); } get_fast_modinfo(0, 0, true); // Reset other singletons. if (class_exists('core_plugin_manager')) { core_plugin_manager::reset_caches(true); } if (class_exists('\\core\\update\\checker')) { \core\update\checker::reset_caches(true); } // Clear static cache within restore. if (class_exists('restore_section_structure_step')) { restore_section_structure_step::reset_caches(); } // purge dataroot directory self::reset_dataroot(); // restore original config once more in case resetting of caches changed CFG $CFG = self::get_global_backup('CFG'); // inform data generator self::get_data_generator()->reset(); // fix PHP settings error_reporting($CFG->debug); // Reset the date/time class. core_date::phpunit_reset(); // Make sure the time locale is consistent - that is Australian English. setlocale(LC_TIME, $localename); // Reset the log manager cache. get_log_manager(true); // verify db writes just in case something goes wrong in reset if (self::$lastdbwrites != $DB->perf_get_writes()) { error_log('Unexpected DB writes in phpunit_util::reset_all_data()'); self::$lastdbwrites = $DB->perf_get_writes(); } if ($warnings) { $warnings = implode("\n", $warnings); trigger_error($warnings, E_USER_WARNING); } }
/** * Check logs to find out if a course was modified since the given time. * * @param int $courseid course id to check * @param int $since timestamp, from which to check * * @return bool true if the course was modified, false otherwise. This also returns false if no readers are enabled. This is * intentional, since we cannot reliably determine if any modification was made or not. */ protected static function is_course_modified($courseid, $since) { $logmang = get_log_manager(); $readers = $logmang->get_readers('core\\log\\sql_select_reader'); $where = "courseid = :courseid and timecreated > :since and crud <> 'r'"; $params = array('courseid' => $courseid, 'since' => $since); foreach ($readers as $reader) { if ($reader->get_events_select_count($where, $params)) { return true; } } return false; }
function cr_logging_info() { global $DB, $CFG; static $uselegacyreader; static $useinternalreader; static $logtable; if (isset($uselegacyreader) && isset($useinternalreader) && isset($logtable)) { return array($uselegacyreader, $useinternalreader, $logtable); } $uselegacyreader = false; // Flag to determine if we should use the legacy reader. $useinternalreader = false; // Flag to determine if we should use the internal reader. $logtable = ''; // Pre 2.7. if ($CFG->version < 2014051200) { $uselegacyreader = true; $logtable = 'log'; } else { // Get list of readers. $logmanager = get_log_manager(); $readers = $logmanager->get_readers(); // Get preferred reader. if (!empty($readers)) { foreach ($readers as $readerpluginname => $reader) { // If legacy reader is preferred reader. if ($readerpluginname == 'logstore_legacy') { $uselegacyreader = true; $logtable = 'log'; } // If sql_internal_table_reader is preferred reader. if ($reader instanceof \core\log\sql_internal_table_reader or $reader instanceof \core\log\sql_internal_reader) { $useinternalreader = true; $logtable = $reader->get_internal_log_table_name(); } } } } return array($uselegacyreader, $useinternalreader, $logtable); }