--- /dev/null
+<?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/>.
+/**
+ * Privacy Subsystem implementation for core_admin.
+ *
+ * @package core_admin
+ * @copyright 2018 Carlos Escobedo <carlos@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace core_admin\privacy;
+defined('MOODLE_INTERNAL') || die();
+/**
+ * Privacy Subsystem for core_admin implementing null_provider.
+ *
+ * @copyright 2018 Carlos Escobedo <carlos@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+ /**
+ * Get the language string identifier with the component's language
+ * file to explain why this plugin stores no data.
+ *
+ * @return string
+ */
+ public static function get_reason() : string {
+ return 'privacy:metadata';
+ }
+}
echo $OUTPUT->heading(get_string('registerwithmoodleorg', 'admin'));
}
-$renderer = $PAGE->get_renderer('core', 'register');
+$renderer = $PAGE->get_renderer('core', 'admin');
echo $renderer->moodleorg_registration_message();
$siteregistrationform->display();
+++ /dev/null
-<?php
-
-///////////////////////////////////////////////////////////////////////////
-// //
-// This file is part of Moodle - http://moodle.org/ //
-// Moodle - Modular Object-Oriented Dynamic Learning Environment //
-// //
-// 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/>. //
-// //
-///////////////////////////////////////////////////////////////////////////
-
-/**
- * Registration renderer.
- * @package moodle
- * @subpackage registration
- * @copyright 2010 Moodle Pty Ltd (http://moodle.com)
- * @author Jerome Mouneyrac
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class core_register_renderer extends plugin_renderer_base {
-
- /**
- * Display message about the benefits of registering on Moodle.org
- *
- * @return string
- */
- public function moodleorg_registration_message() {
- return format_text(get_string('registermoodlenet', 'admin'), FORMAT_HTML, ['noclean' => true]);
- }
-}
return $this->warning(get_string('legacylogginginuse'));
}
}
+
+ /**
+ * Display message about the benefits of registering on Moodle.org
+ *
+ * @return string
+ */
+ public function moodleorg_registration_message() {
+ return format_text(get_string('registermoodlenet', 'admin'), FORMAT_HTML, ['noclean' => true]);
+ }
}
if (!$this->disabled) {
$output .= '<input type="hidden" name="contextlevel' . $cl . '" value="0" />';
}
- $output .= '<input type="checkbox" id="cl' . $cl . '" name="contextlevel' . $cl .
+ $output .= '<div class="form-check justify-content-start w-100">';
+ $output .= '<input class="form-check-input" type="checkbox" id="cl' . $cl . '" name="contextlevel' . $cl .
'" value="1" ' . $extraarguments . '/> ';
- $output .= '<label for="cl' . $cl . '">' . $clname . "</label><br />\n";
+ $output .= '<label class="form-check-label" for="cl' . $cl . '">' . $clname . "</label>\n";
+ $output .= '</div>';
}
return $output;
}
--- /dev/null
+<?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/>.
+/**
+ * Privacy Subsystem implementation for core_role.
+ *
+ * @package core_role
+ * @copyright 2018 Carlos Escobedo <carlos@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace core_role\privacy;
+defined('MOODLE_INTERNAL') || die();
+
+use \core_privacy\local\metadata\collection;
+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;
+
+/**
+ * Privacy provider for core_role.
+ *
+ * @copyright 2018 Carlos Escobedo <carlos@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements
+ \core_privacy\local\metadata\provider,
+ \core_privacy\local\request\subsystem\provider,
+ \core_privacy\local\request\subsystem\plugin_provider,
+ \core_privacy\local\request\user_preference_provider {
+
+ /**
+ * Get information about the user data stored by this plugin.
+ *
+ * @param collection $collection An object for storing metadata.
+ * @return collection The metadata.
+ */
+ public static function get_metadata(collection $collection) : collection {
+ $rolecapabilities = [
+ 'roleid' => 'privacy:metadata:role_capabilities:roleid',
+ 'capability' => 'privacy:metadata:role_capabilities:capability',
+ 'permission' => 'privacy:metadata:role_capabilities:permission',
+ 'timemodified' => 'privacy:metadata:role_capabilities:timemodified',
+ 'modifierid' => 'privacy:metadata:role_capabilities:modifierid'
+ ];
+ $roleassignments = [
+ 'roleid' => 'privacy:metadata:role_assignments:roleid',
+ 'userid' => 'privacy:metadata:role_assignments:userid',
+ 'timemodified' => 'privacy:metadata:role_assignments:timemodified',
+ 'modifierid' => 'privacy:metadata:role_assignments:modifierid',
+ 'component' => 'privacy:metadata:role_assignments:component',
+ 'itemid' => 'privacy:metadata:role_assignments:itemid'
+ ];
+ $collection->add_database_table('role_capabilities', $rolecapabilities,
+ 'privacy:metadata:role_capabilities:tableexplanation');
+ $collection->add_database_table('role_assignments', $roleassignments,
+ 'privacy:metadata:role_assignments:tableexplanation');
+
+ $collection->add_user_preference('definerole_showadvanced',
+ 'privacy:metadata:preference:showadvanced');
+
+ return $collection;
+ }
+ /**
+ * Export all user preferences for the plugin.
+ *
+ * @param int $userid The userid of the user whose data is to be exported.
+ */
+ public static function export_user_preferences(int $userid) {
+ $showadvanced = get_user_preferences('definerole_showadvanced', null, $userid);
+ if ($showadvanced !== null) {
+ writer::export_user_preference('core_role',
+ 'definerole_showadvanced',
+ transform::yesno($showadvanced),
+ get_string('privacy:metadata:preference:showadvanced', 'core_role')
+ );
+ }
+ }
+ /**
+ * Return all contexts for this userid.
+ *
+ * @param int $userid The user ID.
+ * @return contextlist The list of context IDs.
+ */
+ public static function get_contexts_for_userid(int $userid) : contextlist {
+ global $DB;
+
+ $contextlist = new contextlist();
+
+ // The role_capabilities table contains user data.
+ $contexts = [
+ CONTEXT_SYSTEM,
+ CONTEXT_USER,
+ CONTEXT_COURSECAT,
+ CONTEXT_COURSE,
+ CONTEXT_MODULE,
+ CONTEXT_BLOCK
+ ];
+ list($insql, $inparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
+ $sql = "SELECT ctx.id
+ FROM {context} ctx
+ JOIN {role_capabilities} rc
+ ON rc.contextid = ctx.id
+ AND ((ctx.contextlevel {$insql} AND rc.modifierid = :modifierid)
+ OR (ctx.contextlevel = :contextlevel AND ctx.instanceid = :userid))";
+ $params = [
+ 'modifierid' => $userid,
+ 'contextlevel' => CONTEXT_USER,
+ 'userid' => $userid
+ ];
+ $params += $inparams;
+
+ $contextlist->add_from_sql($sql, $params);
+
+ // The role_assignments table contains user data.
+ $contexts = [
+ CONTEXT_SYSTEM,
+ CONTEXT_USER,
+ CONTEXT_COURSECAT,
+ CONTEXT_COURSE,
+ CONTEXT_MODULE,
+ CONTEXT_BLOCK
+ ];
+ list($insql, $inparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
+ $params = [
+ 'userid' => $userid,
+ 'modifierid' => $userid
+ ];
+ $params += $inparams;
+ $sql = "SELECT ctx.id
+ FROM {role_assignments} ra
+ JOIN {context} ctx
+ ON ctx.id = ra.contextid
+ AND ctx.contextlevel {$insql}
+ WHERE (ra.userid = :userid
+ OR ra.modifierid = :modifierid)
+ AND ra.component != 'tool_cohortroles'";
+ $contextlist->add_from_sql($sql, $params);
+
+ return $contextlist;
+ }
+ /**
+ * Export all user data for the specified user, in the specified contexts.
+ *
+ * @param approved_contextlist $contextlist The list of approved contexts for a user.
+ */
+ public static function export_user_data(approved_contextlist $contextlist) {
+ global $DB;
+
+ if (empty($contextlist)) {
+ return;
+ }
+
+ $rolesnames = self::get_roles_name();
+ $userid = $contextlist->get_user()->id;
+ $ctxfields = \context_helper::get_preload_record_columns_sql('ctx');
+ list($insql, $inparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
+
+ // Role Assignments export data.
+ $contexts = [
+ CONTEXT_SYSTEM,
+ CONTEXT_USER,
+ CONTEXT_COURSECAT,
+ CONTEXT_COURSE,
+ CONTEXT_MODULE,
+ CONTEXT_BLOCK
+ ];
+ list($inctxsql, $ctxparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
+ $sql = "SELECT ra.id, ra.contextid, ra.roleid, ra.userid, ra.timemodified, ra.modifierid, $ctxfields
+ FROM {role_assignments} ra
+ JOIN {context} ctx
+ ON ctx.id = ra.contextid
+ AND ctx.contextlevel {$inctxsql}
+ AND (ra.userid = :userid OR ra.modifierid = :modifierid)
+ AND ra.component != 'tool_cohortroles'
+ JOIN {role} r
+ ON r.id = ra.roleid
+ WHERE ctx.id {$insql}";
+ $params = ['userid' => $userid, 'modifierid' => $userid];
+ $params += $inparams;
+ $params += $ctxparams;
+ $assignments = $DB->get_recordset_sql($sql, $params);
+ foreach ($assignments as $assignment) {
+ \context_helper::preload_from_record($assignment);
+ $alldata[$assignment->contextid][$rolesnames[$assignment->roleid]][] = (object)[
+ 'timemodified' => transform::datetime($assignment->timemodified),
+ 'userid' => transform::user($assignment->userid),
+ 'modifierid' => transform::user($assignment->modifierid)
+ ];
+ }
+ $assignments->close();
+ if (!empty($alldata)) {
+ array_walk($alldata, function($roledata, $contextid) {
+ $context = \context::instance_by_id($contextid);
+ array_walk($roledata, function($data, $rolename) use ($context) {
+ writer::with_context($context)->export_data(
+ [get_string('privacy:metadata:role_assignments', 'core_role'), $rolename],
+ (object)$data);
+ });
+ });
+ unset($alldata);
+ }
+
+ // Role Capabilities export data.
+ $strpermissions = self::get_permissions_name();
+ $contexts = [
+ CONTEXT_SYSTEM,
+ CONTEXT_USER,
+ CONTEXT_COURSECAT,
+ CONTEXT_COURSE,
+ CONTEXT_MODULE,
+ CONTEXT_BLOCK
+ ];
+ list($inctxsql, $ctxparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
+ $sql = "SELECT rc.id, rc.contextid, rc.capability, rc.permission, rc.timemodified, rc.roleid, $ctxfields
+ FROM {context} ctx
+ JOIN {role_capabilities} rc
+ ON rc.contextid = ctx.id
+ AND ((ctx.contextlevel {$inctxsql} AND rc.modifierid = :modifierid)
+ OR (ctx.contextlevel = :contextlevel AND ctx.instanceid = :userid))
+ WHERE ctx.id {$insql}";
+ $params = [
+ 'modifierid' => $userid,
+ 'contextlevel' => CONTEXT_USER,
+ 'userid' => $userid
+ ];
+ $params += $inparams;
+ $params += $ctxparams;
+ $capabilities = $DB->get_recordset_sql($sql, $params);
+ foreach ($capabilities as $capability) {
+ \context_helper::preload_from_record($capability);
+ $alldata[$capability->contextid][$rolesnames[$capability->roleid]][] = (object)[
+ 'timemodified' => transform::datetime($capability->timemodified),
+ 'capability' => $capability->capability,
+ 'permission' => $strpermissions[$capability->permission]
+ ];
+ }
+ $capabilities->close();
+ if (!empty($alldata)) {
+ array_walk($alldata, function($capdata, $contextid) {
+ $context = \context::instance_by_id($contextid);
+ array_walk($capdata, function($data, $rolename) use ($context) {
+ writer::with_context($context)->export_data(
+ [get_string('privacy:metadata:role_capabilities', 'core_role'), $rolename],
+ (object)$data);
+ });
+ });
+ }
+ }
+ /**
+ * Exports the data relating to tool_cohortroles component on role assignments by
+ * Assign user roles to cohort feature.
+ *
+ * @param int $userid The user ID.
+ */
+ public static function export_user_role_to_cohort(int $userid) {
+ global $DB;
+
+ $rolesnames = self::get_roles_name();
+ $sql = "SELECT ra.id, ra.contextid, ra.roleid, ra.userid, ra.timemodified, ra.modifierid, r.id as roleid
+ FROM {role_assignments} ra
+ JOIN {context} ctx
+ ON ctx.id = ra.contextid
+ AND ctx.contextlevel = :contextlevel
+ AND ra.component = 'tool_cohortroles'
+ JOIN {role} r
+ ON r.id = ra.roleid
+ WHERE ctx.instanceid = :instanceid
+ OR ra.userid = :userid";
+ $params = ['userid' => $userid, 'instanceid' => $userid, 'contextlevel' => CONTEXT_USER];
+ $assignments = $DB->get_recordset_sql($sql, $params);
+ foreach ($assignments as $assignment) {
+ $alldata[$assignment->contextid][$rolesnames[$assignment->roleid]][] = (object)[
+ 'timemodified' => transform::datetime($assignment->timemodified),
+ 'userid' => transform::user($assignment->userid),
+ 'modifierid' => transform::user($assignment->modifierid)
+ ];
+ }
+ $assignments->close();
+ if (!empty($alldata)) {
+ array_walk($alldata, function($roledata, $contextid) {
+ $context = \context::instance_by_id($contextid);
+ array_walk($roledata, function($data, $rolename) use ($context) {
+ writer::with_context($context)->export_related_data(
+ [get_string('privacy:metadata:role_cohortroles', 'core_role'), $rolename], 'cohortroles',
+ (object)$data);
+ });
+ });
+ }
+ }
+ /**
+ * Delete all user data for this context.
+ *
+ * @param \context $context The context to delete data for.
+ */
+ public static function delete_data_for_all_users_in_context(\context $context) {
+ global $DB;
+
+ // Don't remove data from role_capabilities.
+ // Because this data affects the whole Moodle, there are override capabilities.
+ // Don't belong to the modifier user.
+
+ // Remove data from role_assignments.
+ if (empty($context)) {
+ return;
+ }
+ $DB->delete_records('role_assignments', ['contextid' => $context->id]);
+ }
+ /**
+ * Delete all user data for this user only.
+ *
+ * @param approved_contextlist $contextlist The list of approved contexts for a user.
+ */
+ public static function delete_data_for_user(approved_contextlist $contextlist) {
+ global $DB;
+
+ // Don't remove data from role_capabilities.
+ // Because this data affects the whole Moodle, there are override capabilities.
+ // Don't belong to the modifier user.
+
+ // Remove data from role_assignments.
+ if (empty($contextlist->count())) {
+ return;
+ }
+ $userid = $contextlist->get_user()->id;
+ foreach ($contextlist->get_contexts() as $context) {
+ // Only delete the roles assignments where the user is assigned in all contexts.
+ $DB->delete_records('role_assignments', ['userid' => $userid, 'contextid' => $context->id]);
+ }
+ }
+ /**
+ * Delete user entries in role_assignments related to the feature
+ * Assign user roles to cohort feature.
+ *
+ * @param int $userid The user ID.
+ */
+ public static function delete_user_role_to_cohort(int $userid) {
+ global $DB;
+
+ // Delete entries where userid is a mentor by tool_cohortroles.
+ $DB->delete_records('role_assignments', ['userid' => $userid, 'component' => 'tool_cohortroles']);
+ }
+ /**
+ * Get all the localised roles name in a simple array.
+ *
+ * @return array Array of name of the roles by roleid.
+ */
+ protected static function get_roles_name() {
+ $roles = role_fix_names(get_all_roles(), \context_system::instance(), ROLENAME_ORIGINAL);
+ $rolesnames = array();
+ foreach ($roles as $role) {
+ $rolesnames[$role->id] = $role->localname;
+ }
+ return $rolesnames;
+ }
+ /**
+ * Get all the permissions name in a simple array.
+ *
+ * @return array Array of permissions name.
+ */
+ protected static function get_permissions_name() {
+ $strpermissions = array(
+ CAP_INHERIT => get_string('inherit', 'role'),
+ CAP_ALLOW => get_string('allow', 'role'),
+ CAP_PREVENT => get_string('prevent', 'role'),
+ CAP_PROHIBIT => get_string('prohibit', 'role')
+ );
+ return $strpermissions;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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/>.
+/**
+ * Privacy test for core_role
+ *
+ * @package core_role
+ * @category test
+ * @copyright 2018 Carlos Escobedo <carlos@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+defined('MOODLE_INTERNAL') || die();
+use \core_role\privacy\provider;
+use \core_privacy\local\request\approved_contextlist;
+use \core_privacy\local\request\writer;
+use \core_privacy\tests\provider_testcase;
+use \core_privacy\local\request\transform;
+use \tool_cohortroles\api;
+
+/**
+ * Privacy test for core_role
+ *
+ * @copyright 2018 Carlos Escobedo <carlos@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_role_privacy_testcase extends provider_testcase {
+ /**
+ * Test to check export_user_preferences.
+ * returns user preferences data.
+ */
+ public function test_export_user_preferences() {
+ $this->resetAfterTest();
+ $this->setAdminUser();
+ $user = $this->getDataGenerator()->create_user();
+ $this->setUser($user);
+ $showadvanced = 1;
+ set_user_preference('definerole_showadvanced', $showadvanced);
+ provider::export_user_preferences($user->id);
+ $writer = writer::with_context(\context_system::instance());
+ $prefs = $writer->get_user_preferences('core_role');
+ $this->assertEquals(transform::yesno($showadvanced), transform::yesno($prefs->definerole_showadvanced->value));
+ $this->assertEquals(get_string('privacy:metadata:preference:showadvanced', 'core_role'),
+ $prefs->definerole_showadvanced->description);
+ }
+ /**
+ * Check all contexts are returned if there is any user data for this user.
+ */
+ public function test_get_contexts_for_userid() {
+ global $DB;
+
+ $this->resetAfterTest();
+ $this->setAdminUser();
+ $user = $this->getDataGenerator()->create_user();
+ $this->assertEmpty(provider::get_contexts_for_userid($user->id));
+
+ $user2 = $this->getDataGenerator()->create_user();
+ $usercontext2 = \context_user::instance($user2->id);
+ $course = $this->getDataGenerator()->create_course();
+ $course2 = $this->getDataGenerator()->create_course();
+ $coursecat = $this->getDataGenerator()->create_category();
+ $cm = $this->getDataGenerator()->create_module('chat', ['course' => $course->id]);
+ $cmcontext = \context_module::instance($cm->cmid);
+ $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
+ $cmcontext2 = \context_module::instance($page->cmid);
+ $coursecontext = \context_course::instance($course->id);
+ $coursecontext2 = \context_course::instance($course2->id);
+ $coursecatcontext = \context_coursecat::instance($coursecat->id);
+ $systemcontext = \context_system::instance();
+ $block = $this->getDataGenerator()->create_block('online_users');
+ $blockcontext = \context_block::instance($block->id);
+
+ $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
+ $manager = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
+
+ // Role assignments, where the user is assigned.
+ role_assign($student->id, $user->id, $cmcontext2->id);
+ role_assign($student->id, $user->id, $coursecontext2->id);
+ role_assign($student->id, $user->id, $blockcontext->id);
+ role_assign($manager->id, $user->id, $usercontext2->id);
+ // Role assignments, where the user makes assignments.
+ $this->setUser($user);
+ role_assign($student->id, $user2->id, $coursecontext->id);
+ role_assign($manager->id, $user2->id, $coursecatcontext->id);
+ role_assign($manager->id, $user2->id, $systemcontext->id);
+
+ // Role capabilities.
+ $this->setUser($user);
+ $result = assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $student->id, $cmcontext->id);
+
+ $contextlist = provider::get_contexts_for_userid($user->id)->get_contextids();
+ $this->assertCount(8, $contextlist);
+ $this->assertTrue(in_array($cmcontext->id, $contextlist));
+ }
+
+ /**
+ * Test that user data is exported correctly.
+ */
+ public function test_export_user_data() {
+ global $DB;
+
+ $this->resetAfterTest();
+ $this->setAdminUser();
+ $user = $this->getDataGenerator()->create_user();
+ $user2 = $this->getDataGenerator()->create_user();
+ $usercontext2 = \context_user::instance($user2->id);
+ $course = $this->getDataGenerator()->create_course();
+ $course2 = $this->getDataGenerator()->create_course();
+ $coursecat = $this->getDataGenerator()->create_category();
+ $cm = $this->getDataGenerator()->create_module('chat', ['course' => $course->id]);
+ $cmcontext = \context_module::instance($cm->cmid);
+ $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
+ $cmcontext2 = \context_module::instance($page->cmid);
+ $coursecontext = \context_course::instance($course->id);
+ $coursecontext2 = \context_course::instance($course2->id);
+ $coursecatcontext = \context_coursecat::instance($coursecat->id);
+ $systemcontext = \context_system::instance();
+ $block = $this->getDataGenerator()->create_block('online_users');
+ $blockcontext = \context_block::instance($block->id);
+
+ $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
+ $manager = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
+ $rolesnames = self::get_roles_name();
+
+ $subcontextstudent = [
+ get_string('privacy:metadata:role_assignments', 'core_role'),
+ $rolesnames[$student->id]
+ ];
+ $subcontextmanager = [
+ get_string('privacy:metadata:role_assignments', 'core_role'),
+ $rolesnames[$manager->id]
+ ];
+ $subcontextrc = [
+ get_string('privacy:metadata:role_capabilities', 'core_role'),
+ $rolesnames[$student->id]
+ ];
+
+ // Test over role assignments.
+ // Where the user is assigned.
+ role_assign($student->id, $user->id, $cmcontext2->id);
+ role_assign($student->id, $user->id, $coursecontext2->id);
+ role_assign($student->id, $user->id, $blockcontext->id);
+ role_assign($manager->id, $user->id, $usercontext2->id);
+ // Where the user makes assignments.
+ $this->setUser($user);
+ role_assign($manager->id, $user2->id, $coursecatcontext->id);
+ role_assign($manager->id, $user2->id, $systemcontext->id);
+
+ // Test overridable roles in module, course, category, user, system and block.
+ assign_capability('moodle/backup:backupactivity', CAP_ALLOW, $student->id, $cmcontext->id, true);
+ assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $student->id, $coursecontext->id, true);
+ assign_capability('moodle/category:manage', CAP_ALLOW, $student->id, $coursecatcontext->id, true);
+ assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $student->id, $systemcontext->id, true);
+ assign_capability('moodle/block:edit', CAP_ALLOW, $student->id, $blockcontext->id, true);
+ assign_capability('moodle/competency:evidencedelete', CAP_ALLOW, $student->id, $usercontext2->id, true);
+
+ // Retrieve the user's context ids.
+ $contextlist = provider::get_contexts_for_userid($user->id);
+ $approvedcontextlist = new approved_contextlist($user, 'core_role', $contextlist->get_contextids());
+
+ $strpermissions = array(
+ CAP_INHERIT => get_string('inherit', 'role'),
+ CAP_ALLOW => get_string('allow', 'role'),
+ CAP_PREVENT => get_string('prevent', 'role'),
+ CAP_PROHIBIT => get_string('prohibit', 'role')
+ );
+ // Retrieve role capabilities and role assignments.
+ provider::export_user_data($approvedcontextlist);
+ foreach ($contextlist as $context) {
+ $writer = writer::with_context($context);
+ $this->assertTrue($writer->has_any_data());
+ if ($context->contextlevel == CONTEXT_MODULE) {
+ if ($data = $writer->get_data($subcontextstudent)) {
+ $this->assertEquals($user->id, reset($data)->userid);
+ }
+ if ($data = $writer->get_data($subcontextrc)) {
+ $this->assertEquals('moodle/backup:backupactivity', reset($data)->capability);
+ $this->assertEquals($strpermissions[CAP_ALLOW], reset($data)->permission);
+ }
+ }
+ if ($context->contextlevel == CONTEXT_COURSE) {
+ if ($data = $writer->get_data($subcontextstudent)) {
+ $this->assertEquals($user->id, reset($data)->userid);
+ }
+ if ($data = $writer->get_data($subcontextrc)) {
+ $this->assertEquals('moodle/backup:backupcourse', reset($data)->capability);
+ }
+ }
+ if ($context->contextlevel == CONTEXT_COURSECAT) {
+ if ($data = $writer->get_data($subcontextmanager)) {
+ $this->assertEquals($user->id, reset($data)->modifierid);
+ }
+ if ($data = $writer->get_data($subcontextrc)) {
+ $this->assertEquals('moodle/category:manage', reset($data)->capability);
+ }
+ }
+ if ($context->contextlevel == CONTEXT_SYSTEM) {
+ if ($data = $writer->get_data($subcontextmanager)) {
+ $this->assertEquals($user->id, reset($data)->modifierid);
+ }
+ if ($data = $writer->get_data($subcontextrc)) {
+ $this->assertEquals('moodle/backup:backupcourse', reset($data)->capability);
+ }
+ }
+ if ($context->contextlevel == CONTEXT_BLOCK) {
+ if ($data = $writer->get_data($subcontextstudent)) {
+ $this->assertEquals($user->id, reset($data)->userid);
+ }
+ if ($data = $writer->get_data($subcontextrc)) {
+ $this->assertEquals('moodle/block:edit', reset($data)->capability);
+ }
+ }
+ if ($context->contextlevel == CONTEXT_USER) {
+ if ($data = $writer->get_data($subcontextmanager)) {
+ $this->assertEquals($user->id, reset($data)->userid);
+ }
+ if ($data = $writer->get_data($subcontextrc)) {
+ $this->assertEquals('moodle/competency:evidencedelete', reset($data)->capability);
+ }
+ }
+ }
+ }
+ /**
+ * Test for provider::delete_data_for_all_users_in_context().
+ */
+ public function test_delete_data_for_all_users_in_context() {
+ global $DB;
+
+ $this->resetAfterTest();
+ $this->setAdminUser();
+ $user = $this->getDataGenerator()->create_user();
+ $user2 = $this->getDataGenerator()->create_user();
+ $usercontext2 = \context_user::instance($user2->id);
+ $user3 = $this->getDataGenerator()->create_user();
+ $course = $this->getDataGenerator()->create_course();
+ $coursecontext = \context_course::instance($course->id);
+ $coursecat = $this->getDataGenerator()->create_category();
+ $coursecatcontext = \context_coursecat::instance($coursecat->id);
+ $systemcontext = \context_system::instance();
+ $cm = $this->getDataGenerator()->create_module('chat', ['course' => $course->id]);
+ $cmcontext = \context_module::instance($cm->cmid);
+ $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
+ $manager = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
+ $block = $this->getDataGenerator()->create_block('online_users');
+ $blockcontext = \context_block::instance($block->id);
+
+ // Role assignments CONTEXT_COURSE.
+ role_assign($student->id, $user->id, $coursecontext->id);
+ role_assign($student->id, $user2->id, $coursecontext->id);
+ role_assign($student->id, $user3->id, $coursecontext->id);
+ $count = $DB->count_records('role_assignments', ['contextid' => $coursecontext->id]);
+ $this->assertEquals(3, $count);
+ // Role assignments CONTEXT_COURSECAT.
+ role_assign($student->id, $user2->id, $coursecatcontext->id);
+ role_assign($student->id, $user3->id, $coursecatcontext->id);
+ $count = $DB->count_records('role_assignments', ['contextid' => $coursecatcontext->id]);
+ $this->assertEquals(2, $count);
+ // Role assignments CONTEXT_SYSTEM.
+ role_assign($student->id, $user->id, $systemcontext->id);
+ $count = $DB->count_records('role_assignments', ['contextid' => $systemcontext->id]);
+ $this->assertEquals(1, $count);
+ // Role assignments CONTEXT_MODULE.
+ role_assign($student->id, $user->id, $cmcontext->id);
+ $count = $DB->count_records('role_assignments', ['contextid' => $cmcontext->id]);
+ $this->assertEquals(1, $count);
+ // Role assigments CONTEXT_BLOCK.
+ role_assign($student->id, $user->id, $blockcontext->id);
+ $count = $DB->count_records('role_assignments', ['contextid' => $blockcontext->id]);
+ $this->assertEquals(1, $count);
+ // Role assigments CONTEXT_USER.
+ role_assign($manager->id, $user->id, $usercontext2->id);
+ $count = $DB->count_records('role_assignments', ['contextid' => $usercontext2->id]);
+ $this->assertEquals(1, $count);
+
+ // Delete data based on CONTEXT_COURSE context.
+ provider::delete_data_for_all_users_in_context($coursecontext);
+ // After deletion, the role_assignments entries for this context should have been deleted.
+ $count = $DB->count_records('role_assignments', ['contextid' => $coursecontext->id]);
+ $this->assertEquals(0, $count);
+ // Check it is not removing data on other contexts.
+ $count = $DB->count_records('role_assignments', ['contextid' => $coursecatcontext->id]);
+ $this->assertEquals(2, $count);
+ $count = $DB->count_records('role_assignments', ['contextid' => $systemcontext->id]);
+ $this->assertEquals(1, $count);
+ $count = $DB->count_records('role_assignments', ['contextid' => $cmcontext->id]);
+ $this->assertEquals(1, $count);
+ // Delete data based on CONTEXT_COURSECAT context.
+ provider::delete_data_for_all_users_in_context($coursecatcontext);
+ // After deletion, the role_assignments entries for this context should have been deleted.
+ $count = $DB->count_records('role_assignments', ['contextid' => $coursecatcontext->id]);
+ $this->assertEquals(0, $count);
+ // Delete data based on CONTEXT_SYSTEM context.
+ provider::delete_data_for_all_users_in_context($systemcontext);
+ // After deletion, the role_assignments entries for this context should have been deleted.
+ $count = $DB->count_records('role_assignments', ['contextid' => $systemcontext->id]);
+ $this->assertEquals(0, $count);
+ // Delete data based on CONTEXT_MODULE context.
+ provider::delete_data_for_all_users_in_context($cmcontext);
+ // After deletion, the role_assignments entries for this context should have been deleted.
+ $count = $DB->count_records('role_assignments', ['contextid' => $cmcontext->id]);
+ $this->assertEquals(0, $count);
+ // Delete data based on CONTEXT_BLOCK context.
+ provider::delete_data_for_all_users_in_context($usercontext2);
+ // After deletion, the role_assignments entries for this context should have been deleted.
+ $count = $DB->count_records('role_assignments', ['contextid' => $usercontext2->id]);
+ $this->assertEquals(0, $count);
+ }
+ /**
+ * Test for provider::delete_data_for_user().
+ */
+ public function test_delete_data_for_user() {
+ global $DB;
+
+ $this->resetAfterTest();
+ $this->setAdminUser();
+ $user = $this->getDataGenerator()->create_user();
+ $user2 = $this->getDataGenerator()->create_user();
+ $usercontext2 = \context_user::instance($user2->id);
+ $user3 = $this->getDataGenerator()->create_user();
+ $usercontext3 = \context_user::instance($user3->id);
+ $course = $this->getDataGenerator()->create_course();
+ $course2 = $this->getDataGenerator()->create_course();
+ $course3 = $this->getDataGenerator()->create_course();
+ $coursecontext = \context_course::instance($course->id);
+ $coursecontext2 = \context_course::instance($course2->id);
+ $coursecontext3 = \context_course::instance($course3->id);
+ $coursecat = $this->getDataGenerator()->create_category();
+ $coursecatcontext = \context_coursecat::instance($coursecat->id);
+ $systemcontext = \context_system::instance();
+ $cm = $this->getDataGenerator()->create_module('chat', ['course' => $course->id]);
+ $cmcontext = \context_module::instance($cm->cmid);
+ $student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
+ $manager = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
+ $block = $this->getDataGenerator()->create_block('online_users');
+ $blockcontext = \context_block::instance($block->id);
+
+ // Role assignments, Where the user is assigned.
+ role_assign($student->id, $user->id, $coursecontext->id);
+ role_assign($student->id, $user->id, $coursecontext2->id);
+ role_assign($student->id, $user->id, $coursecatcontext->id);
+ role_assign($student->id, $user->id, $cmcontext->id);
+ role_assign($student->id, $user->id, $systemcontext->id);
+ role_assign($student->id, $user->id, $blockcontext->id);
+ role_assign($manager->id, $user->id, $usercontext2->id);
+ role_assign($manager->id, $user->id, $usercontext3->id);
+ $count = $DB->count_records('role_assignments', ['userid' => $user->id]);
+ $this->assertEquals(8, $count);
+ // Role assignments, where the user makes assignments.
+ $this->setUser($user);
+ role_assign($student->id, $user2->id, $coursecontext3->id);
+ role_assign($student->id, $user3->id, $coursecontext3->id);
+ $count = $DB->count_records('role_assignments', ['modifierid' => $user->id]);
+ $this->assertEquals(2, $count);
+
+ $contextlist = provider::get_contexts_for_userid($user->id);
+ $approvedcontextlist = new approved_contextlist($user, 'core_role', $contextlist->get_contextids());
+ provider::delete_data_for_user($approvedcontextlist);
+ // After deletion, the role_assignments assigned to the user should have been deleted.
+ $count = $DB->count_records('role_assignments', ['userid' => $user->id]);
+ $this->assertEquals(0, $count);
+ // After deletion, the role_assignments assigned by the user should not have been deleted.
+ $count = $DB->count_records('role_assignments', ['modifierid' => $user->id]);
+ $this->assertEquals(2, $count);
+ }
+ /**
+ * Export for a user with a key against a script where no instance is specified.
+ */
+ public function test_export_user_role_to_cohort() {
+ global $DB;
+
+ $this->resetAfterTest();
+ $this->setAdminUser();
+ // Assign user roles to cohort.
+ $user = $this->getDataGenerator()->create_user();
+ $contextuser = \context_user::instance($user->id);
+ $teacher = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
+ $cohort = $this->getDataGenerator()->create_cohort();
+ $userassignover = $this->getDataGenerator()->create_user();
+ $contextuserassignover = \context_user::instance($userassignover->id);
+ cohort_add_member($cohort->id, $userassignover->id);
+ $this->setAdminUser();
+ $params = (object) array(
+ 'userid' => $user->id,
+ 'roleid' => $teacher->id,
+ 'cohortid' => $cohort->id
+ );
+ api::create_cohort_role_assignment($params);
+ api::sync_all_cohort_roles();
+ $rolesnames = self::get_roles_name();
+ $subcontextteacher = [
+ get_string('privacy:metadata:role_cohortroles', 'core_role'),
+ $rolesnames[$teacher->id]
+ ];
+ // Test User is assigned role teacher to cohort.
+ provider::export_user_role_to_cohort($user->id);
+ $writer = writer::with_context($contextuserassignover);
+ $this->assertTrue($writer->has_any_data());
+ $exported = $writer->get_related_data($subcontextteacher, 'cohortroles');
+ $this->assertEquals($user->id, reset($exported)->userid);
+
+ // Test User is member of a cohort which User2 is assigned to role to this cohort.
+ $user2 = $this->getDataGenerator()->create_user();
+ $cohort2 = $this->getDataGenerator()->create_cohort();
+ cohort_add_member($cohort2->id, $user->id);
+ $params = (object) array(
+ 'userid' => $user2->id,
+ 'roleid' => $teacher->id,
+ 'cohortid' => $cohort2->id
+ );
+ api::create_cohort_role_assignment($params);
+ api::sync_all_cohort_roles();
+ provider::export_user_role_to_cohort($user->id);
+ $writer = writer::with_context($contextuser);
+ $this->assertTrue($writer->has_any_data());
+ $exported = $writer->get_related_data($subcontextteacher, 'cohortroles');
+ $this->assertEquals($user2->id, reset($exported)->userid);
+ }
+ /**
+ * Test for provider::delete_user_role_to_cohort().
+ */
+ public function test_delete_user_role_to_cohort() {
+ global $DB;
+
+ $this->resetAfterTest();
+ $this->setAdminUser();
+ // Assign user roles to cohort.
+ $user = $this->getDataGenerator()->create_user();
+ $user2 = $this->getDataGenerator()->create_user();
+ $user3 = $this->getDataGenerator()->create_user();
+ $user4 = $this->getDataGenerator()->create_user();
+ $teacher = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
+ $cohort = $this->getDataGenerator()->create_cohort();
+ cohort_add_member($cohort->id, $user2->id);
+ cohort_add_member($cohort->id, $user3->id);
+ cohort_add_member($cohort->id, $user4->id);
+ $this->setAdminUser();
+ $params = (object) array(
+ 'userid' => $user->id,
+ 'roleid' => $teacher->id,
+ 'cohortid' => $cohort->id
+ );
+ api::create_cohort_role_assignment($params);
+ api::sync_all_cohort_roles();
+
+ $count = $DB->count_records('role_assignments', ['userid' => $user->id, 'component' => 'tool_cohortroles']);
+ $this->assertEquals(3, $count);
+
+ provider::delete_user_role_to_cohort($user->id);
+ $count = $DB->count_records('role_assignments', ['userid' => $user->id, 'component' => 'tool_cohortroles']);
+ $this->assertEquals(0, $count);
+ }
+ /**
+ * Supoort function to get all the localised roles name
+ * in a simple array for testing.
+ *
+ * @return array Array of name of the roles by roleid.
+ */
+ protected static function get_roles_name() {
+ $roles = role_fix_names(get_all_roles(), \context_system::instance(), ROLENAME_ORIGINAL);
+ $rolesnames = array();
+ foreach ($roles as $role) {
+ $rolesnames[$role->id] = $role->localname;
+ }
+ return $rolesnames;
+ }
+}
\ No newline at end of file
$temp->add(new admin_setting_configexecutable('pathtosassc', new lang_string('pathtosassc', 'admin'), new lang_string('pathtosassc_help', 'admin'), ''));
+ $temp->add(new admin_setting_configcheckbox('forceclean', new lang_string('forceclean', 'core_admin'),
+ new lang_string('forceclean_desc', 'core_admin'), 0));
+
$ADMIN->add('experimental', $temp);
// "debugging" settingpage
$ADMIN->add('development', $temp);
// "Profiling" settingpage (conditionally if the 'xhprof' extension is available only).
- $xhprofenabled = extension_loaded('xhprof') || extension_loaded('tideways');
+ $xhprofenabled = extension_loaded('tideways_xhprof');
+ $xhprofenabled = $xhprofenabled || extension_loaded('tideways');
+ $xhprofenabled = $xhprofenabled || extension_loaded('xhprof');
$temp = new admin_settingpage('profiling', new lang_string('profiling', 'admin'), 'moodle/site:config', !$xhprofenabled);
// Main profiling switch.
$temp->add(new admin_setting_configcheckbox('profilingenabled', new lang_string('profilingenabled', 'admin'), new lang_string('profilingenabled_help', 'admin'), false));
+++ /dev/null
-<?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/>.
-
-/**
- * Adds privacy and policies links to admin tree.
- *
- * @package core_privacy
- * @copyright 2018 Marina Glancy
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-if ($hassiteconfig) {
- // Privacy settings.
- $temp = new admin_settingpage('privacysettings', new lang_string('privacysettings', 'admin'));
-
- $options = array(
- 0 => get_string('no'),
- 1 => get_string('yes')
- );
- $url = new moodle_url('/admin/settings.php?section=supportcontact');
- $url = $url->out();
- $setting = new admin_setting_configselect('agedigitalconsentverification',
- new lang_string('agedigitalconsentverification', 'admin'),
- new lang_string('agedigitalconsentverification_desc', 'admin', $url), 0, $options);
- $setting->set_force_ltr(true);
- $temp->add($setting);
-
- $setting = new admin_setting_agedigitalconsentmap('agedigitalconsentmap',
- new lang_string('ageofdigitalconsentmap', 'admin'),
- new lang_string('ageofdigitalconsentmap_desc', 'admin'),
- // See {@link https://gdpr-info.eu/art-8-gdpr/}.
- implode(PHP_EOL, [
- '*, 16',
- 'AT, 14',
- 'CZ, 13',
- 'DE, 14',
- 'DK, 13',
- 'ES, 13',
- 'FI, 15',
- 'GB, 13',
- 'HU, 14',
- 'IE, 13',
- 'LT, 16',
- 'LU, 16',
- 'NL, 16',
- 'PL, 13',
- 'SE, 13',
- ]),
- PARAM_RAW
- );
- $temp->add($setting);
-
- $ADMIN->add('privacy', $temp);
-
- // Policy settings.
- $temp = new admin_settingpage('policysettings', new lang_string('policysettings', 'admin'));
- $temp->add(new admin_settings_sitepolicy_handler_select('sitepolicyhandler', new lang_string('sitepolicyhandler', 'core_admin'),
- new lang_string('sitepolicyhandler_desc', 'core_admin')));
- $temp->add(new admin_setting_configtext('sitepolicy', new lang_string('sitepolicy', 'core_admin'),
- new lang_string('sitepolicy_help', 'core_admin'), '', PARAM_RAW));
- $temp->add(new admin_setting_configtext('sitepolicyguest', new lang_string('sitepolicyguest', 'core_admin'),
- new lang_string('sitepolicyguest_help', 'core_admin'), (isset($CFG->sitepolicy) ? $CFG->sitepolicy : ''), PARAM_RAW));
-
- $ADMIN->add('privacy', $temp);
-}
$temp->add(new admin_setting_configtext('userquota', new lang_string('userquota', 'admin'),
new lang_string('configuserquota', 'admin', $params), $defaultuserquota, PARAM_INT, 30));
- $temp->add(new admin_setting_configcheckbox('forceclean', new lang_string('forceclean', 'core_admin'),
- new lang_string('forceclean_desc', 'core_admin'), 0));
-
$temp->add(new admin_setting_configcheckbox('allowobjectembed', new lang_string('allowobjectembed', 'admin'), new lang_string('configallowobjectembed', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('enabletrusttext', new lang_string('enabletrusttext', 'admin'), new lang_string('configenabletrusttext', 'admin'), 0));
$temp->add(new admin_setting_configselect('maxeditingtime', new lang_string('maxeditingtime','admin'), new lang_string('configmaxeditingtime','admin'), 1800,
$ADMIN->add('root', new admin_category('location', new lang_string('location','admin')));
$ADMIN->add('root', new admin_category('language', new lang_string('language')));
$ADMIN->add('root', new admin_category('modules', new lang_string('plugins', 'admin')));
-$ADMIN->add('root', new admin_category('privacy', new lang_string('privacyandpolicies', 'admin')));
$ADMIN->add('root', new admin_category('security', new lang_string('security','admin')));
$ADMIN->add('root', new admin_category('appearance', new lang_string('appearance','admin')));
$ADMIN->add('root', new admin_category('frontpage', new lang_string('frontpage','admin')));
$ADMIN->add('users', new admin_category('accounts', new lang_string('accounts', 'admin')));
$ADMIN->add('users', new admin_category('roles', new lang_string('permissions', 'role')));
+$ADMIN->add('users', new admin_category('privacy', new lang_string('privacyandpolicies', 'admin')));
if ($hassiteconfig
or has_capability('moodle/user:create', $systemcontext)
$ADMIN->add('roles', new admin_externalpage('checkpermissions', new lang_string('checkglobalpermissions', 'role'), "$CFG->wwwroot/$CFG->admin/roles/check.php?contextid=".$systemcontext->id, array('moodle/role:assign', 'moodle/role:safeoverride', 'moodle/role:override', 'moodle/role:manage')));
} // end of speedup
+
+// Privacy settings.
+if ($hassiteconfig) {
+ $temp = new admin_settingpage('privacysettings', new lang_string('privacysettings', 'admin'));
+
+ $options = array(
+ 0 => get_string('no'),
+ 1 => get_string('yes')
+ );
+ $url = new moodle_url('/admin/settings.php?section=supportcontact');
+ $url = $url->out();
+ $setting = new admin_setting_configselect('agedigitalconsentverification',
+ new lang_string('agedigitalconsentverification', 'admin'),
+ new lang_string('agedigitalconsentverification_desc', 'admin', $url), 0, $options);
+ $setting->set_force_ltr(true);
+ $temp->add($setting);
+
+ $setting = new admin_setting_agedigitalconsentmap('agedigitalconsentmap',
+ new lang_string('ageofdigitalconsentmap', 'admin'),
+ new lang_string('ageofdigitalconsentmap_desc', 'admin'),
+ // See {@link https://gdpr-info.eu/art-8-gdpr/}.
+ implode(PHP_EOL, [
+ '*, 16',
+ 'AT, 14',
+ 'CZ, 13',
+ 'DE, 14',
+ 'DK, 13',
+ 'ES, 13',
+ 'FI, 15',
+ 'GB, 13',
+ 'HU, 14',
+ 'IE, 13',
+ 'LT, 16',
+ 'LU, 16',
+ 'NL, 16',
+ 'PL, 13',
+ 'SE, 13',
+ ]),
+ PARAM_RAW
+ );
+ $temp->add($setting);
+
+ $ADMIN->add('privacy', $temp);
+
+ // Policy settings.
+ $temp = new admin_settingpage('policysettings', new lang_string('policysettings', 'admin'));
+ $temp->add(new admin_settings_sitepolicy_handler_select('sitepolicyhandler', new lang_string('sitepolicyhandler', 'core_admin'),
+ new lang_string('sitepolicyhandler_desc', 'core_admin')));
+ $temp->add(new admin_setting_configtext('sitepolicy', new lang_string('sitepolicy', 'core_admin'),
+ new lang_string('sitepolicy_help', 'core_admin'), '', PARAM_RAW));
+ $temp->add(new admin_setting_configtext('sitepolicyguest', new lang_string('sitepolicyguest', 'core_admin'),
+ new lang_string('sitepolicyguest_help', 'core_admin'), (isset($CFG->sitepolicy) ? $CFG->sitepolicy : ''), PARAM_RAW));
+
+ $ADMIN->add('privacy', $temp);
+}
\ No newline at end of file
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires = 2017110800; // Requires this Moodle version.
+$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2018050800; // Requires this Moodle version.
$plugin->component = 'tool_analytics'; // Full name of the plugin (used for diagnostics).
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
$plugin->component = 'tool_assignmentupgrade';
-$plugin->dependencies = array('mod_assign' => 2017110800);
+$plugin->dependencies = array('mod_assign' => 2018050800);
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
$plugin->component = 'tool_availabilityconditions';
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX)
-$plugin->requires = 2017110800; // Requires this Moodle version
+$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->requires = 2018050800; // Requires this Moodle version
$plugin->component = 'tool_behat'; // Full name of the plugin (used for diagnostics)
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires = 2017110800; // Requires this Moodle version.
+$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2018050800; // Requires this Moodle version.
$plugin->component = 'tool_capability'; // Full name of the plugin (used for diagnostics).
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires = 2017110800; // Requires this Moodle version.
+$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2018050800; // Requires this Moodle version.
$plugin->component = 'tool_cohortroles'; // Full name of the plugin (used for diagnostics).
$plugin->dependencies = array(
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
$plugin->component = 'tool_customlang'; // Full name of the plugin (used for diagnostics)
}
}
if (!empty($roles)) {
- $privacysettings->add(new admin_setting_configmultiselect('tool_dataprivacy/dporoles',
+ $privacysettings->add(new admin_setting_configmulticheckbox('tool_dataprivacy/dporoles',
new lang_string('dporolemapping', 'tool_dataprivacy'),
new lang_string('dporolemapping_desc', 'tool_dataprivacy'), null, $roles)
);
defined('MOODLE_INTERNAL') || die;
-$plugin->version = 2018040500;
-$plugin->requires = 2018040500; // Moodle 3.5dev (Build 2018031600) and upwards.
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800; // Moodle 3.5dev (Build 2018031600) and upwards.
$plugin->component = 'tool_dataprivacy';
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires = 2017110800; // Requires this Moodle version.
+$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2018050800; // Requires this Moodle version.
$plugin->component = 'tool_dbtransfer'; // Full name of the plugin (used for diagnostics).
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
$plugin->component = 'tool_filetypes';
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
$plugin->component = 'tool_generator';
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX)
-$plugin->requires = 2017110800; // Requires this Moodle version
+$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->requires = 2018050800; // Requires this Moodle version
$plugin->component = 'tool_health'; // Full name of the plugin (used for diagnostics)
$plugin->maturity = MATURITY_ALPHA; // this version's maturity level
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires = 2017110800; // Requires this Moodle version.
+$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2018050800; // Requires this Moodle version.
$plugin->component = 'tool_httpsreplace'; // Full name of the plugin (used for diagnostics).
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX)
-$plugin->requires = 2017110800; // Requires this Moodle version
+$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->requires = 2018050800; // Requires this Moodle version
$plugin->component = 'tool_innodb'; // Full name of the plugin (used for diagnostics)
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'tool_installaddon';
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
$plugin->maturity = MATURITY_STABLE;
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX)
-$plugin->requires = 2017110800; // Requires this Moodle version
+$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->requires = 2018050800; // Requires this Moodle version
$plugin->component = 'tool_langimport'; // Full name of the plugin (used for diagnostics)
--- /dev/null
+<?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/>.
+
+/**
+ * Privacy helper.
+ *
+ * @package tool_log
+ * @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 tool_log\local\privacy;
+defined('MOODLE_INTERNAL') || die();
+
+use core_privacy\local\request\transform;
+
+/**
+ * Privacy helper class.
+ *
+ * @package tool_log
+ * @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 helper {
+
+ /**
+ * Returns an event from a standard record.
+ *
+ * @see \logstore_standard\log\store::get_log_event()
+ * @param object $data Log data.
+ * @return \core\event\base
+ */
+ protected static function restore_event_from_standard_record($data) {
+ $extra = ['origin' => $data->origin, 'ip' => $data->ip, 'realuserid' => $data->realuserid];
+ $data = (array) $data;
+ $id = $data['id'];
+ $data['other'] = unserialize($data['other']);
+ if ($data['other'] === false) {
+ $data['other'] = [];
+ }
+ unset($data['origin']);
+ unset($data['ip']);
+ unset($data['realuserid']);
+ unset($data['id']);
+
+ if (!$event = \core\event\base::restore($data, $extra)) {
+ return null;
+ }
+
+ return $event;
+ }
+
+ /**
+ * Transform a standard log record for a user.
+ *
+ * @param object $record The record.
+ * @param int $userid The user ID.
+ * @return array
+ */
+ public static function transform_standard_log_record_for_userid($record, $userid) {
+
+ // Restore the event to try to get the name, description and other field.
+ $restoredevent = static::restore_event_from_standard_record($record);
+ if ($restoredevent) {
+ $name = $restoredevent->get_name();
+ $description = $restoredevent->get_description();
+ $other = $restoredevent->other;
+
+ } else {
+ $name = $record->eventname;
+ $description = "Unknown event ({$name})";
+ $other = unserialize($record->other);
+ }
+
+ $realuserid = $record->realuserid;
+ $isauthor = $record->userid == $userid;
+ $isrelated = $record->relateduserid == $userid;
+ $isrealuser = $realuserid == $userid;
+ $ismasqueraded = $realuserid !== null && $record->userid != $realuserid;
+ $ismasquerading = $isrealuser && !$isauthor;
+ $isanonymous = $record->anonymous;
+
+ $data = [
+ 'name' => $name,
+ 'description' => $description,
+ 'timecreated' => transform::datetime($record->timecreated),
+ 'ip' => $record->ip,
+ 'origin' => static::transform_origin($record->origin),
+ 'other' => $other ? $other : []
+ ];
+
+ if ($isanonymous) {
+ $data['action_was_done_anonymously'] = transform::yesno($isanonymous);
+ }
+ if ($isauthor || !$isanonymous) {
+ $data['authorid'] = transform::user($record->userid);
+ $data['author_of_the_action_was_you'] = transform::yesno($isauthor);
+ }
+
+ if ($record->relateduserid) {
+ $data['relateduserid'] = transform::user($record->relateduserid);
+ $data['related_user_was_you'] = transform::yesno($isrelated);
+ }
+
+ if ($ismasqueraded) {
+ $data['author_of_the_action_was_masqueraded'] = transform::yesno(true);
+ if ($ismasquerading || !$isanonymous) {
+ $data['masqueradinguserid'] = transform::user($realuserid);
+ $data['masquerading_user_was_you'] = transform::yesno($ismasquerading);
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Transform origin.
+ *
+ * @param string $origin The page request origin.
+ * @return string
+ */
+ public static function transform_origin($origin) {
+ switch ($origin) {
+ case 'cli':
+ case 'restore':
+ case 'web':
+ case 'ws':
+ return get_string('privacy:request:origin:' . $origin, 'tool_log');
+ break;
+ }
+ return $origin;
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Logstore provider interface.
+ *
+ * @package tool_log
+ * @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 tool_log\local\privacy;
+defined('MOODLE_INTERNAL') || die();
+
+use context;
+use core_privacy\local\request\contextlist;
+use core_privacy\local\request\approved_contextlist;
+
+/**
+ * Logstore provider interface.
+ *
+ * Logstore subplugins providers must implement this interface.
+ *
+ * @package tool_log
+ * @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
+ */
+interface logstore_provider extends \core_privacy\local\request\plugin\subplugin_provider {
+
+ /**
+ * 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);
+
+ /**
+ * Export all user data for the specified user, in the specified contexts.
+ *
+ * @param approved_contextlist $contextlist The approved contexts to export information for.
+ * @return void
+ */
+ public static function export_user_data(approved_contextlist $contextlist);
+
+ /**
+ * Delete all data for all users in the specified context.
+ *
+ * @param context $context The specific context to delete data for.
+ * @return void
+ */
+ public static function delete_data_for_all_users_in_context(context $context);
+
+ /**
+ * Delete all user data for the specified user, in the specified contexts.
+ *
+ * @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
+ * @return void
+ */
+ public static function delete_data_for_user(approved_contextlist $contextlist);
+
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Moodle database: export and delete.
+ *
+ * @package tool_log
+ * @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 tool_log\local\privacy;
+defined('MOODLE_INTERNAL') || die();
+
+use context;
+use core_privacy\local\request\approved_contextlist;
+use core_privacy\local\request\writer;
+
+/**
+ * Moodle database: export and delete trait.
+ *
+ * This is to be used with logstores which use a database and table with the same columns
+ * as the core plugin 'logstore_standard'.
+ *
+ * This trait expects the following methods to be present in the object:
+ *
+ * - public static function get_database_and_table(): [moodle_database|null, string|null]
+ * - public static function get_export_subcontext(): []
+ *
+ * @package tool_log
+ * @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
+ */
+trait moodle_database_export_and_delete {
+
+ /**
+ * Export all user data for the specified user, in the specified contexts.
+ *
+ * @param approved_contextlist $contextlist The approved contexts to export information for.
+ */
+ public static function export_user_data(approved_contextlist $contextlist) {
+ list($db, $table) = static::get_database_and_table();
+ if (!$db || !$table) {
+ return;
+ }
+
+ $userid = $contextlist->get_user()->id;
+ list($insql, $inparams) = $db->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
+
+ $sql = "(userid = :userid1 OR relateduserid = :userid2 OR realuserid = :userid3) AND contextid $insql";
+ $params = array_merge($inparams, [
+ 'userid1' => $userid,
+ 'userid2' => $userid,
+ 'userid3' => $userid,
+ ]);
+
+ $path = static::get_export_subcontext();
+ $flush = function($lastcontextid, $data) use ($path) {
+ $context = context::instance_by_id($lastcontextid);
+ writer::with_context($context)->export_data($path, (object) ['logs' => $data]);
+ };
+
+ $lastcontextid = null;
+ $data = [];
+ $recordset = $db->get_recordset_select($table, $sql, $params, 'contextid, timecreated, id');
+ foreach ($recordset as $record) {
+ if ($lastcontextid && $lastcontextid != $record->contextid) {
+ $flush($lastcontextid, $data);
+ $data = [];
+ }
+ $data[] = helper::transform_standard_log_record_for_userid($record, $userid);
+ $lastcontextid = $record->contextid;
+ }
+ if ($lastcontextid) {
+ $flush($lastcontextid, $data);
+ }
+ $recordset->close();
+ }
+
+ /**
+ * Delete all data for all users in the specified context.
+ *
+ * @param context $context The specific context to delete data for.
+ */
+ public static function delete_data_for_all_users_in_context(context $context) {
+ list($db, $table) = static::get_database_and_table();
+ if (!$db || !$table) {
+ return;
+ }
+ $db->delete_records($table, ['contextid' => $context->id]);
+ }
+
+ /**
+ * Delete all user data for the specified user, in the specified contexts.
+ *
+ * @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
+ */
+ public static function delete_data_for_user(approved_contextlist $contextlist) {
+ list($db, $table) = static::get_database_and_table();
+ if (!$db || !$table) {
+ return;
+ }
+ list($insql, $inparams) = $db->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
+ $params = array_merge($inparams, ['userid' => $contextlist->get_user()->id]);
+ $db->delete_records_select($table, "userid = :userid AND contextid $insql", $params);
+ }
+
+}
--- /dev/null
+<?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 tool_log
+ * @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 tool_log\privacy;
+defined('MOODLE_INTERNAL') || die();
+
+use context;
+use core_privacy\local\metadata\collection;
+use core_privacy\local\request\approved_contextlist;
+use core_privacy\local\request\transform;
+use core_privacy\local\request\writer;
+use tool_log\log\manager;
+
+/**
+ * Data provider class.
+ *
+ * @package tool_log
+ * @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,
+ \core_privacy\local\request\subsystem\provider {
+
+ /**
+ * 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_plugintype_link('logstore', [], 'privacy:metadata:logstore');
+ return $collection;
+ }
+
+ /**
+ * Get the list of contexts that contain user information for the specified user.
+ *
+ * @param int $userid The user to search.
+ * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
+ */
+ public static function get_contexts_for_userid(int $userid) : \core_privacy\local\request\contextlist {
+ $contextlist = new \core_privacy\local\request\contextlist();
+ static::call_subplugins_method_with_args('add_contexts_for_userid', [$contextlist, $userid]);
+ return $contextlist;
+ }
+
+ /**
+ * Export all user data for the specified user, in the specified contexts.
+ *
+ * @param approved_contextlist $contextlist The approved contexts to export information for.
+ */
+ public static function export_user_data(approved_contextlist $contextlist) {
+ static::call_subplugins_method_with_args('export_user_data', [$contextlist]);
+ }
+
+ /**
+ * Delete all data for all users in the specified context.
+ *
+ * @param context $context The specific context to delete data for.
+ */
+ public static function delete_data_for_all_users_in_context(context $context) {
+ static::call_subplugins_method_with_args('delete_data_for_all_users_in_context', [$context]);
+ }
+
+ /**
+ * Delete all user data for the specified user, in the specified contexts.
+ *
+ * @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
+ */
+ public static function delete_data_for_user(approved_contextlist $contextlist) {
+ static::call_subplugins_method_with_args('delete_data_for_user', [$contextlist]);
+ }
+
+ /**
+ * Invoke the subplugins method with arguments.
+ *
+ * @param string $method The method name.
+ * @param array $args The arguments.
+ * @return void
+ */
+ protected static function call_subplugins_method_with_args($method, array $args = []) {
+ $interface = \tool_log\local\privacy\logstore_provider::class;
+ \core_privacy\manager::plugintype_class_callback('logstore', $interface, $method, $args);
+ }
+
+}
$string['configlogplugins'] = 'Please enable all required plugins and arrange them in appropriate order.';
$string['logging'] = 'Logging';
$string['managelogging'] = 'Manage log stores';
-$string['reportssupported'] = 'Reports supported';
$string['pluginname'] = 'Log store manager';
+$string['privacy:metadata:logstore'] = 'The log stores';
+$string['privacy:path:logs'] = 'Logs';
+$string['privacy:request:origin:cli'] = 'Command line tool';
+$string['privacy:request:origin:restore'] = 'Backup being restored';
+$string['privacy:request:origin:web'] = 'Standard web request';
+$string['privacy:request:origin:ws'] = 'Mobile app or web service';
+$string['reportssupported'] = 'Reports supported';
$string['subplugintype_logstore'] = 'Log store';
$string['subplugintype_logstore_plural'] = 'Log stores';
--- /dev/null
+<?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 {
+
+ 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, []);
+ }
+
+ /**
+ * 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')];
+ }
+}
$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 at which 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';
--- /dev/null
+<?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
+ */
+
+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 logstore_database_privacy_testcase extends provider_testcase {
+
+ public function setUp() {
+ 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]);
+ }
+
+ 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]));
+ }
+
+ 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();
+