���ѧۧݧ�ӧ�� �ާ֧ߧ֧էا֧� - ���֧էѧܧ�ڧ��ӧѧ�� - /home3/cpr76684/public_html/database.tar
���ѧ٧ѧ�
db/upgrade.php 0000644 00000002532 15152567426 0007310 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Database log store upgrade. * * @package logstore_database * @copyright 2014 onwards Ankit Agarwal <ankit.agrr@gmail.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); function xmldb_logstore_database_upgrade($oldversion) { global $CFG; // Automatically generated Moodle v3.9.0 release upgrade line. // Put any upgrade step following this. // Automatically generated Moodle v4.0.0 release upgrade line. // Put any upgrade step following this. // Automatically generated Moodle v4.1.0 release upgrade line. // Put any upgrade step following this. return true; } test_settings.php 0000644 00000007530 15152567426 0010176 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Filter form. * * @package logstore_database * @copyright 2014 onwards Ankit Agarwal * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once('../../../../../config.php'); require_once($CFG->dirroot . '/lib/adminlib.php'); require_sesskey(); navigation_node::override_active_url(new moodle_url('/admin/settings.php', array('section' => 'logsettingdatabase'))); admin_externalpage_setup('logstoredbtestsettings'); echo $OUTPUT->header(); echo $OUTPUT->heading(get_string('testingsettings', 'logstore_database')); // NOTE: this is not localised intentionally, admins are supposed to understand English at least a bit... raise_memory_limit(MEMORY_HUGE); $dbtable = get_config('logstore_database', 'dbtable'); if (empty($dbtable)) { echo $OUTPUT->notification('External table not specified.', 'notifyproblem'); die(); } $dbdriver = get_config('logstore_database', 'dbdriver'); list($dblibrary, $dbtype) = explode('/', $dbdriver); if (!$db = \moodle_database::get_driver_instance($dbtype, $dblibrary, true)) { echo $OUTPUT->notification("Unknown driver $dblibrary/$dbtype", "notifyproblem"); die(); } $olddebug = $CFG->debug; $olddisplay = ini_get('display_errors'); ini_set('display_errors', '1'); $CFG->debug = DEBUG_DEVELOPER; error_reporting($CFG->debug); $dboptions = array(); $dboptions['dbpersist'] = get_config('logstore_database', 'dbpersist'); $dboptions['dbsocket'] = get_config('logstore_database', 'dbsocket'); $dboptions['dbport'] = get_config('logstore_database', 'dbport'); $dboptions['dbschema'] = get_config('logstore_database', 'dbschema'); $dboptions['dbcollation'] = get_config('logstore_database', 'dbcollation'); $dboptions['dbhandlesoptions'] = get_config('logstore_database', 'dbhandlesoptions'); try { $db->connect(get_config('logstore_database', 'dbhost'), get_config('logstore_database', 'dbuser'), get_config('logstore_database', 'dbpass'), get_config('logstore_database', 'dbname'), false, $dboptions); } catch (\moodle_exception $e) { echo $OUTPUT->notification('Cannot connect to the database.', 'notifyproblem'); $CFG->debug = $olddebug; ini_set('display_errors', $olddisplay); error_reporting($CFG->debug); ob_end_flush(); echo $OUTPUT->footer(); die(); } echo $OUTPUT->notification('Connection made.', 'notifysuccess'); $tables = $db->get_tables(); if (!in_array($dbtable, $tables)) { echo $OUTPUT->notification('Cannot find the specified table ' . $dbtable, 'notifyproblem'); $CFG->debug = $olddebug; ini_set('display_errors', $olddisplay); error_reporting($CFG->debug); ob_end_flush(); echo $OUTPUT->footer(); die(); } echo $OUTPUT->notification('Table ' . $dbtable . ' found.', 'notifysuccess'); $cols = $db->get_columns($dbtable); if (empty($cols)) { echo $OUTPUT->notification('Can not read external table.', 'notifyproblem'); } else { $columns = array_keys((array)$cols); echo $OUTPUT->notification('External table contains following columns:<br />' . implode(', ', $columns), 'notifysuccess'); } $db->dispose(); $CFG->debug = $olddebug; ini_set('display_errors', $olddisplay); error_reporting($CFG->debug); ob_end_flush(); echo $OUTPUT->footer(); tests/fixtures/event.php 0000644 00000002624 15152567426 0011432 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Fixtures for database log storage testing. * * @package logstore_database * @copyright 2014 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace logstore_database\event; defined('MOODLE_INTERNAL') || die(); class unittest_executed extends \core\event\base { public static function get_name() { return 'xxx'; } public function get_description() { return 'yyy'; } protected function init() { $this->data['crud'] = 'u'; $this->data['edulevel'] = self::LEVEL_PARTICIPATING; } public function get_url() { return new \moodle_url('/somepath/somefile.php', array('id' => $this->data['other']['sample'])); } } tests/fixtures/store.php 0000644 00000002356 15152567426 0011447 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Fixtures for database log storage testing. * * @package logstore_database * @copyright 2014 onwards Ankit Agarwal * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace logstore_database\test; defined('MOODLE_INTERNAL') || die(); class store extends \logstore_database\log\store { /** * Public wrapper for testing. * * @param \core\event\base $event * * @return bool */ public function is_event_ignored(\core\event\base $event) { return parent::is_event_ignored($event); } } tests/privacy/provider_test.php 0000644 00000057340 15152567426 0013013 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Data provider tests. * * @package logstore_database * @category test * @copyright 2018 Frédéric Massart * @author Frédéric Massart <fred@branchup.tech> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace logstore_database\privacy; defined('MOODLE_INTERNAL') || die(); global $CFG; use core_privacy\tests\provider_testcase; use core_privacy\local\request\contextlist; use core_privacy\local\request\approved_contextlist; use core_privacy\local\request\transform; use core_privacy\local\request\writer; use logstore_database\privacy\provider; require_once(__DIR__ . '/../fixtures/event.php'); /** * Data provider testcase class. * * This testcase is almost identical to the logstore_standard testcase, aside from the * initialisation of the relevant logstore obviously. * * @package logstore_database * @category test * @copyright 2018 Frédéric Massart * @author Frédéric Massart <fred@branchup.tech> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider_test extends provider_testcase { public function setUp(): void { global $CFG; $this->resetAfterTest(); $this->preventResetByRollback(); // Logging waits till the transaction gets committed. // Fake the settings, we will abuse the standard plugin table here... set_config('dbdriver', $CFG->dblibrary . '/' . $CFG->dbtype, 'logstore_database'); set_config('dbhost', $CFG->dbhost, 'logstore_database'); set_config('dbuser', $CFG->dbuser, 'logstore_database'); set_config('dbpass', $CFG->dbpass, 'logstore_database'); set_config('dbname', $CFG->dbname, 'logstore_database'); set_config('dbtable', $CFG->prefix . 'logstore_standard_log', 'logstore_database'); if (!empty($CFG->dboptions['dbpersist'])) { set_config('dbpersist', 1, 'logstore_database'); } else { set_config('dbpersist', 0, 'logstore_database'); } if (!empty($CFG->dboptions['dbsocket'])) { set_config('dbsocket', $CFG->dboptions['dbsocket'], 'logstore_database'); } else { set_config('dbsocket', '', 'logstore_database'); } if (!empty($CFG->dboptions['dbport'])) { set_config('dbport', $CFG->dboptions['dbport'], 'logstore_database'); } else { set_config('dbport', '', 'logstore_database'); } if (!empty($CFG->dboptions['dbschema'])) { set_config('dbschema', $CFG->dboptions['dbschema'], 'logstore_database'); } else { set_config('dbschema', '', 'logstore_database'); } if (!empty($CFG->dboptions['dbcollation'])) { set_config('dbcollation', $CFG->dboptions['dbcollation'], 'logstore_database'); } else { set_config('dbcollation', '', 'logstore_database'); } if (!empty($CFG->dboptions['dbhandlesoptions'])) { set_config('dbhandlesoptions', $CFG->dboptions['dbhandlesoptions'], 'logstore_database'); } else { set_config('dbhandlesoptions', false, 'logstore_database'); } } public function test_get_contexts_for_userid() { $admin = \core_user::get_user(2); $u1 = $this->getDataGenerator()->create_user(); $u2 = $this->getDataGenerator()->create_user(); $u3 = $this->getDataGenerator()->create_user(); $c1 = $this->getDataGenerator()->create_course(); $cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]); $c2 = $this->getDataGenerator()->create_course(); $cm2 = $this->getDataGenerator()->create_module('url', ['course' => $c2]); $sysctx = \context_system::instance(); $c1ctx = \context_course::instance($c1->id); $c2ctx = \context_course::instance($c2->id); $cm1ctx = \context_module::instance($cm1->cmid); $cm2ctx = \context_module::instance($cm2->cmid); $this->enable_logging(); $manager = get_log_manager(true); // User 1 is the author. $this->setUser($u1); $this->assert_contextlist_equals($this->get_contextlist_for_user($u1), []); $e = \logstore_database\event\unittest_executed::create(['context' => $cm1ctx]); $e->trigger(); $this->assert_contextlist_equals($this->get_contextlist_for_user($u1), [$cm1ctx]); // User 2 is the related user. $this->setUser(0); $this->assert_contextlist_equals($this->get_contextlist_for_user($u2), []); $e = \logstore_database\event\unittest_executed::create(['context' => $cm2ctx, 'relateduserid' => $u2->id]); $e->trigger(); $this->assert_contextlist_equals($this->get_contextlist_for_user($u2), [$cm2ctx]); // Admin user is the real user. $this->assert_contextlist_equals($this->get_contextlist_for_user($admin), []); $this->assert_contextlist_equals($this->get_contextlist_for_user($u3), []); $this->setAdminUser(); \core\session\manager::loginas($u3->id, $sysctx); $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); $this->assert_contextlist_equals($this->get_contextlist_for_user($admin), [$sysctx, $c1ctx]); $this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx, $c1ctx]); // By admin user masquerading u1 related to u3. $this->assert_contextlist_equals($this->get_contextlist_for_user($u1), [$cm1ctx]); $this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx, $c1ctx]); $this->assert_contextlist_equals($this->get_contextlist_for_user($admin), [$sysctx, $c1ctx]); $this->setAdminUser(); \core\session\manager::loginas($u1->id, \context_system::instance()); $e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx, 'relateduserid' => $u3->id]); $e->trigger(); $this->assert_contextlist_equals($this->get_contextlist_for_user($u1), [$sysctx, $cm1ctx, $c2ctx]); $this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx, $c1ctx, $c2ctx]); $this->assert_contextlist_equals($this->get_contextlist_for_user($admin), [$sysctx, $c1ctx, $c2ctx]); } /** * Check that user IDs are returned for a given context. */ public function test_add_userids_for_context() { $admin = \core_user::get_user(2); $u1 = $this->getDataGenerator()->create_user(); $u2 = $this->getDataGenerator()->create_user(); $u3 = $this->getDataGenerator()->create_user(); $u4 = $this->getDataGenerator()->create_user(); $c1 = $this->getDataGenerator()->create_course(); $sysctx = \context_system::instance(); $c1ctx = \context_course::instance($c1->id); $this->enable_logging(); $manager = get_log_manager(true); $userlist = new \core_privacy\local\request\userlist($sysctx, 'logstore_database'); $userids = $userlist->get_userids(); $this->assertEmpty($userids); provider::add_userids_for_context($userlist); $userids = $userlist->get_userids(); $this->assertEmpty($userids); // User one should be added (userid). $this->setUser($u1); $e = \logstore_database\event\unittest_executed::create(['context' => $sysctx]); $e->trigger(); // User two (userids) and three (relateduserid) should be added. $this->setUser($u2); $e = \logstore_database\event\unittest_executed::create(['context' => $sysctx, 'relateduserid' => $u3->id]); $e->trigger(); // The admin user should be added (realuserid). $this->setAdminUser(); \core\session\manager::loginas($u2->id, \context_system::instance()); $e = \logstore_database\event\unittest_executed::create(['context' => $sysctx]); $e->trigger(); // Set off an event in a different context. User 4 should not be returned below. $this->setUser($u4); $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); provider::add_userids_for_context($userlist); $userids = $userlist->get_userids(); $this->assertCount(4, $userids); $expectedresult = [$admin->id, $u1->id, $u2->id, $u3->id]; $this->assertEmpty(array_diff($expectedresult, $userids)); } public function test_delete_data_for_user() { global $DB; $u1 = $this->getDataGenerator()->create_user(); $u2 = $this->getDataGenerator()->create_user(); $c1 = $this->getDataGenerator()->create_course(); $c2 = $this->getDataGenerator()->create_course(); $sysctx = \context_system::instance(); $c1ctx = \context_course::instance($c1->id); $c2ctx = \context_course::instance($c2->id); $this->enable_logging(); $manager = get_log_manager(true); // User 1 is the author. $this->setUser($u1); $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); $e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx]); $e->trigger(); // User 2 is the author. $this->setUser($u2); $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); $e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx]); $e->trigger(); // Confirm data present. $this->assertTrue($DB->record_exists('logstore_standard_log', ['userid' => $u1->id, 'contextid' => $c1ctx->id])); $this->assertEquals(3, $DB->count_records('logstore_standard_log', ['userid' => $u1->id])); $this->assertEquals(2, $DB->count_records('logstore_standard_log', ['userid' => $u2->id])); // Delete all the things! provider::delete_data_for_user(new approved_contextlist($u1, 'logstore_database', [$c1ctx->id])); $this->assertFalse($DB->record_exists('logstore_standard_log', ['userid' => $u1->id, 'contextid' => $c1ctx->id])); $this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u1->id])); $this->assertEquals(2, $DB->count_records('logstore_standard_log', ['userid' => $u2->id])); } public function test_delete_data_for_all_users_in_context() { global $DB; $u1 = $this->getDataGenerator()->create_user(); $u2 = $this->getDataGenerator()->create_user(); $c1 = $this->getDataGenerator()->create_course(); $c2 = $this->getDataGenerator()->create_course(); $sysctx = \context_system::instance(); $c1ctx = \context_course::instance($c1->id); $c2ctx = \context_course::instance($c2->id); $this->enable_logging(); $manager = get_log_manager(true); // User 1 is the author. $this->setUser($u1); $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); $e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx]); $e->trigger(); // User 2 is the author. $this->setUser($u2); $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); $e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx]); $e->trigger(); // Confirm data present. $this->assertTrue($DB->record_exists('logstore_standard_log', ['contextid' => $c1ctx->id])); $this->assertEquals(3, $DB->count_records('logstore_standard_log', ['userid' => $u1->id])); $this->assertEquals(2, $DB->count_records('logstore_standard_log', ['userid' => $u2->id])); // Delete all the things! provider::delete_data_for_all_users_in_context($c1ctx); $this->assertFalse($DB->record_exists('logstore_standard_log', ['contextid' => $c1ctx->id])); $this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u1->id])); $this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u2->id])); } /** * Check that data is removed for the listed users in a given context. */ public function test_delete_data_for_userlist() { global $DB; $u1 = $this->getDataGenerator()->create_user(); $u2 = $this->getDataGenerator()->create_user(); $u3 = $this->getDataGenerator()->create_user(); $u4 = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); $sysctx = \context_system::instance(); $c1ctx = \context_course::instance($course->id); $this->enable_logging(); $manager = get_log_manager(true); $this->setUser($u1); $e = \logstore_database\event\unittest_executed::create(['context' => $sysctx]); $e->trigger(); $this->setUser($u2); $e = \logstore_database\event\unittest_executed::create(['context' => $sysctx]); $e->trigger(); $this->setUser($u3); $e = \logstore_database\event\unittest_executed::create(['context' => $sysctx]); $e->trigger(); $this->setUser($u4); $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); // Check that four records were created. $this->assertEquals(4, $DB->count_records('logstore_standard_log')); $userlist = new \core_privacy\local\request\approved_userlist($sysctx, 'logstore_database', [$u1->id, $u3->id]); provider::delete_data_for_userlist($userlist); // We should have a record for u2 and u4. $this->assertEquals(2, $DB->count_records('logstore_standard_log')); $records = $DB->get_records('logstore_standard_log', ['contextid' => $sysctx->id]); $this->assertCount(1, $records); $currentrecord = array_shift($records); $this->assertEquals($u2->id, $currentrecord->userid); } public function test_export_data_for_user() { $admin = \core_user::get_user(2); $u1 = $this->getDataGenerator()->create_user(); $u2 = $this->getDataGenerator()->create_user(); $u3 = $this->getDataGenerator()->create_user(); $u4 = $this->getDataGenerator()->create_user(); $c1 = $this->getDataGenerator()->create_course(); $cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]); $c2 = $this->getDataGenerator()->create_course(); $cm2 = $this->getDataGenerator()->create_module('url', ['course' => $c2]); $sysctx = \context_system::instance(); $c1ctx = \context_course::instance($c1->id); $c2ctx = \context_course::instance($c2->id); $cm1ctx = \context_module::instance($cm1->cmid); $cm2ctx = \context_module::instance($cm2->cmid); $path = [get_string('privacy:path:logs', 'tool_log'), get_string('pluginname', 'logstore_database')]; $this->enable_logging(); $manager = get_log_manager(true); // User 1 is the author. $this->setUser($u1); $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx, 'other' => ['i' => 0]]); $e->trigger(); // User 2 is related. $this->setUser(0); $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx, 'relateduserid' => $u2->id, 'other' => ['i' => 1]]); $e->trigger(); // Admin user masquerades u3, which is related to u4. $this->setAdminUser(); \core\session\manager::loginas($u3->id, $sysctx); $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx, 'relateduserid' => $u4->id, 'other' => ['i' => 2]]); $e->trigger(); // Confirm data present for u1. provider::export_user_data(new approved_contextlist($u1, 'logstore_database', [$c2ctx->id, $c1ctx->id])); $data = writer::with_context($c2ctx)->get_data($path); $this->assertEmpty($data); $data = writer::with_context($c1ctx)->get_data($path); $this->assertCount(1, $data->logs); $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']); $this->assertSame(0, $data->logs[0]['other']['i']); // Confirm data present for u2. writer::reset(); provider::export_user_data(new approved_contextlist($u2, 'logstore_database', [$c2ctx->id, $c1ctx->id])); $data = writer::with_context($c2ctx)->get_data($path); $this->assertEmpty($data); $data = writer::with_context($c1ctx)->get_data($path); $this->assertCount(1, $data->logs); $this->assertEquals(transform::yesno(false), $data->logs[0]['author_of_the_action_was_you']); $this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']); $this->assertSame(1, $data->logs[0]['other']['i']); // Confirm data present for u3. writer::reset(); provider::export_user_data(new approved_contextlist($u3, 'logstore_database', [$c2ctx->id, $c1ctx->id])); $data = writer::with_context($c2ctx)->get_data($path); $this->assertEmpty($data); $data = writer::with_context($c1ctx)->get_data($path); $this->assertCount(1, $data->logs); $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']); $this->assertEquals(transform::yesno(false), $data->logs[0]['related_user_was_you']); $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']); $this->assertEquals(transform::yesno(false), $data->logs[0]['masquerading_user_was_you']); $this->assertSame(2, $data->logs[0]['other']['i']); // Confirm data present for u4. writer::reset(); provider::export_user_data(new approved_contextlist($u4, 'logstore_database', [$c2ctx->id, $c1ctx->id])); $data = writer::with_context($c2ctx)->get_data($path); $this->assertEmpty($data); $data = writer::with_context($c1ctx)->get_data($path); $this->assertCount(1, $data->logs); $this->assertEquals(transform::yesno(false), $data->logs[0]['author_of_the_action_was_you']); $this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']); $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']); $this->assertEquals(transform::yesno(false), $data->logs[0]['masquerading_user_was_you']); $this->assertSame(2, $data->logs[0]['other']['i']); // Add anonymous events. $this->setUser($u1); $e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx, 'relateduserid' => $u2->id, 'anonymous' => true]); $e->trigger(); $this->setAdminUser(); \core\session\manager::loginas($u3->id, $sysctx); $e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx, 'relateduserid' => $u4->id, 'anonymous' => true]); $e->trigger(); // Confirm data present for u1. provider::export_user_data(new approved_contextlist($u1, 'logstore_database', [$c2ctx->id])); $data = writer::with_context($c2ctx)->get_data($path); $this->assertCount(1, $data->logs); $this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']); $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']); // Confirm data present for u2. writer::reset(); provider::export_user_data(new approved_contextlist($u2, 'logstore_database', [$c2ctx->id])); $data = writer::with_context($c2ctx)->get_data($path); $this->assertCount(1, $data->logs); $this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']); $this->assertArrayNotHasKey('author_of_the_action_was_you', $data->logs[0]); $this->assertArrayNotHasKey('authorid', $data->logs[0]); $this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']); // Confirm data present for u3. writer::reset(); provider::export_user_data(new approved_contextlist($u3, 'logstore_database', [$c2ctx->id])); $data = writer::with_context($c2ctx)->get_data($path); $this->assertCount(1, $data->logs); $this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']); $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']); $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']); $this->assertArrayNotHasKey('masquerading_user_was_you', $data->logs[0]); $this->assertArrayNotHasKey('masqueradinguserid', $data->logs[0]); // Confirm data present for u4. writer::reset(); provider::export_user_data(new approved_contextlist($u4, 'logstore_database', [$c2ctx->id])); $data = writer::with_context($c2ctx)->get_data($path); $this->assertCount(1, $data->logs); $this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']); $this->assertArrayNotHasKey('author_of_the_action_was_you', $data->logs[0]); $this->assertArrayNotHasKey('authorid', $data->logs[0]); $this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']); $this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']); $this->assertArrayNotHasKey('masquerading_user_was_you', $data->logs[0]); $this->assertArrayNotHasKey('masqueradinguserid', $data->logs[0]); } /** * Assert the content of a context list. * * @param contextlist $contextlist The collection. * @param array $expected List of expected contexts or IDs. * @return void */ protected function assert_contextlist_equals($contextlist, array $expected) { $expectedids = array_map(function($context) { if (is_object($context)) { return $context->id; } return $context; }, $expected); $contextids = array_map('intval', $contextlist->get_contextids()); sort($contextids); sort($expectedids); $this->assertEquals($expectedids, $contextids); } /** * Enable logging. * * @return void */ protected function enable_logging() { set_config('enabled_stores', 'logstore_database', 'tool_log'); set_config('buffersize', 0, 'logstore_database'); set_config('logguests', 1, 'logstore_database'); get_log_manager(true); } /** * Get the contextlist for a user. * * @param object $user The user. * @return contextlist */ protected function get_contextlist_for_user($user) { $contextlist = new contextlist(); provider::add_contexts_for_userid($contextlist, $user->id); return $contextlist; } } tests/store_test.php 0000644 00000035056 15152567426 0010640 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. namespace logstore_database; defined('MOODLE_INTERNAL') || die(); require_once(__DIR__ . '/fixtures/event.php'); require_once(__DIR__ . '/fixtures/store.php'); /** * External database log store tests. * * @package logstore_database * @copyright 2014 Petr Skoda {@link http://skodak.org/} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class store_test extends \advanced_testcase { /** * Tests log writing. * * @param bool $jsonformat True to test with JSON format * @dataProvider log_writing_provider */ public function test_log_writing(bool $jsonformat) { global $DB, $CFG; $this->resetAfterTest(); $this->preventResetByRollback(); // Logging waits till the transaction gets committed. // Apply JSON format system setting. set_config('jsonformat', $jsonformat ? 1 : 0, 'logstore_database'); $dbman = $DB->get_manager(); $this->assertTrue($dbman->table_exists('logstore_standard_log')); $DB->delete_records('logstore_standard_log'); $this->setAdminUser(); $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $module1 = $this->getDataGenerator()->create_module('resource', array('course' => $course1)); $course2 = $this->getDataGenerator()->create_course(); $module2 = $this->getDataGenerator()->create_module('resource', array('course' => $course2)); // Test all plugins are disabled by this command. set_config('enabled_stores', '', 'tool_log'); $manager = get_log_manager(true); $stores = $manager->get_readers(); $this->assertCount(0, $stores); // Fake the settings, we will abuse the standard plugin table here... set_config('dbdriver', $CFG->dblibrary . '/' . $CFG->dbtype, 'logstore_database'); set_config('dbhost', $CFG->dbhost, 'logstore_database'); set_config('dbuser', $CFG->dbuser, 'logstore_database'); set_config('dbpass', $CFG->dbpass, 'logstore_database'); set_config('dbname', $CFG->dbname, 'logstore_database'); set_config('dbtable', $CFG->prefix . 'logstore_standard_log', 'logstore_database'); if (!empty($CFG->dboptions['dbpersist'])) { set_config('dbpersist', 1, 'logstore_database'); } else { set_config('dbpersist', 0, 'logstore_database'); } if (!empty($CFG->dboptions['dbsocket'])) { set_config('dbsocket', $CFG->dboptions['dbsocket'], 'logstore_database'); } else { set_config('dbsocket', '', 'logstore_database'); } if (!empty($CFG->dboptions['dbport'])) { set_config('dbport', $CFG->dboptions['dbport'], 'logstore_database'); } else { set_config('dbport', '', 'logstore_database'); } if (!empty($CFG->dboptions['dbschema'])) { set_config('dbschema', $CFG->dboptions['dbschema'], 'logstore_database'); } else { set_config('dbschema', '', 'logstore_database'); } if (!empty($CFG->dboptions['dbcollation'])) { set_config('dbcollation', $CFG->dboptions['dbcollation'], 'logstore_database'); } else { set_config('dbcollation', '', 'logstore_database'); } if (!empty($CFG->dboptions['dbhandlesoptions'])) { set_config('dbhandlesoptions', $CFG->dboptions['dbhandlesoptions'], 'logstore_database'); } else { set_config('dbhandlesoptions', false, 'logstore_database'); } // Enable logging plugin. set_config('enabled_stores', 'logstore_database', 'tool_log'); set_config('buffersize', 0, 'logstore_database'); set_config('logguests', 1, 'logstore_database'); $manager = get_log_manager(true); $stores = $manager->get_readers(); $this->assertCount(1, $stores); $this->assertEquals(array('logstore_database'), array_keys($stores)); $store = $stores['logstore_database']; $this->assertInstanceOf('logstore_database\log\store', $store); $this->assertInstanceOf('tool_log\log\writer', $store); $this->assertTrue($store->is_logging()); $logs = $DB->get_records('logstore_standard_log', array(), 'id ASC'); $this->assertCount(0, $logs); $this->setCurrentTimeStart(); $this->setUser(0); $event1 = \logstore_database\event\unittest_executed::create( array('context' => \context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10))); $event1->trigger(); $logs = $DB->get_records('logstore_standard_log', array(), 'id ASC'); $this->assertCount(1, $logs); $log1 = reset($logs); unset($log1->id); if ($jsonformat) { $log1->other = json_decode($log1->other, true); } else { $log1->other = unserialize($log1->other); } $log1 = (array)$log1; $data = $event1->get_data(); $data['origin'] = 'cli'; $data['ip'] = null; $data['realuserid'] = null; $this->assertEquals($data, $log1); $this->setAdminUser(); \core\session\manager::loginas($user1->id, \context_system::instance()); $this->assertEquals(2, $DB->count_records('logstore_standard_log')); $event2 = \logstore_database\event\unittest_executed::create( array('context' => \context_module::instance($module2->cmid), 'other' => array('sample' => 6, 'xx' => 9))); $event2->trigger(); \core\session\manager::init_empty_session(); $this->assertFalse(\core\session\manager::is_loggedinas()); $logs = $DB->get_records('logstore_standard_log', array(), 'id ASC'); $this->assertCount(3, $logs); array_shift($logs); $log2 = array_shift($logs); $this->assertSame('\core\event\user_loggedinas', $log2->eventname); $log3 = array_shift($logs); unset($log3->id); if ($jsonformat) { $log3->other = json_decode($log3->other, true); } else { $log3->other = unserialize($log3->other); } $log3 = (array)$log3; $data = $event2->get_data(); $data['origin'] = 'cli'; $data['ip'] = null; $data['realuserid'] = 2; $this->assertEquals($data, $log3); // Test reading. $this->assertSame(3, $store->get_events_select_count('', array())); $events = $store->get_events_select('', array(), 'timecreated ASC', 0, 0); // Is actually sorted by "timecreated ASC, id ASC". $this->assertCount(3, $events); $resev1 = array_shift($events); array_shift($events); $resev2 = array_shift($events); $this->assertEquals($event1->get_data(), $resev1->get_data()); $this->assertEquals($event2->get_data(), $resev2->get_data()); // Test buffering. set_config('buffersize', 3, 'logstore_database'); $manager = get_log_manager(true); $stores = $manager->get_readers(); /** @var \logstore_database\log\store $store */ $store = $stores['logstore_database']; $DB->delete_records('logstore_standard_log'); \logstore_database\event\unittest_executed::create( array('context' => \context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger(); $this->assertEquals(0, $DB->count_records('logstore_standard_log')); \logstore_database\event\unittest_executed::create( array('context' => \context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger(); $this->assertEquals(0, $DB->count_records('logstore_standard_log')); $store->flush(); $this->assertEquals(2, $DB->count_records('logstore_standard_log')); \logstore_database\event\unittest_executed::create( array('context' => \context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger(); $this->assertEquals(2, $DB->count_records('logstore_standard_log')); \logstore_database\event\unittest_executed::create( array('context' => \context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger(); $this->assertEquals(2, $DB->count_records('logstore_standard_log')); \logstore_database\event\unittest_executed::create( array('context' => \context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger(); $this->assertEquals(5, $DB->count_records('logstore_standard_log')); \logstore_database\event\unittest_executed::create( array('context' => \context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger(); $this->assertEquals(5, $DB->count_records('logstore_standard_log')); \logstore_database\event\unittest_executed::create( array('context' => \context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger(); $this->assertEquals(5, $DB->count_records('logstore_standard_log')); \logstore_database\event\unittest_executed::create( array('context' => \context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger(); $this->assertEquals(8, $DB->count_records('logstore_standard_log')); // Test guest logging setting. set_config('logguests', 0, 'logstore_database'); set_config('buffersize', 0, 'logstore_database'); get_log_manager(true); $DB->delete_records('logstore_standard_log'); get_log_manager(true); $this->setUser(null); \logstore_database\event\unittest_executed::create( array('context' => \context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger(); $this->assertEquals(0, $DB->count_records('logstore_standard_log')); $this->setGuestUser(); \logstore_database\event\unittest_executed::create( array('context' => \context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger(); $this->assertEquals(0, $DB->count_records('logstore_standard_log')); $this->setUser($user1); \logstore_database\event\unittest_executed::create( array('context' => \context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger(); $this->assertEquals(1, $DB->count_records('logstore_standard_log')); $this->setUser($user2); \logstore_database\event\unittest_executed::create( array('context' => \context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger(); $this->assertEquals(2, $DB->count_records('logstore_standard_log')); set_config('enabled_stores', '', 'tool_log'); get_log_manager(true); } /** * Returns different JSON format settings so the test can be run with JSON format either on or * off. * * @return bool[] Array of true/false */ public static function log_writing_provider(): array { return [ [false], [true] ]; } /** * Test method is_event_ignored. */ public function test_is_event_ignored() { $this->resetAfterTest(); // Test guest filtering. set_config('logguests', 0, 'logstore_database'); $this->setGuestUser(); $event = \logstore_database\event\unittest_executed::create( array('context' => \context_system::instance(), 'other' => array('sample' => 5, 'xx' => 10))); $logmanager = get_log_manager(); $store = new \logstore_database\test\store($logmanager); $this->assertTrue($store->is_event_ignored($event)); set_config('logguests', 1, 'logstore_database'); $store = new \logstore_database\test\store($logmanager); // Reload. $this->assertFalse($store->is_event_ignored($event)); // Test action/level filtering. set_config('includelevels', '', 'logstore_database'); set_config('includeactions', '', 'logstore_database'); $store = new \logstore_database\test\store($logmanager); // Reload. $this->assertTrue($store->is_event_ignored($event)); set_config('includelevels', '0,1', 'logstore_database'); $store = new \logstore_database\test\store($logmanager); // Reload. $this->assertTrue($store->is_event_ignored($event)); set_config('includelevels', '0,1,2', 'logstore_database'); $store = new \logstore_database\test\store($logmanager); // Reload. $this->assertFalse($store->is_event_ignored($event)); set_config('includelevels', '', 'logstore_database'); set_config('includeactions', 'c,r,d', 'logstore_database'); $store = new \logstore_database\test\store($logmanager); // Reload. $this->assertTrue($store->is_event_ignored($event)); set_config('includeactions', 'c,r,u,d', 'logstore_database'); $store = new \logstore_database\test\store($logmanager); // Reload. $this->assertFalse($store->is_event_ignored($event)); } /** * 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'); // Make sure all supported reports are installed. $expectedreports = array_intersect_key([ 'log' => 'report_log', 'loglive' => 'report_loglive', ], $allreports); $supportedreports = $logmanager->get_supported_reports('logstore_database'); foreach ($expectedreports as $expectedreport) { $this->assertArrayHasKey($expectedreport, $supportedreports); } } } backup/moodle2/backup_logstore_database_subplugin.class.php 0000644 00000005036 15152567426 0020307 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Backup implementation for the (tool_log) logstore_database subplugin. * * @package logstore_database * @category backup * @copyright 2015 Mark Nelson <markn@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); require_once('backup_logstore_database_nested_element.php'); class backup_logstore_database_subplugin extends backup_tool_log_logstore_subplugin { /** * Returns the subplugin structure to attach to the 'logstore' XML element. * * @return backup_subplugin_element the subplugin structure to be attached. */ protected function define_logstore_subplugin_structure() { $subplugin = $this->get_subplugin_element(); $subpluginwrapper = new backup_nested_element($this->get_recommended_name()); // Create the custom (base64 encoded, xml safe) 'other' final element. $otherelement = new base64_encode_final_element('other'); $subpluginlog = new backup_logstore_database_nested_element('logstore_database_log', array('id'), array( 'eventname', 'component', 'action', 'target', 'objecttable', 'objectid', 'crud', 'edulevel', 'contextid', 'userid', 'relateduserid', 'anonymous', $otherelement, 'timecreated', 'ip', 'realuserid')); $subplugin->add_child($subpluginwrapper); $subpluginwrapper->add_child($subpluginlog); // Get the details for the external database. $manager = new \tool_log\log\manager(); $store = new \logstore_database\log\store($manager); $extdb = $store->get_extdb(); if (!$extdb) { return false; } $subpluginlog->set_source_db($extdb); $subpluginlog->set_source_table($store->get_config_value('dbtable'), array('contextid' => backup::VAR_CONTEXTID)); return $subplugin; } } backup/moodle2/restore_logstore_database_subplugin.class.php 0000644 00000007051 15152567426 0020524 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Restore implementation for the (tool_log) logstore_database subplugin. * * @package logstore_database * @category backup * @copyright 2015 Mark Nelson <markn@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); class restore_logstore_database_subplugin extends restore_tool_log_logstore_subplugin { /** * @var moodle_database the external database. */ private static $extdb = null; /** * @var string the external database table name. */ private static $extdbtablename = null; /** * The constructor for this logstore. * * @param string $subplugintype the subplugin type. * @param string $subpluginname the subplugin name. * @param restore_structure_step $step. */ public function __construct($subplugintype, $subpluginname, $step) { // Check that the logstore is enabled before setting variables. $enabledlogstores = explode(',', get_config('tool_log', 'enabled_stores')); if (in_array('logstore_database', $enabledlogstores)) { $manager = new \tool_log\log\manager(); $store = new \logstore_database\log\store($manager); self::$extdb = $store->get_extdb(); self::$extdbtablename = $store->get_config_value('dbtable'); } parent::__construct($subplugintype, $subpluginname, $step); } /** * Returns the subplugin structure to attach to the 'logstore' XML element. * * @return restore_path_element[] array of elements to be processed on restore. */ protected function define_logstore_subplugin_structure() { // If the logstore is not enabled we don't add structures for it. $enabledlogstores = explode(',', get_config('tool_log', 'enabled_stores')); if (!in_array('logstore_database', $enabledlogstores)) { return array(); // The logstore is not enabled, nothing to restore. } $paths = array(); $elename = $this->get_namefor('log'); $elepath = $this->get_pathfor('/logstore_database_log'); $paths[] = new restore_path_element($elename, $elepath); return $paths; } /** * Process logstore_database_log entries. * * This method proceeds to read, complete, remap and, finally, * discard or save every log entry. * * @param array() $data log entry. * @return null if we are not restoring the log. */ public function process_logstore_database_log($data) { // Do not bother processing if we can not add it to a database. if (!self::$extdb || !self::$extdbtablename) { return; } $data = $this->process_log($data, get_config('logstore_database', 'jsonformat')); if ($data) { self::$extdb->insert_record(self::$extdbtablename, $data); } } } backup/moodle2/backup_logstore_database_nested_element.php 0000644 00000006470 15152567426 0020171 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Backup implementation for the (tool_log) logstore_database nested element. * * @package logstore_database * @category backup * @copyright 2015 Damyon Wiese <damyon@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Custom subclass of backup_nested_element that iterates over an external DB connection. * * @package logstore_database * @category backup * @copyright 2015 Damyon Wiese <damyon@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class backup_logstore_database_nested_element extends backup_nested_element { /** * @var \moodle_database $sourcedb */ protected $sourcedb; /** * Constructor - instantiates one backup_nested_element, specifying its basic info. * * @param string $name name of the element * @param array $attributes attributes this element will handle (optional, defaults to null) * @param array $finalelements this element will handle (optional, defaults to null) */ public function __construct($name, $attributes = null, $finalelements = null) { global $DB; parent::__construct($name, $attributes, $finalelements); $this->sourcedb = $DB; } /** * For sql or table datasources, this will iterate over the "external" DB connection * stored in this class instead of the default $DB. All other cases use the parent default. * @param object $processor the processor */ protected function get_iterator($processor) { if ($this->get_source_table() !== null) { // It's one table, return recordset iterator. return $this->get_source_db()->get_recordset( $this->get_source_table(), backup_structure_dbops::convert_params_to_values($this->procparams, $processor), $this->get_source_table_sortby() ); } else if ($this->get_source_sql() !== null) { // It's one sql, return recordset iterator. return $this->get_source_db()->get_recordset_sql( $this->get_source_sql(), backup_structure_dbops::convert_params_to_values($this->procparams, $processor) ); } return parent::get_iterator($processor); } /** * Set the database we want to use. * * @param \moodle_database $db */ public function set_source_db($db) { $this->sourcedb = $db; } /** * Get the database we want to use. * * @return \moodle_database $db */ public function get_source_db() { return $this->sourcedb; } } settings.php 0000644 00000011114 15152567426 0007130 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * External database log store settings. * * @package logstore_database * @copyright 2013 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); if ($hassiteconfig) { $testurl = new moodle_url('/admin/tool/log/store/database/test_settings.php', array('sesskey' => sesskey())); $test = new admin_externalpage('logstoredbtestsettings', get_string('testsettings', 'logstore_database'), $testurl, 'moodle/site:config', true); $ADMIN->add('logging', $test); $drivers = \logstore_database\helper::get_drivers(); // Database settings. $link = html_writer::link($testurl, get_string('testsettings', 'logstore_database'), array('target' => '_blank')); $settings->add(new admin_setting_heading('dbsettings', get_string('databasesettings', 'logstore_database'), get_string('databasesettings_help', 'logstore_database', $link))); $settings->add(new admin_setting_configselect('logstore_database/dbdriver', get_string('databasetypehead', 'install'), '', '', $drivers)); $settings->add(new admin_setting_configtext('logstore_database/dbhost', get_string('databasehost', 'install'), '', '')); $settings->add(new admin_setting_configtext('logstore_database/dbuser', get_string('databaseuser', 'install'), '', '')); $settings->add(new admin_setting_configpasswordunmask('logstore_database/dbpass', get_string('databasepass', 'install'), '', '')); $settings->add(new admin_setting_configtext('logstore_database/dbname', get_string('databasename', 'install'), '', '')); $settings->add(new admin_setting_configtext('logstore_database/dbtable', get_string('databasetable', 'logstore_database'), get_string('databasetable_help', 'logstore_database'), '')); $settings->add(new admin_setting_configcheckbox('logstore_database/dbpersist', get_string('databasepersist', 'logstore_database'), '', '0')); $settings->add(new admin_setting_configtext('logstore_database/dbsocket', get_string('databasesocket', 'install'), '', '')); $settings->add(new admin_setting_configtext('logstore_database/dbport', get_string('databaseport', 'install'), '', '')); $settings->add(new admin_setting_configtext('logstore_database/dbschema', get_string('databaseschema', 'logstore_database'), '', '')); $settings->add(new admin_setting_configtext('logstore_database/dbcollation', get_string('databasecollation', 'logstore_database'), '', '')); $settings->add(new admin_setting_configcheckbox('logstore_database/dbhandlesoptions', get_string('databasehandlesoptions', 'logstore_database'), get_string('databasehandlesoptions_help', 'logstore_database'), '0')); $settings->add(new admin_setting_configtext('logstore_database/buffersize', get_string('buffersize', 'logstore_database'), get_string('buffersize_help', 'logstore_database'), 50)); $settings->add(new admin_setting_configcheckbox('logstore_database/jsonformat', new lang_string('jsonformat', 'logstore_database'), new lang_string('jsonformat_desc', 'logstore_database'), 1)); // Filters. $settings->add(new admin_setting_heading('filters', get_string('filters', 'logstore_database'), get_string('filters_help', 'logstore_database'))); $settings->add(new admin_setting_configcheckbox('logstore_database/logguests', get_string('logguests', 'logstore_database'), '', '0')); $levels = \logstore_database\helper::get_level_options(); $settings->add(new admin_setting_configmulticheckbox('logstore_database/includelevels', get_string('includelevels', 'logstore_database'), '', $levels, $levels)); $actions = \logstore_database\helper::get_action_options(); $settings->add(new admin_setting_configmulticheckbox('logstore_database/includeactions', get_string('includeactions', 'logstore_database'), '', $actions, $actions)); } upgrade.txt 0000644 00000001271 15152567426 0006752 0 ustar 00 This files describes API changes in the logstore_database code. === 3.4 === * PostgreSQL connections now use advanced options to reduce connection overhead. These options are not compatible with some connection poolers. The dbhandlesoptions parameter has been added to allow the database to configure the required defaults. The parameters that are required in the database are; ALTER DATABASE moodle SET client_encoding = UTF8; ALTER DATABASE moodle SET standard_conforming_strings = on; ALTER DATABASE moodle SET search_path = 'moodle,public'; -- Optional, if you wish to use a custom schema. You can set these options against the database or the moodle user who connects. version.php 0000644 00000002225 15152567426 0006760 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * External database log store. * * @package logstore_database * @copyright 2013 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $plugin->version = 2022112800; // The current plugin version (Date: YYYYMMDDXX). $plugin->requires = 2022111800; // Requires this Moodle version. $plugin->component = 'logstore_database'; // Full name of the plugin (used for diagnostics). classes/privacy/provider.php 0000644 00000012245 15152567426 0012242 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Data provider. * * @package logstore_database * @copyright 2018 Frédéric Massart * @author Frédéric Massart <fred@branchup.tech> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace logstore_database\privacy; defined('MOODLE_INTERNAL') || die(); use context; use core_privacy\local\metadata\collection; use core_privacy\local\request\contextlist; /** * Data provider class. * * @package logstore_database * @copyright 2018 Frédéric Massart * @author Frédéric Massart <fred@branchup.tech> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\provider, \tool_log\local\privacy\logstore_provider, \tool_log\local\privacy\logstore_userlist_provider { use \tool_log\local\privacy\moodle_database_export_and_delete; /** * Returns metadata. * * @param collection $collection The initialised collection to add items to. * @return collection A listing of user data stored through this system. */ public static function get_metadata(collection $collection) : collection { $collection->add_external_location_link('log', [ 'eventname' => 'privacy:metadata:log:eventname', 'userid' => 'privacy:metadata:log:userid', 'relateduserid' => 'privacy:metadata:log:relateduserid', 'anonymous' => 'privacy:metadata:log:anonymous', 'other' => 'privacy:metadata:log:other', 'timecreated' => 'privacy:metadata:log:timecreated', 'origin' => 'privacy:metadata:log:origin', 'ip' => 'privacy:metadata:log:ip', 'realuserid' => 'privacy:metadata:log:realuserid', ], 'privacy:metadata:log'); return $collection; } /** * Add contexts that contain user information for the specified user. * * @param contextlist $contextlist The contextlist to add the contexts to. * @param int $userid The user to find the contexts for. * @return void */ public static function add_contexts_for_userid(contextlist $contextlist, $userid) { list($db, $table) = static::get_database_and_table(); if (!$db || !$table) { return; } $sql = 'userid = :userid1 OR relateduserid = :userid2 OR realuserid = :userid3'; $params = ['userid1' => $userid, 'userid2' => $userid, 'userid3' => $userid]; $contextids = $db->get_fieldset_select($table, 'DISTINCT contextid', $sql, $params); if (empty($contextids)) { return; } $sql = implode(' UNION ', array_map(function($id) use ($db) { return 'SELECT ' . $id . $db->sql_null_from_clause(); }, $contextids)); $contextlist->add_from_sql($sql, []); } /** * Add user IDs that contain user information for the specified context. * * @param \core_privacy\local\request\userlist $userlist The userlist to add the users to. * @return void */ public static function add_userids_for_context(\core_privacy\local\request\userlist $userlist) { list($db, $table) = static::get_database_and_table(); if (!$db || !$table) { return; } $userids = []; $records = $db->get_records($table, ['contextid' => $userlist->get_context()->id], '', 'id, userid, relateduserid, realuserid'); if (empty($records)) { return; } foreach ($records as $record) { $userids[] = $record->userid; if (!empty($record->relateduserid)) { $userids[] = $record->relateduserid; } if (!empty($record->realuserid)) { $userids[] = $record->realuserid; } } $userids = array_unique($userids); $userlist->add_users($userids); } /** * Get the database object. * * @return array Containing moodle_database, string, or null values. */ protected static function get_database_and_table() { $manager = get_log_manager(); $store = new \logstore_database\log\store($manager); $db = $store->get_extdb(); return $db ? [$db, $store->get_config_value('dbtable')] : [null, null]; } /** * Get the path to export the logs to. * * @return array */ protected static function get_export_subcontext() { return [get_string('privacy:path:logs', 'tool_log'), get_string('pluginname', 'logstore_database')]; } } classes/log/store.php 0000644 00000024607 15152567426 0010655 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * External database store. * * @package logstore_database * @copyright 2013 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace logstore_database\log; defined('MOODLE_INTERNAL') || die(); class store implements \tool_log\log\writer, \core\log\sql_reader { use \tool_log\helper\store, \tool_log\helper\reader, \tool_log\helper\buffered_writer { dispose as helper_dispose; } /** @var \moodle_database $extdb */ protected $extdb; /** @var bool $logguests true if logging guest access */ protected $logguests; /** @var array $includelevels An array of education levels to include */ protected $includelevels = array(); /** @var array $includeactions An array of actions types to include */ protected $includeactions = array(); /** * Construct * * @param \tool_log\log\manager $manager */ public function __construct(\tool_log\log\manager $manager) { $this->helper_setup($manager); $this->buffersize = $this->get_config('buffersize', 50); $this->logguests = $this->get_config('logguests', 1); $actions = $this->get_config('includeactions', ''); $levels = $this->get_config('includelevels', ''); $this->includeactions = $actions === '' ? array() : explode(',', $actions); $this->includelevels = $levels === '' ? array() : explode(',', $levels); // JSON writing defaults to false (table format compatibility with older versions). // Note: This variable is defined in the buffered_writer trait. $this->jsonformat = (bool)$this->get_config('jsonformat', false); } /** * Setup the Database. * * @return bool */ protected function init() { if (isset($this->extdb)) { return !empty($this->extdb); } $dbdriver = $this->get_config('dbdriver'); if (empty($dbdriver)) { $this->extdb = false; return false; } list($dblibrary, $dbtype) = explode('/', $dbdriver); if (!$db = \moodle_database::get_driver_instance($dbtype, $dblibrary, true)) { debugging("Unknown driver $dblibrary/$dbtype", DEBUG_DEVELOPER); $this->extdb = false; return false; } $dboptions = array(); $dboptions['dbpersist'] = $this->get_config('dbpersist', '0'); $dboptions['dbsocket'] = $this->get_config('dbsocket', ''); $dboptions['dbport'] = $this->get_config('dbport', ''); $dboptions['dbschema'] = $this->get_config('dbschema', ''); $dboptions['dbcollation'] = $this->get_config('dbcollation', ''); $dboptions['dbhandlesoptions'] = $this->get_config('dbhandlesoptions', false); try { $db->connect($this->get_config('dbhost'), $this->get_config('dbuser'), $this->get_config('dbpass'), $this->get_config('dbname'), false, $dboptions); $tables = $db->get_tables(); if (!in_array($this->get_config('dbtable'), $tables)) { debugging('Cannot find the specified table', DEBUG_DEVELOPER); $this->extdb = false; return false; } } catch (\moodle_exception $e) { debugging('Cannot connect to external database: ' . $e->getMessage(), DEBUG_DEVELOPER); $this->extdb = false; return false; } $this->extdb = $db; return true; } /** * Should the event be ignored (== not logged)? * @param \core\event\base $event * @return bool */ protected function is_event_ignored(\core\event\base $event) { if (!in_array($event->crud, $this->includeactions) && !in_array($event->edulevel, $this->includelevels) ) { // Ignore event if the store settings do not want to store it. return true; } if ((!CLI_SCRIPT or PHPUNIT_TEST) and !$this->logguests) { // Always log inside CLI scripts because we do not login there. if (!isloggedin() or isguestuser()) { return true; } } return false; } /** * Insert events in bulk to the database. * * @param array $evententries raw event data */ protected function insert_event_entries($evententries) { if (!$this->init()) { return; } if (!$dbtable = $this->get_config('dbtable')) { return; } try { $this->extdb->insert_records($dbtable, $evententries); } catch (\moodle_exception $e) { debugging('Cannot write to external database: ' . $e->getMessage(), DEBUG_DEVELOPER); } } /** * Get an array of events based on the passed on params. * * @param string $selectwhere select conditions. * @param array $params params. * @param string $sort sortorder. * @param int $limitfrom limit constraints. * @param int $limitnum limit constraints. * * @return array|\core\event\base[] array of events. */ public function get_events_select($selectwhere, array $params, $sort, $limitfrom, $limitnum) { if (!$this->init()) { return array(); } if (!$dbtable = $this->get_config('dbtable')) { return array(); } $sort = self::tweak_sort_by_id($sort); $events = array(); $records = $this->extdb->get_records_select($dbtable, $selectwhere, $params, $sort, '*', $limitfrom, $limitnum); foreach ($records as $data) { if ($event = $this->get_log_event($data)) { $events[$data->id] = $event; } } return $events; } /** * Fetch records using given criteria returning a Traversable object. * * Note that the traversable object contains a moodle_recordset, so * remember that is important that you call close() once you finish * using it. * * @param string $selectwhere * @param array $params * @param string $sort * @param int $limitfrom * @param int $limitnum * @return \core\dml\recordset_walk|\core\event\base[] */ public function get_events_select_iterator($selectwhere, array $params, $sort, $limitfrom, $limitnum) { if (!$this->init()) { return array(); } if (!$dbtable = $this->get_config('dbtable')) { return array(); } $sort = self::tweak_sort_by_id($sort); $recordset = $this->extdb->get_recordset_select($dbtable, $selectwhere, $params, $sort, '*', $limitfrom, $limitnum); return new \core\dml\recordset_walk($recordset, array($this, 'get_log_event')); } /** * Returns an event from the log data. * * @param stdClass $data Log data * @return \core\event\base */ public function get_log_event($data) { $extra = array('origin' => $data->origin, 'ip' => $data->ip, 'realuserid' => $data->realuserid); $data = (array)$data; $id = $data['id']; $data['other'] = self::decode_other($data['other']); if ($data['other'] === false) { $data['other'] = array(); } unset($data['origin']); unset($data['ip']); unset($data['realuserid']); unset($data['id']); if (!$event = \core\event\base::restore($data, $extra)) { return null; } return $event; } /** * Get number of events present for the given select clause. * * @param string $selectwhere select conditions. * @param array $params params. * * @return int Number of events available for the given conditions */ public function get_events_select_count($selectwhere, array $params) { if (!$this->init()) { return 0; } if (!$dbtable = $this->get_config('dbtable')) { return 0; } return $this->extdb->count_records_select($dbtable, $selectwhere, $params); } /** * Get whether events are present for the given select clause. * * @param string $selectwhere select conditions. * @param array $params params. * * @return bool Whether events available for the given conditions */ public function get_events_select_exists(string $selectwhere, array $params): bool { if (!$this->init()) { return false; } if (!$dbtable = $this->get_config('dbtable')) { return false; } return $this->extdb->record_exists_select($dbtable, $selectwhere, $params); } /** * Get a config value for the store. * * @param string $name Config name * @param mixed $default default value * @return mixed config value if set, else the default value. */ public function get_config_value($name, $default = null) { return $this->get_config($name, $default); } /** * Get the external database object. * * @return \moodle_database $extdb */ public function get_extdb() { if (!$this->init()) { return false; } return $this->extdb; } /** * Are the new events appearing in the reader? * * @return bool true means new log events are being added, false means no new data will be added */ public function is_logging() { if (!$this->init()) { return false; } return true; } /** * Dispose off database connection after pushing any buffered events to the database. */ public function dispose() { $this->helper_dispose(); if ($this->extdb) { $this->extdb->dispose(); } $this->extdb = null; } } classes/helper.php 0000644 00000005263 15152567426 0010214 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Helper class locally used. * * @package logstore_database * @copyright 2014 onwards Ankit Agarwal * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace logstore_database; defined('MOODLE_INTERNAL') || die(); /** * Helper class locally used. * * @copyright 2014 onwards Ankit Agarwal * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class helper { /** * Returns list of fully working database drivers present in system. * @return array */ public static function get_drivers() { return array( '' => get_string('choosedots'), 'native/mysqli' => \moodle_database::get_driver_instance('mysqli', 'native')->get_name(), 'native/mariadb' => \moodle_database::get_driver_instance('mariadb', 'native')->get_name(), 'native/pgsql' => \moodle_database::get_driver_instance('pgsql', 'native')->get_name(), 'native/oci' => \moodle_database::get_driver_instance('oci', 'native')->get_name(), 'native/sqlsrv' => \moodle_database::get_driver_instance('sqlsrv', 'native')->get_name() ); } /** * Get a list of edu levels. * * @return array */ public static function get_level_options() { return array( \core\event\base::LEVEL_TEACHING => get_string('teaching', 'logstore_database'), \core\event\base::LEVEL_PARTICIPATING => get_string('participating', 'logstore_database'), \core\event\base::LEVEL_OTHER => get_string('other', 'logstore_database'), ); } /** * Get a list of database actions. * * @return array */ public static function get_action_options() { return array( 'c' => get_string('create', 'logstore_database'), 'r' => get_string('read', 'logstore_database'), 'u' => get_string('update', 'logstore_database'), 'd' => get_string('delete') ); } } lang/en/logstore_database.php 0000644 00000007046 15152567426 0012306 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Log store lang strings. * * @package logstore_database * @copyright 2013 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $string['buffersize'] = 'Buffer size'; $string['buffersize_help'] = 'Number of log entries inserted in one batch database operation, which improves performance.'; $string['conectexception'] = 'Cannot connect to the database.'; $string['create'] = 'Create'; $string['databasesettings'] = 'Database settings'; $string['databasesettings_help'] = 'Connection details for the external log database: {$a}'; $string['databasepersist'] = 'Persistent database connections'; $string['databaseschema'] = 'Database schema'; $string['databasecollation'] = 'Database collation'; $string['databasehandlesoptions'] = 'Database handles options'; $string['databasehandlesoptions_help'] = 'Does the remote database handle its own options.'; $string['databasetable'] = 'Database table'; $string['databasetable_help'] = 'Name of the table where logs will be stored. This table should have a structure identical to the one used by logstore_standard (mdl_logstore_standard_log).'; $string['includeactions'] = 'Include actions of these types'; $string['includelevels'] = 'Include actions with these educational levels'; $string['filters'] = 'Filter logs'; $string['filters_help'] = 'Enable filters that exclude some actions from being logged.'; $string['jsonformat'] = 'JSON format'; $string['jsonformat_desc'] = 'Use standard JSON format instead of PHP serialised data in the \'other\' database field.'; $string['logguests'] = 'Log guest actions'; $string['other'] = 'Other'; $string['participating'] = 'Participating'; $string['pluginname'] = 'External database log'; $string['pluginname_desc'] = 'A log plugin that stores log entries in an external database table.'; $string['privacy:metadata:log'] = 'A collection of past events'; $string['privacy:metadata:log:anonymous'] = 'Whether the event was flagged as anonymous'; $string['privacy:metadata:log:eventname'] = 'The event name'; $string['privacy:metadata:log:ip'] = 'The IP address used at the time of the event'; $string['privacy:metadata:log:origin'] = 'The origin of the event'; $string['privacy:metadata:log:other'] = 'Additional information about the event'; $string['privacy:metadata:log:realuserid'] = 'The ID of the real user behind the event, when masquerading a user.'; $string['privacy:metadata:log:relateduserid'] = 'The ID of a user related to this event'; $string['privacy:metadata:log:timecreated'] = 'The time when the event occurred'; $string['privacy:metadata:log:userid'] = 'The ID of the user who triggered this event'; $string['read'] = 'Read'; $string['tablenotfound'] = 'Specified table was not found'; $string['teaching'] = 'Teaching'; $string['testsettings'] = 'Test connection'; $string['testingsettings'] = 'Testing database settings...'; $string['update'] = 'Update';
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | ���֧ߧ֧�ѧ�ڧ� ����ѧߧڧ��: 0 |
proxy
|
phpinfo
|
���ѧ����ۧܧ�