���ѧۧݧ�ӧ�� �ާ֧ߧ֧էا֧� - ���֧էѧܧ�ڧ��ӧѧ�� - /home3/cpr76684/public_html/standard.tar
���ѧ٧ѧ�
db/upgrade.php 0000644 00000004677 15152567405 0007321 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/>. /** * Standard log store upgrade. * * @package logstore_standard * @copyright 2014 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); function xmldb_logstore_standard_upgrade($oldversion) { global $CFG, $DB; require_once($CFG->libdir.'/db/upgradelib.php'); // Core Upgrade-related functions. $dbman = $DB->get_manager(); // Loads ddl manager and xmldb classes. // 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. if ($oldversion < 2022053000) { // Define index relateduserid (not unique) to be added to logstore_standard_log. $table = new xmldb_table('logstore_standard_log'); // Launch add key userid. $key = new xmldb_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']); $dbman->add_key($table, $key); // Launch add key courseid. $key = new xmldb_key('courseid', XMLDB_KEY_FOREIGN, ['courseid'], 'course', ['id']); $dbman->add_key($table, $key); // Launch add key realuserid. $key = new xmldb_key('realuserid', XMLDB_KEY_FOREIGN, ['realuserid'], 'user', ['id']); $dbman->add_key($table, $key); // Launch add key relateduserid. $key = new xmldb_key('relateduserid', XMLDB_KEY_FOREIGN, ['relateduserid'], 'user', ['id']); $dbman->add_key($table, $key); // Standard savepoint reached. upgrade_plugin_savepoint(true, 2022053000, 'logstore', 'standard'); } // Automatically generated Moodle v4.1.0 release upgrade line. // Put any upgrade step following this. return true; } db/tasks.php 0000644 00000002244 15152567405 0007003 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/>. /** * Standard log reader/writer cron task. * * @package logstore_standard * @copyright 2014 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $tasks = array( array( 'classname' => '\logstore_standard\task\cleanup_task', 'blocking' => 0, 'minute' => 'R', 'hour' => '4', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), ); db/install.xml 0000644 00000006455 15152567405 0007345 0 ustar 00 <?xml version="1.0" encoding="UTF-8" ?> <XMLDB PATH="admin/tool/log/store/standard/db" VERSION="20220616" COMMENT="XMLDB file for Moodle admin/tool/log/store/standard" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/xmldb/xmldb.xsd" > <TABLES> <TABLE NAME="logstore_standard_log" COMMENT="Standard log table"> <FIELDS> <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/> <FIELD NAME="eventname" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="action" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="target" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="objecttable" TYPE="char" LENGTH="50" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="objectid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="crud" TYPE="char" LENGTH="1" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="edulevel" TYPE="int" LENGTH="1" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="contextlevel" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="contextinstanceid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="relateduserid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="anonymous" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Was this event anonymous at the time of triggering?"/> <FIELD NAME="other" TYPE="text" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="origin" TYPE="char" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="cli, cron, ws, etc."/> <FIELD NAME="ip" TYPE="char" LENGTH="45" NOTNULL="false" SEQUENCE="false" COMMENT="IP address"/> <FIELD NAME="realuserid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="real user id when logged-in-as"/> </FIELDS> <KEYS> <KEY NAME="primary" TYPE="primary" FIELDS="id"/> <KEY NAME="contextid" TYPE="foreign" FIELDS="contextid" REFTABLE="context" REFFIELDS="id"/> <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/> <KEY NAME="courseid" TYPE="foreign" FIELDS="courseid" REFTABLE="course" REFFIELDS="id"/> <KEY NAME="realuserid" TYPE="foreign" FIELDS="realuserid" REFTABLE="user" REFFIELDS="id"/> <KEY NAME="relateduserid" TYPE="foreign" FIELDS="relateduserid" REFTABLE="user" REFFIELDS="id"/> </KEYS> <INDEXES> <INDEX NAME="timecreated" UNIQUE="false" FIELDS="timecreated"/> <INDEX NAME="course-time" UNIQUE="false" FIELDS="courseid, anonymous, timecreated"/> <INDEX NAME="user-module" UNIQUE="false" FIELDS="userid, contextlevel, contextinstanceid, crud, edulevel, timecreated"/> </INDEXES> </TABLE> </TABLES> </XMLDB> tests/fixtures/event.php 0000644 00000003304 15152567405 0011423 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 standard log storage testing. * * @package logstore_standard * @copyright 2014 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace logstore_standard\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'])); } /** * The 'other' fields for this event do not need to mapped during backup and restore as they * only contain test values, not IDs for anything on the course. * * @return array Empty array */ public static function get_other_mapping(): array { return []; } } tests/fixtures/restore_hack.php 0000644 00000002170 15152567405 0012753 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 controller hackery. * * @package tool_log * @copyright 2014 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); class logstore_standard_restore extends restore_controller { public static function hack_executing($state) { self::$executing = $state; } } tests/privacy/provider_test.php 0000644 00000053035 15152567405 0013005 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_standard * @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_standard\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_standard\privacy\provider; require_once(__DIR__ . '/../fixtures/event.php'); /** * Data provider testcase class. * * @package logstore_standard * @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 { $this->resetAfterTest(); $this->preventResetByRollback(); // Logging waits till the transaction gets committed. } 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_standard\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_standard\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_standard\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_standard\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]); } 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_standard_log'); $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_standard\event\unittest_executed::create(['context' => $sysctx]); $e->trigger(); // User two (userids) and three (relateduserid) should be added. $this->setUser($u2); $e = \logstore_standard\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_standard\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_standard\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_standard\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); $e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); $e = \logstore_standard\event\unittest_executed::create(['context' => $c2ctx]); $e->trigger(); // User 2 is the author. $this->setUser($u2); $e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); $e = \logstore_standard\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_standard', [$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_standard\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); $e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); $e = \logstore_standard\event\unittest_executed::create(['context' => $c2ctx]); $e->trigger(); // User 2 is the author. $this->setUser($u2); $e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]); $e->trigger(); $e = \logstore_standard\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])); } 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_standard\event\unittest_executed::create(['context' => $sysctx]); $e->trigger(); $this->setUser($u2); $e = \logstore_standard\event\unittest_executed::create(['context' => $sysctx]); $e->trigger(); $this->setUser($u3); $e = \logstore_standard\event\unittest_executed::create(['context' => $sysctx]); $e->trigger(); $this->setUser($u4); $e = \logstore_standard\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_standard_log', [$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_standard')]; $this->enable_logging(); $manager = get_log_manager(true); // User 1 is the author. $this->setUser($u1); $e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx, 'other' => ['i' => 0]]); $e->trigger(); // User 2 is related. $this->setUser(0); $e = \logstore_standard\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_standard\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_standard', [$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_standard', [$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_standard', [$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_standard', [$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_standard\event\unittest_executed::create(['context' => $c2ctx, 'relateduserid' => $u2->id, 'anonymous' => true]); $e->trigger(); $this->setAdminUser(); \core\session\manager::loginas($u3->id, $sysctx); $e = \logstore_standard\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_standard', [$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_standard', [$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_standard', [$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_standard', [$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_standard', 'tool_log'); set_config('buffersize', 0, 'logstore_standard'); set_config('logguests', 1, 'logstore_standard'); } /** * 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 00000054512 15152567405 0010633 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_standard; defined('MOODLE_INTERNAL') || die(); require_once(__DIR__ . '/fixtures/event.php'); require_once(__DIR__ . '/fixtures/restore_hack.php'); /** * Standard log store tests. * * @package logstore_standard * @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 { /** * @var bool Determine if we disabled the GC, so it can be re-enabled in tearDown. */ private $wedisabledgc = false; /** * 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; $this->resetAfterTest(); $this->preventResetByRollback(); // Logging waits till the transaction gets committed. // Apply JSON format system setting. set_config('jsonformat', $jsonformat ? 1 : 0, 'logstore_standard'); $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); // Enable logging plugin. set_config('enabled_stores', 'logstore_standard', 'tool_log'); set_config('buffersize', 0, 'logstore_standard'); set_config('logguests', 1, 'logstore_standard'); $manager = get_log_manager(true); $stores = $manager->get_readers(); $this->assertCount(1, $stores); $this->assertEquals(array('logstore_standard'), array_keys($stores)); /** @var \logstore_standard\log\store $store */ $store = $stores['logstore_standard']; $this->assertInstanceOf('logstore_standard\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); $exists = $store->get_events_select_exists('', array(), 'timecreated ASC', 0, 0); $this->assertFalse($exists); $this->setCurrentTimeStart(); $this->setUser(0); $event1 = \logstore_standard\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')); \logstore_standard_restore::hack_executing(1); $event2 = \logstore_standard\event\unittest_executed::create( array('context' => \context_module::instance($module2->cmid), 'other' => array('sample' => 6, 'xx' => 9))); $event2->trigger(); \logstore_standard_restore::hack_executing(0); \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); $this->assertSame('cli', $log2->origin); $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'] = 'restore'; $data['ip'] = null; $data['realuserid'] = 2; $this->assertEquals($data, $log3); // Test table exists. $tablename = $store->get_internal_log_table_name(); $this->assertTrue($DB->get_manager()->table_exists($tablename)); // 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); $exists = $store->get_events_select_exists('', array(), 'timecreated ASC', 0, 0); $this->assertTrue($exists); $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_standard'); $manager = get_log_manager(true); $stores = $manager->get_readers(); /** @var \logstore_standard\log\store $store */ $store = $stores['logstore_standard']; $DB->delete_records('logstore_standard_log'); \logstore_standard\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_standard\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_standard\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_standard\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_standard\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_standard\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_standard\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_standard\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_standard'); set_config('buffersize', 0, 'logstore_standard'); get_log_manager(true); $DB->delete_records('logstore_standard_log'); get_log_manager(true); $this->setUser(null); \logstore_standard\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_standard\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_standard\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_standard\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 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', 'outline' => 'report_outline', 'participation' => 'report_participation', 'stats' => 'report_stats' ], $allreports); $supportedreports = $logmanager->get_supported_reports('logstore_standard'); foreach ($expectedreports as $expectedreport) { $this->assertArrayHasKey($expectedreport, $supportedreports); } } /** * Verify that gc disabling works */ public function test_gc_enabled_as_expected() { if (!gc_enabled()) { $this->markTestSkipped('Garbage collector (gc) is globally disabled.'); } $this->disable_gc(); $this->assertTrue($this->wedisabledgc); $this->assertFalse(gc_enabled()); } /** * Test sql_reader::get_events_select_iterator. * @return void */ public function test_events_traversable() { global $DB; $this->disable_gc(); $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); } /** * Test that the standard log cleanup works correctly. */ public function test_cleanup_task() { global $DB; $this->resetAfterTest(); // Create some records spread over various days; test multiple iterations in cleanup. $ctx = \context_course::instance(1); $record = (object) array( 'edulevel' => 0, 'contextid' => $ctx->id, 'contextlevel' => $ctx->contextlevel, 'contextinstanceid' => $ctx->instanceid, 'userid' => 1, 'timecreated' => time(), ); $DB->insert_record('logstore_standard_log', $record); $record->timecreated -= 3600 * 24 * 30; $DB->insert_record('logstore_standard_log', $record); $record->timecreated -= 3600 * 24 * 30; $DB->insert_record('logstore_standard_log', $record); $record->timecreated -= 3600 * 24 * 30; $DB->insert_record('logstore_standard_log', $record); $this->assertEquals(4, $DB->count_records('logstore_standard_log')); // Remove all logs before "today". set_config('loglifetime', 1, 'logstore_standard'); $this->expectOutputString(" Deleted old log records from standard store.\n"); $clean = new \logstore_standard\task\cleanup_task(); $clean->execute(); $this->assertEquals(1, $DB->count_records('logstore_standard_log')); } /** * Tests the decode_other function can cope with both JSON and PHP serialized format. * * @param mixed $value Value to encode and decode * @dataProvider decode_other_provider */ public function test_decode_other($value) { $this->assertEquals($value, \logstore_standard\log\store::decode_other(serialize($value))); $this->assertEquals($value, \logstore_standard\log\store::decode_other(json_encode($value))); } public function test_decode_other_with_wrongly_encoded_contents() { $this->assertSame(null, \logstore_standard\log\store::decode_other(null)); } /** * List of possible values for 'other' field. * * I took these types from our logs based on the different first character of PHP serialized * data - my query found only these types. The normal case is an array. * * @return array Array of parameters */ public function decode_other_provider(): array { return [ [['info' => 'd2819896', 'logurl' => 'discuss.php?d=2819896']], [null], ['just a string'], [32768] ]; } /** * Checks that backup and restore of log data works correctly. * * @param bool $jsonformat True to test with JSON format * @dataProvider log_writing_provider */ public function test_backup_restore(bool $jsonformat) { global $DB; $this->resetAfterTest(); $this->preventResetByRollback(); // Enable logging plugin. set_config('enabled_stores', 'logstore_standard', 'tool_log'); set_config('buffersize', 0, 'logstore_standard'); $manager = get_log_manager(true); // User must be enrolled in course. $generator = $this->getDataGenerator(); $course = $generator->create_course(); $user = $generator->create_user(); $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student'); $this->setUser($user); // Apply JSON format system setting. set_config('jsonformat', $jsonformat ? 1 : 0, 'logstore_standard'); // Create some log data in a course - one with other data, one without. \logstore_standard\event\unittest_executed::create([ 'context' => \context_course::instance($course->id), 'other' => ['sample' => 5, 'xx' => 10]])->trigger(); $this->waitForSecond(); \logstore_standard\event\unittest_executed::create([ 'context' => \context_course::instance($course->id)])->trigger(); $records = array_values($DB->get_records('logstore_standard_log', ['courseid' => $course->id, 'target' => 'unittest'], 'timecreated')); $this->assertCount(2, $records); // Work out expected 'other' values based on JSON format. $expected0 = [ false => 'a:2:{s:6:"sample";i:5;s:2:"xx";i:10;}', true => '{"sample":5,"xx":10}' ]; $expected1 = [ false => 'N;', true => 'null' ]; // Backup the course twice, including log data. $this->setAdminUser(); $backupid1 = $this->backup($course); $backupid2 = $this->backup($course); // Restore it with same jsonformat. $newcourseid = $this->restore($backupid1, $course, '_A'); // Check entries are correctly encoded. $records = array_values($DB->get_records('logstore_standard_log', ['courseid' => $newcourseid, 'target' => 'unittest'], 'timecreated')); $this->assertCount(2, $records); $this->assertEquals($expected0[$jsonformat], $records[0]->other); $this->assertEquals($expected1[$jsonformat], $records[1]->other); // Change JSON format to opposite value and restore again. set_config('jsonformat', $jsonformat ? 0 : 1, 'logstore_standard'); $newcourseid = $this->restore($backupid2, $course, '_B'); // Check entries are correctly encoded in other format. $records = array_values($DB->get_records('logstore_standard_log', ['courseid' => $newcourseid, 'target' => 'unittest'], 'timecreated')); $this->assertEquals($expected0[!$jsonformat], $records[0]->other); $this->assertEquals($expected1[!$jsonformat], $records[1]->other); } /** * Backs a course up to temp directory. * * @param \stdClass $course Course object to backup * @return string ID of backup */ protected function backup($course): string { global $USER, $CFG; require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); // Turn off file logging, otherwise it can't delete the file (Windows). $CFG->backup_file_logger_level = \backup::LOG_NONE; // Do backup with default settings. MODE_IMPORT means it will just // create the directory and not zip it. $bc = new \backup_controller(\backup::TYPE_1COURSE, $course->id, \backup::FORMAT_MOODLE, \backup::INTERACTIVE_NO, \backup::MODE_IMPORT, $USER->id); $bc->get_plan()->get_setting('users')->set_status(\backup_setting::NOT_LOCKED); $bc->get_plan()->get_setting('users')->set_value(true); $bc->get_plan()->get_setting('logs')->set_value(true); $backupid = $bc->get_backupid(); $bc->execute_plan(); $bc->destroy(); return $backupid; } /** * Restores a course from temp directory. * * @param string $backupid Backup id * @param \stdClass $course Original course object * @param string $suffix Suffix to add after original course shortname and fullname * @return int New course id * @throws \restore_controller_exception */ protected function restore(string $backupid, $course, string $suffix): int { global $USER, $CFG; require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); // Do restore to new course with default settings. $newcourseid = \restore_dbops::create_new_course( $course->fullname . $suffix, $course->shortname . $suffix, $course->category); $rc = new \restore_controller($backupid, $newcourseid, \backup::INTERACTIVE_NO, \backup::MODE_GENERAL, $USER->id, \backup::TARGET_NEW_COURSE); $rc->get_plan()->get_setting('logs')->set_value(true); $rc->get_plan()->get_setting('users')->set_value(true); $this->assertTrue($rc->execute_precheck()); $rc->execute_plan(); $rc->destroy(); return $newcourseid; } /** * Disable the garbage collector if it's enabled to ensure we don't adjust memory statistics. */ private function disable_gc() { if (gc_enabled()) { $this->wedisabledgc = true; gc_disable(); } } /** * Reset any garbage collector changes to the previous state at the end of the test. */ public function tearDown(): void { if ($this->wedisabledgc) { gc_enable(); } $this->wedisabledgc = false; } } backup/moodle2/backup_logstore_standard_subplugin.class.php 0000644 00000004254 15152567405 0020341 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_standard subplugin. * * @package logstore_standard * @category backup * @copyright 2015 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); class backup_logstore_standard_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_nested_element('logstore_standard_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); $subpluginlog->set_source_table('logstore_standard_log', array('contextid' => backup::VAR_CONTEXTID)); return $subplugin; } } backup/moodle2/restore_logstore_standard_subplugin.class.php 0000644 00000004544 15152567405 0020561 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_standard subplugin. * * @package logstore_standard * @category backup * @copyright 2015 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); class restore_logstore_standard_subplugin extends restore_tool_log_logstore_subplugin { /** * 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_standard', $enabledlogstores)) { return array(); // The logstore is not enabled, nothing to restore. } $paths = array(); $elename = $this->get_namefor('log'); $elepath = $this->get_pathfor('/logstore_standard_log'); $paths[] = new restore_path_element($elename, $elepath); return $paths; } /** * Process logstore_standard_log entries. * * This method proceeds to read, complete, remap and, finally, * discard or save every log entry. * * @param array() $data log entry. */ public function process_logstore_standard_log($data) { global $DB; $data = $this->process_log($data, get_config('logstore_standard', 'jsonformat')); if ($data) { $DB->insert_record('logstore_standard_log', $data); } } } settings.php 0000644 00000004552 15152567405 0007135 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/>. /** * Standard log store settings. * * @package logstore_standard * @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) { $settings->add(new admin_setting_configcheckbox('logstore_standard/logguests', new lang_string('logguests', 'core_admin'), new lang_string('logguests_help', 'core_admin'), 1)); $settings->add(new admin_setting_configcheckbox('logstore_standard/jsonformat', new lang_string('jsonformat', 'logstore_standard'), new lang_string('jsonformat_desc', 'logstore_standard'), 1)); $options = array( 0 => new lang_string('neverdeletelogs'), 1000 => new lang_string('numdays', '', 1000), 365 => new lang_string('numdays', '', 365), 180 => new lang_string('numdays', '', 180), 150 => new lang_string('numdays', '', 150), 120 => new lang_string('numdays', '', 120), 90 => new lang_string('numdays', '', 90), 60 => new lang_string('numdays', '', 60), 35 => new lang_string('numdays', '', 35), 10 => new lang_string('numdays', '', 10), 5 => new lang_string('numdays', '', 5), 2 => new lang_string('numdays', '', 2)); $settings->add(new admin_setting_configselect('logstore_standard/loglifetime', new lang_string('loglifetime', 'core_admin'), new lang_string('configloglifetime', 'core_admin'), 0, $options)); $settings->add(new admin_setting_configtext('logstore_standard/buffersize', get_string('buffersize', 'logstore_standard'), '', '50', PARAM_INT)); } version.php 0000644 00000002214 15152567405 0006753 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/>. /** * Standard log store. * * @package logstore_standard * @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_standard'; // Full name of the plugin (used for diagnostics). classes/privacy/provider.php 0000644 00000010624 15152567405 0012236 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_standard * @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_standard\privacy; defined('MOODLE_INTERNAL') || die(); use context; use core_privacy\local\metadata\collection; use core_privacy\local\request\contextlist; /** * Data provider class. * * @package logstore_standard * @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_database_table('logstore_standard_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) { $sql = " SELECT l.contextid FROM {logstore_standard_log} l WHERE l.userid = :userid1 OR l.relateduserid = :userid2 OR l.realuserid = :userid3"; $contextlist->add_from_sql($sql, [ 'userid1' => $userid, 'userid2' => $userid, 'userid3' => $userid, ]); } /** * 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) { $params = ['contextid' => $userlist->get_context()->id]; $sql = "SELECT userid, relateduserid, realuserid FROM {logstore_standard_log} WHERE contextid = :contextid"; $userlist->add_from_sql('userid', $sql, $params); $userlist->add_from_sql('relateduserid', $sql, $params); $userlist->add_from_sql('realuserid', $sql, $params); } /** * Get the database object. * * @return array Containing moodle_database, string, or null values. */ protected static function get_database_and_table() { global $DB; return [$DB, 'logstore_standard_log']; } /** * 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_standard')]; } } classes/log/store.php 0000644 00000013606 15152567405 0010647 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/>. /** * Standard log reader/writer. * * @package logstore_standard * @copyright 2013 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace logstore_standard\log; defined('MOODLE_INTERNAL') || die(); class store implements \tool_log\log\writer, \core\log\sql_internal_table_reader { use \tool_log\helper\store, \tool_log\helper\buffered_writer, \tool_log\helper\reader; /** @var string $logguests true if logging guest access */ protected $logguests; public function __construct(\tool_log\log\manager $manager) { $this->helper_setup($manager); // Log everything before setting is saved for the first time. $this->logguests = $this->get_config('logguests', 1); // 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); } /** * Should the event be ignored (== not logged)? * @param \core\event\base $event * @return bool */ protected function is_event_ignored(\core\event\base $event) { 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; } /** * Finally store the events into the database. * * @param array $evententries raw event data */ protected function insert_event_entries($evententries) { global $DB; $DB->insert_records('logstore_standard_log', $evententries); } public function get_events_select($selectwhere, array $params, $sort, $limitfrom, $limitnum) { global $DB; $sort = self::tweak_sort_by_id($sort); $events = array(); $records = $DB->get_recordset_select('logstore_standard_log', $selectwhere, $params, $sort, '*', $limitfrom, $limitnum); foreach ($records as $data) { if ($event = $this->get_log_event($data)) { $events[$data->id] = $event; } } $records->close(); 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) { global $DB; $sort = self::tweak_sort_by_id($sort); $recordset = $DB->get_recordset_select('logstore_standard_log', $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) { global $DB; return $DB->count_records_select('logstore_standard_log', $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 { global $DB; return $DB->record_exists_select('logstore_standard_log', $selectwhere, $params); } public function get_internal_log_table_name() { return 'logstore_standard_log'; } /** * 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() { // Only enabled stpres are queried, // this means we can return true here unless store has some extra switch. return true; } } classes/task/cleanup_task.php 0000644 00000004763 15152567405 0012351 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/>. /** * Standard log reader/writer. * * @package logstore_standard * @copyright 2014 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace logstore_standard\task; defined('MOODLE_INTERNAL') || die(); class cleanup_task extends \core\task\scheduled_task { /** * Get a descriptive name for this task (shown to admins). * * @return string */ public function get_name() { return get_string('taskcleanup', 'logstore_standard'); } /** * Do the job. * Throw exceptions on errors (the job will be retried). */ public function execute() { global $DB; $loglifetime = (int)get_config('logstore_standard', 'loglifetime'); if (empty($loglifetime) || $loglifetime < 0) { return; } $loglifetime = time() - ($loglifetime * 3600 * 24); // Value in days. $lifetimep = array($loglifetime); $start = time(); while ($min = $DB->get_field_select("logstore_standard_log", "MIN(timecreated)", "timecreated < ?", $lifetimep)) { // Break this down into chunks to avoid transaction for too long and generally thrashing database. // Experiments suggest deleting one day takes up to a few seconds; probably a reasonable chunk size usually. // If the cleanup has just been enabled, it might take e.g a month to clean the years of logs. $params = array(min($min + 3600 * 24, $loglifetime)); $DB->delete_records_select("logstore_standard_log", "timecreated < ?", $params); if (time() > $start + 300) { // Do not churn on log deletion for too long each run. break; } } mtrace(" Deleted old log records from standard store."); } } lang/en/logstore_standard.php 0000644 00000004066 15152567405 0012336 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_standard * @copyright 2013 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $string['buffersize'] = 'Write buffer size'; $string['jsonformat'] = 'JSON format'; $string['jsonformat_desc'] = 'Use standard JSON format instead of PHP serialised data in the \'other\' database field.'; $string['pluginname'] = 'Standard log'; $string['pluginname_desc'] = 'A log plugin stores log entries in a Moodle 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['taskcleanup'] = 'Log table cleanup';
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | ���֧ߧ֧�ѧ�ڧ� ����ѧߧڧ��: 0 |
proxy
|
phpinfo
|
���ѧ����ۧܧ�