--- /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
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)
* @return \context|false
*/
protected function delete_expired_context(\core_privacy\manager $privacymanager, \tool_dataprivacy\expired_context $expiredctx) {
- if (!$context = parent::delete_expired_context($privacymanager, $expiredctx)) {
+ $context = \context::instance_by_id($expiredctx->get('contextid'), IGNORE_MISSING);
+ if (!$context) {
+ api::delete_expired_context($expiredctx->get('contextid'));
return false;
}
- // Delete the user.
+ if (!PHPUNIT_TEST) {
+ mtrace('Deleting context ' . $context->id . ' - ' .
+ shorten_text($context->get_context_name(true, true)));
+ }
+
+ // To ensure that all user data is deleted, instead of deleting by context, we run through and collect any stray
+ // contexts for the user that may still exist and call delete_data_for_user().
$user = \core_user::get_user($context->instanceid, '*', MUST_EXIST);
+ $approvedlistcollection = new \core_privacy\local\request\contextlist_collection($user->id);
+ $contextlistcollection = $privacymanager->get_contexts_for_userid($user->id);
+
+ foreach ($contextlistcollection as $contextlist) {
+ $approvedlistcollection->add_contextlist(new \core_privacy\local\request\approved_contextlist(
+ $user,
+ $contextlist->get_component(),
+ $contextlist->get_contextids()
+ ));
+ }
+
+ $privacymanager->delete_data_for_user($approvedlistcollection);
+ api::set_expired_context_status($expiredctx, expired_context::STATUS_CLEANED);
+
+ // Delete the user.
delete_user($user);
return $context;
$data->newdatarequesturl = new moodle_url('/admin/tool/dataprivacy/createdatarequest.php');
$data->newdatarequesturl->param('manage', true);
+ if (!is_https()) {
+ $httpwarningmessage = get_string('httpwarning', 'tool_dataprivacy');
+ $data->httpsite = array('message' => $httpwarningmessage, 'announce' => 1);
+ }
+
$requests = [];
foreach ($this->requests as $request) {
$requestid = $request->get('id');
$data = new stdClass();
$data->newdatarequesturl = new moodle_url('/admin/tool/dataprivacy/createdatarequest.php');
+ if (!is_https()) {
+ $httpwarningmessage = get_string('httpwarning', 'tool_dataprivacy');
+ $data->httpsite = array('message' => $httpwarningmessage, 'announce' => 1);
+ }
+
$requests = [];
foreach ($this->requests as $request) {
$requestid = $request->get('id');
$string['confirmcontextdeletion'] = 'Do you really want to confirm the deletion of the selected contexts? This will also delete all of the user data for their respective sub-contexts.';
$string['confirmdenial'] = 'Do you really want deny this data request?';
$string['contactdataprotectionofficer'] = 'Contact Data Protection Officer';
-$string['contactdataprotectionofficer_desc'] = 'Enabling this feature will provide a link for users to contact the site\'s Data Protection Officer through this site. This link will be shown on their profile page, and on the site\'s privacy policy page, as well. The link leads to a form in which the user can make a data request to the Data Protection Officer.';
+$string['contactdataprotectionofficer_desc'] = 'If enabled, users will be able to contact the Data Protection Officer and make a data request via a link on their profile page.';
$string['contextlevelname10'] = 'Site';
$string['contextlevelname30'] = 'Users';
$string['contextlevelname40'] = 'Course categories';
$string['contextlevelname70'] = 'Activity modules';
$string['contextlevelname80'] = 'Blocks';
$string['contextpurposecategorysaved'] = 'Purpose and category saved.';
-$string['contactdpoviaprivacypolicy'] = 'Please contact the site\'s Data Protection Officer as described in the Privacy Policy';
+$string['contactdpoviaprivacypolicy'] = 'Please contact the Data Protection Officer as described in the privacy policy.';
$string['createcategory'] = 'Create data category';
$string['createpurpose'] = 'Create data purpose';
$string['datadeletion'] = 'Data deletion';
$string['datadeletionpagehelp'] = 'This page lists the contexts that are already past their retention period and need to be confirmed for user data deletion. Once the selected contexts have been confirmed for deletion, the user data related to these contexts will be deleted on the next execution of the "Delete expired contexts" scheduled task.';
-$string['dataprivacy:makedatarequestsforchildren'] = 'Make data requests for children';
+$string['dataprivacy:makedatarequestsforchildren'] = 'Make data requests for minors';
$string['dataprivacy:managedatarequests'] = 'Manage data requests';
$string['dataprivacy:managedataregistry'] = 'Manage data registry';
$string['dataregistry'] = 'Data registry';
$string['denyrequest'] = 'Deny request';
$string['download'] = 'Download';
$string['dporolemapping'] = 'Data Protection Officer role mapping';
-$string['dporolemapping_desc'] = 'Select one or more roles that map to the Data Protection Officer role. Users with these roles will be able to manage data requests. This requires the selected role(s) to have the capability \'tool/dataprivacy:managedatarequests\'';
+$string['dporolemapping_desc'] = 'The Data Protection Officer can manage data requests. The capability tool/dataprivacy:managedatarequests must be allowed for a role to be listed as a Data Protection Officer role mapping option.';
$string['editcategories'] = 'Edit categories';
$string['editcategory'] = 'Edit category';
$string['editcategories'] = 'Edit categories';
$string['gdpr_art_9_2_j_description'] = 'Processing is necessary for archiving purposes in the public interest, scientific or historical research purposes or statistical purposes in accordance with Article 89(1) based on Union or Member State law which shall be proportionate to the aim pursued, respect the essence of the right to data protection and provide for suitable and specific measures to safeguard the fundamental rights and the interests of the data subject';
$string['gdpr_art_9_2_j_name'] = 'Public interest, or scientific/historical/statistical research (GDPR Art. 9.2(j))';
$string['hide'] = 'Collapse all';
+$string['httpwarning'] = 'Any data downloaded from this site may not be encrypted. Please contact your system administrator and request that they install SSL on this site.';
$string['inherit'] = 'Inherit';
$string['lawfulbases'] = 'Lawful bases';
$string['lawfulbases_help'] = 'Select at least one option that will serve as the lawful basis for processing personal data. For details on these lawful bases, please see <a href="https://gdpr-info.eu/art-6-gdpr/" target="_blank">GDPR Art. 6.1</a>';
$string['requestactions'] = 'Actions';
$string['requestby'] = 'Requested by';
$string['requestcomments'] = 'Comments';
-$string['requestcomments_help'] = 'Please feel free to provide more details about your request';
+$string['requestcomments_help'] = 'This box enables you to enter any further details about your data request.';
$string['requestemailintro'] = 'You have received a data request:';
$string['requestfor'] = 'Requesting for';
$string['requeststatus'] = 'Status';
.nav-pills .nav-pills {
margin-left: 1rem;
}
-
+.data-registry > .top-nav > * {
+ margin-right: 0.5rem;
+}
/*Extra attribute selection to have preference over bs2's .moodle-actionmenu[data-enhance] */
.data-registry > .top-nav > .singlebutton,
.data-registry > .top-nav > .moodle-actionmenu[data-owner='dataregistry-actions'] {
}
}}
<div class="data-registry">
- <div class="top-nav">
+ <div class="top-nav d-flex">
{{#defaultsbutton}}
{{> core/action_link}}
{{/defaultsbutton}}
}
}}
+{{#httpsite}}
+ {{> core/notification_warning}}
+{{/httpsite}}
+
<div data-region="datarequests">
<div class="m-t-1 m-b-1">
<a href="{{newdatarequesturl}}" class="btn btn-primary" data-action="new-request">
}
}}
+{{#httpsite}}
+ {{> core/notification_warning}}
+{{/httpsite}}
+
<div data-region="datarequests">
<div class="m-t-1 m-b-1">
<a href="{{newdatarequesturl}}" class="btn btn-primary" data-action="new-request">
$this->getDataGenerator()->enrol_user($user3->id, $course2->id, 'student');
$this->getDataGenerator()->enrol_user($user4->id, $course3->id, 'student');
+ // Add an activity and some data for user 2.
+ $assignmod = $this->getDataGenerator()->create_module('assign', ['course' => $course2->id]);
+ $data = (object) [
+ 'assignment' => $assignmod->id,
+ 'userid' => $user2->id,
+ 'timecreated' => time(),
+ 'timemodified' => time(),
+ 'status' => 'new',
+ 'groupid' => 0,
+ 'attemptnumber' => 0,
+ 'latest' => 1,
+ ];
+ $DB->insert_record('assign_submission', $data);
+ // We should have one record in the assign submission table.
+ $this->assertEquals(1, $DB->count_records('assign_submission'));
+
// Users without lastaccess are skipped as well as users enroled in courses with no end date.
$expired = new \tool_dataprivacy\expired_user_contexts();
$numexpired = $expired->flag_expired();
$deleted = $expired->delete();
$this->assertEquals(0, $deleted);
+ // No user data left in mod_assign.
+ $this->assertEquals(0, $DB->count_records('assign_submission'));
+
// The user is deleted.
$deleteduser = \core_user::get_user($user2->id, 'id, deleted', IGNORE_MISSING);
$this->assertEquals(1, $deleteduser->deleted);
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)
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 = 'logstore_database'; // 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 = 'logstore_legacy'; // 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 = 'logstore_standard'; // 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_log'; // 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_lp'; // 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_lpimportcsv'; // Full name of the plugin (used for diagnostics).
-$plugin->dependencies = array('tool_lp' => 2017110800);
+$plugin->dependencies = array('tool_lp' => 2018050800);
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_lpmigrate'; // Full name of the plugin (used for diagnostics).
$plugin->dependencies = array(
'tool_lp' => ANY_VERSION
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
$plugin->component = 'tool_messageinbound';
*/
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111301; // 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_mobile'; // Full name of the plugin (used for diagnostics).
$plugin->dependencies = array(
- 'webservice_rest' => 2017110800
+ 'webservice_rest' => 2018050800
);
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_monitor'; // 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_multilangupgrade'; // 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_oauth2'; // 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_phpunit'; // Full name of the plugin (used for diagnostics)
$data = (object) [
'pluginbaseurl' => (new moodle_url('/admin/tool/policy'))->out(false),
'returnurl' => $this->returnurl ? (new moodle_url($this->returnurl))->out(false) : null,
- 'editurl' => ($this->manage && $this->policy->status != policy_version::STATUS_ARCHIVED) ?
- (new moodle_url('/admin/tool/policy/editpolicydoc.php',
- ['policyid' => $this->policy->policyid, 'versionid' => $this->policy->id]))->out(false) : null,
'numpolicy' => $this->numpolicy ? : null,
'totalpolicies' => $this->totalpolicies ? : null,
];
+ if ($this->manage && $this->policy->status != policy_version::STATUS_ARCHIVED) {
+ $paramsurl = ['policyid' => $this->policy->policyid, 'versionid' => $this->policy->id];
+ $data->editurl = (new moodle_url('/admin/tool/policy/editpolicydoc.php', $paramsurl))->out(false);
+ }
$data->policy = clone($this->policy);
defined('MOODLE_INTERNAL') || die();
-$string['acceptanceacknowledgement'] = 'I acknowledge that I have received the user\'s request to consent on the abovementioned policy on behalf of the user.';
+$string['acceptanceacknowledgement'] = 'I acknowledge that I have received a request to give consent on behalf of user(s).';
$string['acceptancecount'] = '{$a->agreedcount} of {$a->policiescount}';
$string['acceptancenote'] = 'Remarks';
$string['acceptancepolicies'] = 'Policies';
-$string['acceptancessavedsucessfully'] = 'The agreements has been saved successfully.';
+$string['acceptancessavedsucessfully'] = 'The agreements have been saved successfully.';
$string['acceptancestatusoverall'] = 'Overall';
$string['acceptanceusers'] = 'Users';
$string['actions'] = 'Actions';
$string['activate'] = 'Set status to "Active"';
$string['activating'] = 'Activating a policy';
-$string['activateconfirm'] = '<p>You are about to activate policy <em>\'{$a->name}\'</em> and make the version <em>\'{$a->revision}\'</em> the current one.</p><p>All users will be required to accept this new policy version to be able to use the site.</p>';
+$string['activateconfirm'] = '<p>You are about to activate policy <em>\'{$a->name}\'</em> and make the version <em>\'{$a->revision}\'</em> the current one.</p><p>All users will be required to agree to this new policy version to be able to use the site.</p>';
$string['activateconfirmyes'] = 'Activate';
$string['agreed'] = 'Agreed';
$string['agreedby'] = 'Agreed by';
-$string['agreedno'] = 'Not agreed';
-$string['agreednowithlink'] = 'Not agreed, click to agree to "{$a}"';
-$string['agreednowithlinkall'] = 'Not agreed, click to agree to all policies';
+$string['agreedno'] = 'Consent not given';
+$string['agreednowithlink'] = 'Consent not given; click to give consent on behalf of user for {$a}';
+$string['agreednowithlinkall'] = 'Consent not given; click to give consent on behalf of user for all policies';
$string['agreedon'] = 'Agreed on';
$string['agreedyes'] = 'Agreed';
-$string['agreedyesonbehalf'] = 'Agreed on behalf of';
-$string['agreedyesonbehalfwithlink'] = 'Agreed on behalf of, click to withdraw consent to "{$a}"';
-$string['agreedyesonbehalfwithlinkall'] = 'Agreed on behalf of, click to withdraw consent to all policies"';
-$string['agreedyeswithlink'] = 'Agreed, click to withdraw consent to "{$a}"';
-$string['agreedyeswithlinkall'] = 'Agreed, click to withdraw consent to all policies';
+$string['agreedyesonbehalf'] = 'Consent given on behalf of user';
+$string['agreedyesonbehalfwithlink'] = 'Consent given on behalf of user; click to withdraw user consent for {$a}';
+$string['agreedyesonbehalfwithlinkall'] = 'Consent given on behalf of user; click to withdraw user consent for all policies';
+$string['agreedyeswithlink'] = 'Consent given; click to withdraw user consent for {$a}';
+$string['agreedyeswithlinkall'] = 'Consent given; click to withdraw user consent for all policies';
$string['agreepolicies'] = 'Please agree to the following policies';
$string['backtotop'] = 'Back to top';
$string['consentbulk'] = 'Consent';
-$string['consentdetails'] = 'Agree on behalf of the user';
+$string['consentdetails'] = 'Give consent on behalf of user';
$string['consentpagetitle'] = 'Consent';
$string['contactdpo'] = 'For questions regarding the policies please contact the Data Protection Officer.';
$string['dataproc'] = 'Personal data processing';
$string['editingpolicydocument'] = 'Editing policy';
$string['errorpolicyversionnotfound'] = 'There isn\'t any policy version with this identifier.';
$string['errorsaveasdraft'] = 'Minor change can not be saved as draft';
-$string['errorusercantviewpolicyversion'] = 'The user hasn\'t access to this policy version.';
+$string['errorusercantviewpolicyversion'] = 'The user doesn\'t have access to this policy version.';
$string['event_acceptance_created'] = 'User policy agreement created';
$string['event_acceptance_updated'] = 'User policy agreement updated';
$string['filtercapabilityno'] = 'Permission: Can not agree';
$string['guestconsent:continue'] = 'Continue';
$string['guestconsentmessage'] = 'If you continue browsing this website, you agree to our policies:';
$string['iagree'] = 'I agree to the {$a}';
-$string['iagreetothepolicy'] = 'Agree';
+$string['iagreetothepolicy'] = 'Give consent on behalf of user';
$string['inactivate'] = 'Set status to "Inactive"';
$string['inactivating'] = 'Inactivating a policy';
-$string['inactivatingconfirm'] = '<p>You are about to inactivate policy <em>\'{$a->name}\'</em> version <em>\'{$a->revision}\'</em>.</p><p>The policy will not apply until some version is made the current one.</p>';
+$string['inactivatingconfirm'] = '<p>You are about to inactivate policy <em>\'{$a->name}\'</em> version <em>\'{$a->revision}\'</em>.</p>';
$string['inactivatingconfirmyes'] = 'Inactivate';
$string['invalidversionid'] = 'There is no policy with this identifier!';
-$string['irevokethepolicy'] = 'Withdraw consent';
+$string['irevokethepolicy'] = 'Withdraw user consent';
$string['minorchange'] = 'Minor change';
-$string['minorchangeinfo'] = 'Minor changes do not amend the meaning of the policy text, terms or conditions. Users do not need to reconfirm their consent.';
+$string['minorchangeinfo'] = 'A minor change does not alter the meaning of the policy. Users are not required to agree to the policy again if the edit is marked as a minor change.';
$string['managepolicies'] = 'Manage policies';
$string['movedown'] = 'Move down';
$string['moveup'] = 'Move up';
$string['nopermissiontoagreedocs_desc'] = 'Sorry, you do not have the required permissions to agree to the policies.<br />You will not be able to use this site until the following policies are agreed:';
$string['nopermissiontoagreedocsbehalf'] = 'No permission to agree to the policies on behalf of this user';
$string['nopermissiontoagreedocsbehalf_desc'] = 'Sorry, you do not have the required permission to agree to the following policies on behalf of {$a}:';
-$string['nopermissiontoagreedocscontact'] = 'For further assistance, please contact the following person:';
+$string['nopermissiontoagreedocscontact'] = 'For further assistance, please contact';
$string['nopermissiontoviewpolicyversion'] = 'You do not have permissions to view this policy version.';
$string['nopolicies'] = 'There are no policies for registered users with an active version.';
$string['selectpolicyandversion'] = 'Use the filter above to select policy and/or version';
$string['pluginname'] = 'Policies';
$string['policiesagreements'] = 'Policies and agreements';
$string['policy:accept'] = 'Agree to the policies';
-$string['policy:acceptbehalf'] = 'Agree to the policies on someone else\'s behalf';
+$string['policy:acceptbehalf'] = 'Give consent for policies on someone else\'s behalf';
$string['policy:managedocs'] = 'Manage policies';
$string['policy:viewacceptances'] = 'View user agreement reports';
$string['policydocaudience'] = 'User consent';
$string['policydoctype1'] = 'Privacy policy';
$string['policydoctype2'] = 'Third parties policy';
$string['policydoctype99'] = 'Other policy';
-$string['policyversionacceptedinbehalf'] = 'This policy version has been agreed to by another user on behalf of you.';
-$string['policyversionacceptedinotherlang'] = 'This policy version has been agreed to in a different language.';
+$string['policyversionacceptedinbehalf'] = 'Consent for this policy has been given on your behalf.';
+$string['policyversionacceptedinotherlang'] = 'Consent for this policy version has been given in a different language.';
$string['previousversions'] = '{$a} previous versions';
-$string['privacy:metadata:acceptances'] = 'Information from policies agreements made by the users of this site.';
+$string['privacy:metadata:acceptances'] = 'Information from policy agreements made by site users';
$string['privacy:metadata:acceptances:policyversionid'] = 'The ID of the accepted version policy.';
$string['privacy:metadata:acceptances:userid'] = 'The ID of the user who agreed to the policy.';
$string['privacy:metadata:acceptances:status'] = 'The status of the agreement: 0 if not accepted; 1 otherwise.';
$string['privacy:metadata:acceptances:lang'] = 'The current language displayed when the policy is accepted.';
$string['privacy:metadata:acceptances:usermodified'] = 'The ID of the user accepting the policy, if made on behalf of another user.';
-$string['privacy:metadata:acceptances:timecreated'] = 'The timestamp indicating when the agreement was made by the user.';
-$string['privacy:metadata:acceptances:timemodified'] = 'The timestamp indicating when the agreement was modified by the user.';
-$string['privacy:metadata:acceptances:note'] = 'Any comments added by the user who agree to policies on behalf of another.';
+$string['privacy:metadata:acceptances:timecreated'] = 'The time when the user agreed to the policy';
+$string['privacy:metadata:acceptances:timemodified'] = 'The time when the user modified their agreement';
+$string['privacy:metadata:acceptances:note'] = 'Any comments added by a user when giving consent on behalf of another user';
$string['privacysettings'] = 'Privacy settings';
$string['readpolicy'] = 'Please read our {$a}';
-$string['refertofullpolicytext'] = 'Please refer to the full {$a} text if you would like to review.';
-$string['revokeacknowledgement'] = 'I acknowledge that I have received the user\'s request to withdraw consent on the abovementioned policy on behalf of the user.';
-$string['revokedetails'] = 'Withdraw user\'s consent';
+$string['refertofullpolicytext'] = 'Please refer to the full {$a} if you would like to review the text.';
+$string['revokeacknowledgement'] = 'I acknowledge that I have received a request to withdraw consent on behalf of user(s).';
+$string['revokedetails'] = 'Withdraw user consent';
$string['save'] = 'Save';
$string['saveasdraft'] = 'Save as draft';
$string['selectuser'] = 'Select user {$a}';
-$string['selectusersforconsent'] = 'Select users to give consent for';
-$string['settodraft'] = 'Create a new "Draft"';
+$string['selectusersforconsent'] = 'Select users to give consent on behalf of';
+$string['settodraft'] = 'Create a new draft';
$string['status'] = 'Policy status';
-$string['statusinfo'] = 'An active policy will require consent from new users. Existing users will need to consent to this policy on their next logged in session.';
+$string['statusinfo'] = 'A policy with \'Active\' status requires users to give their consent, either when they first log in, or in the case of existing users when they next log in.';
$string['status0'] = 'Draft';
$string['status1'] = 'Active';
$string['status2'] = 'Inactive';
And I navigate to "Users > Privacy and policies > User agreements" in site administration
And "Agreed" "icon" should exist in the "User One" "table_row"
And "Agreed" "icon" should exist in the "Max Manager" "table_row"
- And "Not agreed" "icon" should exist in the "User Two" "table_row"
+ And "Consent not given" "icon" should exist in the "User Two" "table_row"
Scenario: Agree on behalf of another user as a manager, single policy, javascript off
Given I log in as "admin"
And I press "Next"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "1 of 4 (25%)" "link" in the "This site policy" "table_row"
- And I click on "Not agreed" "link" in the "User One" "table_row"
- Then I should see "Agree on behalf of the user"
+ And I click on "Consent not given" "link" in the "User One" "table_row"
+ Then I should see "Give consent on behalf of user"
And I should see "User One"
And I should see "This site policy"
- And I should see "I acknowledge that I have received the user's request to consent on the abovementioned policy on behalf of the user."
+ And I should see "I acknowledge that I have received a request to give consent on behalf of user(s)."
And I set the field "Remarks" to "Consent received from a parent"
- And I press "Agree"
- And "Agreed on behalf of" "icon" should exist in the "User One" "table_row"
+ And I press "Give consent on behalf of user"
+ And "Consent given on behalf of user" "icon" should exist in the "User One" "table_row"
And "Max Manager" "link" should exist in the "User One" "table_row"
And "Consent received from a parent" "text" should exist in the "User One" "table_row"
- And "Not agreed" "icon" should exist in the "User Two" "table_row"
+ And "Consent not given" "icon" should exist in the "User Two" "table_row"
@javascript
Scenario: Agree on behalf of another user as a manager, single policy, javascript on
And I press "Next"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "1 of 4 (25%)" "link" in the "This site policy" "table_row"
- And I click on "Not agreed" "link" in the "User One" "table_row"
- Then I should see "Agree on behalf of the user"
+ And I click on "Consent not given" "link" in the "User One" "table_row"
+ Then I should see "Give consent on behalf of user"
And I should see "User One"
And I should see "This site policy"
- And I should see "I acknowledge that I have received the user's request to consent on the abovementioned policy on behalf of the user."
+ And I should see "I acknowledge that I have received a request to give consent on behalf of user(s)."
And I set the field "Remarks" to "Consent received from a parent"
- And I press "Agree"
- And "Agreed on behalf of" "icon" should exist in the "User One" "table_row"
+ And I press "Give consent on behalf of user"
+ And "Consent given on behalf of user" "icon" should exist in the "User One" "table_row"
And "Max Manager" "link" should exist in the "User One" "table_row"
And "Consent received from a parent" "text" should exist in the "User One" "table_row"
- And "Not agreed" "icon" should exist in the "User Two" "table_row"
+ And "Consent not given" "icon" should exist in the "User Two" "table_row"
Scenario: View acceptances made by users on their own, multiple policies
Given I log in as "admin"
And I press "Next"
And I navigate to "Users > Privacy and policies > User agreements" in site administration
And "Agreed" "icon" should exist in the "User One" "table_row"
- And "Not agreed" "icon" should not exist in the "User One" "table_row"
+ And "Consent not given" "icon" should not exist in the "User One" "table_row"
And "Agreed" "icon" should exist in the "Max Manager" "table_row"
- And "Not agreed" "icon" should exist in the "User Two" "table_row"
+ And "Consent not given" "icon" should exist in the "User Two" "table_row"
And "Agreed" "icon" should not exist in the "User Two" "table_row"
And I click on "2 of 2" "link" in the "User One" "table_row"
And "Agreed" "icon" should exist in the "This site policy" "table_row"
And I am on site homepage
And I navigate to "Users > Privacy and policies > User agreements" in site administration
And I click on "0 of 2" "link" in the "User Two" "table_row"
- And "Not agreed" "icon" should exist in the "This site policy" "table_row"
- And "Not agreed" "icon" should exist in the "This privacy policy" "table_row"
+ And "Consent not given" "icon" should exist in the "This site policy" "table_row"
+ And "Consent not given" "icon" should exist in the "This privacy policy" "table_row"
Scenario: Agree on behalf of another user as a manager, multiple policies, javascript off
Given I log in as "admin"
And I set the field "I agree to the This privacy policy" to "1"
And I press "Next"
And I navigate to "Users > Privacy and policies > User agreements" in site administration
- And I click on "Not agreed, click to agree to \"This site policy\"" "link" in the "User One" "table_row"
- Then I should see "Agree on behalf of the user"
+ And I click on "Consent not given; click to give consent on behalf of user for This site policy" "link" in the "User One" "table_row"
+ Then I should see "Give consent on behalf of user"
And I should see "User One"
And I should see "This site policy"
- And I should see "I acknowledge that I have received the user's request to consent on the abovementioned policy on behalf of the user."
+ And I should see "I acknowledge that I have received a request to give consent on behalf of user(s)."
And I set the field "Remarks" to "Consent received from a parent"
- And I press "Agree"
- And "Agreed on behalf of" "icon" should exist in the "User One" "table_row"
- And "Not agreed, click to agree to \"This privacy policy\"" "icon" should exist in the "User One" "table_row"
+ And I press "Give consent on behalf of user"
+ And "Consent given on behalf of user" "icon" should exist in the "User One" "table_row"
+ And "Consent not given; click to give consent on behalf of user for This privacy policy" "icon" should exist in the "User One" "table_row"
And I click on "1 of 2" "link" in the "User One" "table_row"
- And "Agreed on behalf of" "icon" should exist in the "This site policy" "table_row"
+ And "Consent given on behalf of user" "icon" should exist in the "This site policy" "table_row"
And "Max Manager" "link" should exist in the "This site policy" "table_row"
And "Consent received from a parent" "text" should exist in the "This site policy" "table_row"
- And "Not agreed" "icon" should exist in the "This privacy policy" "table_row"
+ And "Consent not given" "icon" should exist in the "This privacy policy" "table_row"
@javascript
Scenario: Agree on behalf of another user as a manager, multiple policies, javascript on
And I set the field "I agree to the This privacy policy" to "1"
And I press "Next"
And I navigate to "Users > Privacy and policies > User agreements" in site administration
- And I click on "Not agreed, click to agree to \"This site policy\"" "link" in the "User One" "table_row"
- Then I should see "Agree on behalf of the user"
+ And I click on "Consent not given; click to give consent on behalf of user for This site policy" "link" in the "User One" "table_row"
+ Then I should see "Give consent on behalf of user"
And I should see "User One"
And I should see "This site policy"
- And I should see "I acknowledge that I have received the user's request to consent on the abovementioned policy on behalf of the user."
+ And I should see "I acknowledge that I have received a request to give consent on behalf of user(s)."
And I set the field "Remarks" to "Consent received from a parent"
- And I press "Agree"
- And "Agreed on behalf of" "icon" should exist in the "User One" "table_row"
- And "Not agreed, click to agree to \"This privacy policy\"" "icon" should exist in the "User One" "table_row"
+ And I press "Give consent on behalf of user"
+ And "Consent given on behalf of user" "icon" should exist in the "User One" "table_row"
+ And "Consent not given; click to give consent on behalf of user for This privacy policy" "icon" should exist in the "User One" "table_row"
And I click on "1 of 2" "link" in the "User One" "table_row"
- And "Agreed on behalf of" "icon" should exist in the "This site policy" "table_row"
+ And "Consent given on behalf of user" "icon" should exist in the "This site policy" "table_row"
And "Max Manager" "link" should exist in the "This site policy" "table_row"
And "Consent received from a parent" "text" should exist in the "This site policy" "table_row"
- And "Not agreed" "icon" should exist in the "This privacy policy" "table_row"
+ And "Consent not given" "icon" should exist in the "This privacy policy" "table_row"
Scenario: Policies and agreements profile link visible for current user
Given I log in as "user1"
And I press "Continue"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "1 of 4 (25%)" "link" in the "This site policy" "table_row"
- And I click on "Not agreed" "link" in the "User One" "table_row"
- Then I should see "Agree on behalf of the user"
+ And I click on "Consent not given" "link" in the "User One" "table_row"
+ Then I should see "Give consent on behalf of user"
And I should see "User One"
And I should see "This site policy"
- And I should see "I acknowledge that I have received the user's request to consent on the abovementioned policy on behalf of the user."
+ And I should see "I acknowledge that I have received a request to give consent on behalf of user(s)."
And I set the field "Remarks" to "Consent received from a parent"
- And I press "Agree"
- And "Agreed on behalf of" "icon" should exist in the "User One" "table_row"
+ And I press "Give consent on behalf of user"
+ And "Consent given on behalf of user" "icon" should exist in the "User One" "table_row"
And "Max Manager" "link" should not exist in the "User One" "table_row"
And "Admin User" "link" should exist in the "User One" "table_row"
And "Consent received from a parent" "text" should exist in the "User One" "table_row"
- And "Not agreed" "icon" should exist in the "User Two" "table_row"
+ And "Consent not given" "icon" should exist in the "User Two" "table_row"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I open the action menu in "Policy1" "table_row"
And I click on "Set status to \"Active\"" "link" in the "Policy1" "table_row"
- Then I should see "All users will be required to accept this new policy version to be able to use the site"
+ Then I should see "All users will be required to agree to this new policy version to be able to use the site."
And I press "Continue"
And the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I open the action menu in "Policy1" "table_row"
And I click on "Set status to \"Inactive\"" "link" in the "Policy1" "table_row"
- Then I should see "The policy will not apply until some version is made the current one"
+ Then I should see "You are about to inactivate policy"
And I press "Continue"
And the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy1 Site policy, All users | Inactive | v1 | 1 of 4 (25%) |
And I open the action menu in "Policy1" "table_row"
- And I click on "Create a new \"Draft\"" "link" in the "Policy1" "table_row"
+ And I click on "Create a new draft" "link" in the "Policy1" "table_row"
And I set the field "Version" to "v2"
And I set the field "Name" to "Policy2"
And the field "status" matches value "0"
And I click on "Set status to \"Inactive\"" "link" in the "Policy1" "table_row"
And I press "Continue"
And I open the action menu in "Policy1" "table_row"
- And I click on "Create a new \"Draft\"" "link" in the "Policy1" "table_row"
+ And I click on "Create a new draft" "link" in the "Policy1" "table_row"
And I set the field "Version" to "v2"
And I set the field "Name" to "Policy2"
And I set the field "Active" to "1"
// Create policies and agree to them as admin.
$this->setAdminUser();
$admin = fullclone($USER);
- $admincontext = context_user::instance($admin->id);
+ $admincontext = \context_user::instance($admin->id);
$CFG->sitepolicyhandler = 'tool_policy';
$policy1 = $this->add_policy();
api::make_current($policy1->get('id'));
// Agree to the policies for oneself.
$this->setUser($this->user);
- $usercontext = context_user::instance($this->user->id);
+ $usercontext = \context_user::instance($this->user->id);
api::accept_policies([$policy1->get('id'), $policy2->get('id')]);
// Request export for this user.
$writer = writer::with_context($usercontext);
$datauser = $writer->get_related_data([get_string('userpoliciesagreements', 'tool_policy'), $this->user->id]);
- $this->assertEquals(2, count($datauser));
+ $this->assertCount(2, (array) $datauser);
$this->assertEquals($policy1->get('name'), $datauser['policyagreement-'.$policy1->get('id')]->name);
$this->assertEquals($this->user->id, $datauser['policyagreement-'.$policy1->get('id')]->usermodified);
$this->assertEquals($policy2->get('name'), $datauser['policyagreement-'.$policy2->get('id')]->name);
api::make_current($policy2->get('id'));
// Agree to the policies for oneself and for another user.
- $usercontext = context_user::instance($this->user->id);
- $admincontext = context_user::instance($USER->id);
+ $usercontext = \context_user::instance($this->user->id);
+ $admincontext = \context_user::instance($USER->id);
api::accept_policies([$policy1->get('id'), $policy2->get('id')]);
api::accept_policies([$policy1->get('id'), $policy2->get('id')], $this->user->id, 'Mynote');
$writer = writer::with_context($usercontext);
$datauser = $writer->get_related_data([get_string('userpoliciesagreements', 'tool_policy'), $this->user->id]);
- $this->assertEquals(2, count($datauser));
+ $this->assertCount(2, (array) $datauser);
$this->assertEquals($policy1->get('name'), $datauser['policyagreement-'.$policy1->get('id')]->name);
$this->assertEquals($admin->id, $datauser['policyagreement-'.$policy1->get('id')]->usermodified);
$this->assertEquals('Mynote', $datauser['policyagreement-'.$policy1->get('id')]->note);
// Admin can see all four agreements.
$writer = writer::with_context($admincontext);
$dataadmin = $writer->get_related_data([get_string('userpoliciesagreements', 'tool_policy'), $admin->id]);
- $this->assertEquals(2, count($dataadmin));
+ $this->assertCount(2, (array) $dataadmin);
$this->assertEquals($policy1->get('name'), $dataadmin['policyagreement-'.$policy1->get('id')]->name);
$this->assertEquals($admin->id, $dataadmin['policyagreement-'.$policy1->get('id')]->usermodified);
$this->assertEquals($policy2->get('name'), $dataadmin['policyagreement-'.$policy2->get('id')]->name);
$writer = writer::with_context($usercontext);
$datauser = $writer->get_related_data([get_string('userpoliciesagreements', 'tool_policy'), $this->user->id]);
- $this->assertEquals(2, count($datauser));
+ $this->assertCount(2, (array) $datauser);
$this->assertEquals($policy1->get('name'), $datauser['policyagreement-'.$policy1->get('id')]->name);
$this->assertEquals($admin->id, $datauser['policyagreement-'.$policy1->get('id')]->usermodified);
$this->assertEquals('Mynote', $datauser['policyagreement-'.$policy1->get('id')]->note);
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2018032900; // The current plugin version (Date: YYYYMMDDXX).
-$plugin->requires = 2018032900; // Requires this Moodle version.
+$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2018050800; // Requires this Moodle version.
$plugin->component = 'tool_policy'; // 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_profiling'; // 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_recyclebin'; // 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_replace'; // Full name of the plugin (used for diagnostics)
$plugin->maturity = MATURITY_ALPHA; // this version's maturity level
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
$plugin->component = 'tool_spamcleaner'; // 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_task'; // Full name of the plugin (used for diagnostics)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
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_templatelibrary'; // 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_unsuproles'; // 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_uploadcourse'; // 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_uploaduser'; // Full name of the plugin (used for diagnostics)
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX).
-$plugin->requires = 2017110800; // Requires this Moodle version.
+$plugin->version = 2018051400; // The current module version (Date: YYYYMMDDXX).
+$plugin->requires = 2018050800; // Requires this Moodle version.
$plugin->component = 'tool_usertours'; // 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_xmldb'; // 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 = 'auth_cas'; // Full name of the plugin (used for diagnostics)
-$plugin->dependencies = array('auth_ldap' => 2017110800);
+$plugin->dependencies = array('auth_ldap' => 2018050800);
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 = 'auth_db'; // 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 = 'auth_email'; // 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 = 'auth_ldap'; // 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 = 'auth_lti'; // 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 = 'auth_manual'; // Full name of the plugin (used for diagnostics)
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+
namespace auth_mnet\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 Subsystem for auth_mnet implementing null_provider.
+ * Privacy provider for the mnet authentication
*
* @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 {
+class provider implements
+ \core_privacy\local\metadata\provider,
+ \core_privacy\local\request\plugin\provider {
/**
- * Get the language string identifier with the component's language
- * file to explain why this plugin stores no data.
+ * Returns meta data about this system.
*
- * @return string
+ * @param collection $collection The initialised item collection to add items to.
+ * @return collection A listing of user data stored through this system.
*/
- public static function get_reason() : string {
- return 'privacy:metadata';
+ public static function get_metadata(collection $collection) : collection {
+
+ $sessionfields = [
+ 'userid' => 'privacy:metadata:mnet_session:userid',
+ 'username' => 'privacy:metadata:mnet_session:username',
+ 'token' => 'privacy:metadata:mnet_session:token',
+ 'mnethostid' => 'privacy:metadata:mnet_session:mnethostid',
+ 'useragent' => 'privacy:metadata:mnet_session:useragent',
+ 'expires' => 'privacy:metadata:mnet_session:expires'
+ ];
+
+ $collection->add_database_table('mnet_session', $sessionfields, 'privacy:metadata:mnet_session');
+
+ $logfields = [
+ 'hostid' => 'privacy:metadata:mnet_log:hostid',
+ 'remoteid' => 'privacy:metadata:mnet_log:remoteid',
+ 'time' => 'privacy:metadata:mnet_log:time',
+ 'userid' => 'privacy:metadata:mnet_log:userid',
+ 'ip' => 'privacy:metadata:mnet_log:ip',
+ 'course' => 'privacy:metadata:mnet_log:course',
+ 'coursename' => 'privacy:metadata:mnet_log:coursename',
+ 'module' => 'privacy:metadata:mnet_log:module',
+ 'cmid' => 'privacy:metadata:mnet_log:cmid',
+ 'action' => 'privacy:metadata:mnet_log:action',
+ 'url' => 'privacy:metadata:mnet_log:url',
+ 'info' => 'privacy:metadata:mnet_log:info'
+ ];
+
+ $collection->add_database_table('mnet_log', $logfields, 'privacy:metadata:mnet_log');
+
+ $externalfields = [
+ 'address' => 'privacy:metadata:mnet_external:address',
+ 'aim' => 'privacy:metadata:mnet_external:aim',
+ 'alternatename' => 'privacy:metadata:mnet_external:alternatename',
+ 'autosubscribe' => 'privacy:metadata:mnet_external:autosubscribe',
+ 'calendartype' => 'privacy:metadata:mnet_external:calendartype',
+ 'city' => 'privacy:metadata:mnet_external:city',
+ 'country' => 'privacy:metadata:mnet_external:country',
+ 'currentlogin' => 'privacy:metadata:mnet_external:currentlogin',
+ 'department' => 'privacy:metadata:mnet_external:department',
+ 'description' => 'privacy:metadata:mnet_external:description',
+ 'email' => 'privacy:metadata:mnet_external:email',
+ 'emailstop' => 'privacy:metadata:mnet_external:emailstop',
+ 'firstaccess' => 'privacy:metadata:mnet_external:firstaccess',
+ 'firstname' => 'privacy:metadata:mnet_external:firstname',
+ 'firstnamephonetic' => 'privacy:metadata:mnet_external:firstnamephonetic',
+ 'icq' => 'privacy:metadata:mnet_external:icq',
+ 'id' => 'privacy:metadata:mnet_external:id',
+ 'idnumber' => 'privacy:metadata:mnet_external:idnumber',
+ 'imagealt' => 'privacy:metadata:mnet_external:imagealt',
+ 'institution' => 'privacy:metadata:mnet_external:institution',
+ 'lang' => 'privacy:metadata:mnet_external:lang',
+ 'lastaccess' => 'privacy:metadata:mnet_external:lastaccess',
+ 'lastlogin' => 'privacy:metadata:mnet_external:lastlogin',
+ 'lastname' => 'privacy:metadata:mnet_external:lastname',
+ 'lastnamephonetic' => 'privacy:metadata:mnet_external:lastnamephonetic',
+ 'maildigest' => 'privacy:metadata:mnet_external:maildigest',
+ 'maildisplay' => 'privacy:metadata:mnet_external:maildisplay',
+ 'middlename' => 'privacy:metadata:mnet_external:middlename',
+ 'msn' => 'privacy:metadata:mnet_external:msn',
+ 'phone1' => 'privacy:metadata:mnet_external:phone1',
+ 'pnone2' => 'privacy:metadata:mnet_external:phone2',
+ 'picture' => 'privacy:metadata:mnet_external:picture',
+ 'policyagreed' => 'privacy:metadata:mnet_external:policyagreed',
+ 'skype' => 'privacy:metadata:mnet_external:skype',
+ 'suspended' => 'privacy:metadata:mnet_external:suspended',
+ 'timezone' => 'privacy:metadata:mnet_external:timezone',
+ 'trackforums' => 'privacy:metadata:mnet_external:trackforums',
+ 'trustbitmask' => 'privacy:metadata:mnet_external:trustbitmask',
+ 'url' => 'privacy:metadata:mnet_external:url',
+ 'username' => 'privacy:metadata:mnet_external:username',
+ 'yahoo' => 'privacy:metadata:mnet_external:yahoo',
+ ];
+
+ $collection->add_external_location_link('moodle', $externalfields, 'privacy:metadata:external:moodle');
+
+ $collection->add_external_location_link('mahara', $externalfields, 'privacy:metadata:external:mahara');
+
+ 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 list of contexts used in this plugin.
+ */
+ public static function get_contexts_for_userid(int $userid) : contextlist {
+ $sql = "SELECT ctx.id
+ FROM {mnet_log} ml
+ JOIN {context} ctx ON ctx.instanceid = ml.userid AND ctx.contextlevel = :contextlevel
+ WHERE ml.userid = :userid";
+ $params = ['userid' => $userid, 'contextlevel' => CONTEXT_USER];
+
+ $contextlist = new contextlist();
+ $contextlist->add_from_sql($sql, $params);
+
+ return $contextlist;
+ }
+
+ /**
+ * Export all user data for the specified user, in the specified contexts, using the supplied exporter instance.
+ *
+ * @param approved_contextlist $contextlist The approved contexts to export information for.
+ */
+ public static function export_user_data(approved_contextlist $contextlist) {
+ global $DB;
+
+ $context = \context_user::instance($contextlist->get_user()->id);
+
+ $sql = "SELECT ml.id, mh.wwwroot, mh.name, ml.remoteid, ml.time, ml.userid, ml.ip, ml.course,
+ ml.coursename, ml.module, ml.cmid, ml.action, ml.url, ml.info
+ FROM {mnet_log} ml
+ JOIN {mnet_host} mh ON mh.id = ml.hostid
+ WHERE ml.userid = :userid
+ ORDER BY mh.name, ml.coursename";
+ $params = ['userid' => $contextlist->get_user()->id];
+
+ $data = [];
+ $lastcourseid = null;
+
+ $logentries = $DB->get_recordset_sql($sql, $params);
+ foreach ($logentries as $logentry) {
+ $item = (object) [
+ 'time' => transform::datetime($logentry->time),
+ 'remoteid' => $logentry->remoteid,
+ 'ip' => $logentry->ip,
+ 'course' => $logentry->course,
+ 'coursename' => format_string($logentry->coursename),
+ 'module' => $logentry->module,
+ 'cmid' => $logentry->cmid,
+ 'action' => $logentry->action,
+ 'url' => $logentry->url,
+ 'info' => format_string($logentry->info)
+ ];
+
+ $item->externalhost =
+ ($logentry->name == '') ? preg_replace('#^https?://#', '', $logentry->wwwroot) :
+ preg_replace('#^https?://#', '', $logentry->name);
+
+ if ($lastcourseid && $lastcourseid != $logentry->course) {
+ $path = [get_string('pluginname', 'auth_mnet'), $data[0]->externalhost, $data[0]->coursename];
+ writer::with_context($context)->export_data($path, (object) $data);
+ $data = [];
+ }
+
+ $data[] = $item;
+ $lastcourseid = $logentry->course;
+ }
+ $logentries->close();
+
+ $path = [get_string('pluginname', 'auth_mnet'), $item->externalhost, $item->coursename];
+ writer::with_context($context)->export_data($path, (object) $data);
+ }
+
+ /**
+ * Delete all personal data for all users in the specified context.
+ *
+ * @param context $context Context to delete data from.
+ */
+ public static function delete_data_for_all_users_in_context(\context $context) {
+ global $DB;
+
+ if ($context->contextlevel != CONTEXT_USER) {
+ return;
+ }
+
+ $DB->delete_records('mnet_log', ['userid' => $context->instanceid]);
+ }
+
+ /**
+ * 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) {
+ global $DB;
+
+ if (empty($contextlist->count())) {
+ return;
+ }
+
+ foreach ($contextlist->get_contexts() as $context) {
+ if ($context->contextlevel != CONTEXT_USER) {
+ return;
+ }
+
+ // Because we only use user contexts the instance ID is the user ID.
+ $DB->delete_records('mnet_log', ['userid' => $context->instanceid]);
+ }
}
}
\ No newline at end of file
$string['sso_sp_description'] = 'Publish this service to allow authenticated users from {$a} to access your site without having to re-login. <ul><li><em>Dependency</em>: You must also <strong>subscribe</strong> to the SSO (Identity Provider) service on {$a}.</li></ul><br />Subscribe to this service to allow your users to roam to the {$a} site without having to re-login there. <ul><li><em>Dependency</em>: You must also <strong>publish</strong> the SSO (Identity Provider) service to {$a}.</li></ul><br />';
$string['sso_sp_name'] = 'SSO (Service Provider)';
$string['pluginname'] = 'MNet authentication';
-$string['privacy:metadata'] = 'The MNet authentication plugin does not store any personal data.';
+$string['privacy:metadata:external:mahara'] = 'This plugin can send data externally to a linked Mahara application.';
+$string['privacy:metadata:external:moodle'] = 'This plugin can send data externally to a linked Moodle application.';
+$string['privacy:metadata:mnet_external:address'] = 'The address of the user.';
+$string['privacy:metadata:mnet_external:aim'] = 'The AIM identifier of the user.';
+$string['privacy:metadata:mnet_external:alternatename'] = 'An alternative name for the user.';
+$string['privacy:metadata:mnet_external:autosubscribe'] = 'A preference as to if the user should be auto-subscribed to forums the user posts in.';
+$string['privacy:metadata:mnet_external:calendartype'] = 'A user preference for the type of calendar to use.';
+$string['privacy:metadata:mnet_external:city'] = 'The city of the user.';
+$string['privacy:metadata:mnet_external:country'] = 'The country that the user is in.';
+$string['privacy:metadata:mnet_external:currentlogin'] = 'The current login for this user.';
+$string['privacy:metadata:mnet_external:department'] = 'The department that this user can be found in.';
+$string['privacy:metadata:mnet_external:description'] = 'General details about this user.';
+$string['privacy:metadata:mnet_external:email'] = 'An email address for contact.';
+$string['privacy:metadata:mnet_external:emailstop'] = 'A preference to stop email being sent to the user.';
+$string['privacy:metadata:mnet_external:firstaccess'] = 'The time that this user first accessed the site.';
+$string['privacy:metadata:mnet_external:firstname'] = 'The first name of the user.';
+$string['privacy:metadata:mnet_external:firstnamephonetic'] = 'The phonetic details about the user\'s first name.';
+$string['privacy:metadata:mnet_external:icq'] = 'The ICQ number of the user.';
+$string['privacy:metadata:mnet_external:id'] = 'The identifier for the user.';
+$string['privacy:metadata:mnet_external:idnumber'] = 'An identification number given by the institution.';
+$string['privacy:metadata:mnet_external:imagealt'] = 'Alternative text for the user\'s image.';
+$string['privacy:metadata:mnet_external:institution'] = 'The institution that this user is a member of.';
+$string['privacy:metadata:mnet_external:lang'] = 'A user preference for the language shown.';
+$string['privacy:metadata:mnet_external:lastaccess'] = 'The time that the user last accessed the site.';
+$string['privacy:metadata:mnet_external:lastlogin'] = 'The last login of this user.';
+$string['privacy:metadata:mnet_external:lastname'] = 'The surname of the user.';
+$string['privacy:metadata:mnet_external:lastnamephonetic'] = 'The phonetic details about the user\'s surname.';
+$string['privacy:metadata:mnet_external:maildigest'] = 'A setting for the mail digest for this user.';
+$string['privacy:metadata:mnet_external:maildisplay'] = 'A preference for the user about displaying their email address to other users.';
+$string['privacy:metadata:mnet_external:middlename'] = 'The middle name of the user.';
+$string['privacy:metadata:mnet_external:msn'] = 'The MSN identifier of the user.';
+$string['privacy:metadata:mnet_external:phone1'] = 'A phone number for the user.';
+$string['privacy:metadata:mnet_external:phone2'] = 'An additional phone number for the user.';
+$string['privacy:metadata:mnet_external:picture'] = 'The picture details associated with this user.';
+$string['privacy:metadata:mnet_external:policyagreed'] = 'A flag to determine if the user has agreed to the site policy.';
+$string['privacy:metadata:mnet_external:skype'] = 'The skype identifier of the user.';
+$string['privacy:metadata:mnet_external:suspended'] = 'A flag to show if the user has been suspended on this system.';
+$string['privacy:metadata:mnet_external:timezone'] = 'The timezone that the user resides in.';
+$string['privacy:metadata:mnet_external:trackforums'] = 'A preference for forums and tracking them.';
+$string['privacy:metadata:mnet_external:trustbitmask'] = 'The trust bit mask';
+$string['privacy:metadata:mnet_external:url'] = 'A URL related to this user.';
+$string['privacy:metadata:mnet_external:username'] = 'The username for this user.';
+$string['privacy:metadata:mnet_external:yahoo'] = 'The yahoo identifier of the user.';
+$string['privacy:metadata:mnet_log'] = 'Details of remote actions carried out by a local user logged in a remote system.';
+$string['privacy:metadata:mnet_log:action'] = 'Action carried out by the user.';
+$string['privacy:metadata:mnet_log:cmid'] = 'ID of the course module.';
+$string['privacy:metadata:mnet_log:course'] = 'Remote system course ID where the action occurred.';
+$string['privacy:metadata:mnet_log:coursename'] = 'Remote system course full name where the action occurred.';
+$string['privacy:metadata:mnet_log:hostid'] = 'Remote system MNet ID.';
+$string['privacy:metadata:mnet_log:info'] = 'Additional information about the action.';
+$string['privacy:metadata:mnet_log:ip'] = 'The IP address used at the time of the action occurred.';
+$string['privacy:metadata:mnet_log:module'] = 'Remote system module where the event the action occurred.';
+$string['privacy:metadata:mnet_log:remoteid'] = 'Remote ID of the user who carried out the action in the remote system.';
+$string['privacy:metadata:mnet_log:time'] = 'Time when the action occurred.';
+$string['privacy:metadata:mnet_log:url'] = 'Remote system URL where the action occurred.';
+$string['privacy:metadata:mnet_log:userid'] = 'Local ID of the user who carried out the action in the remote system.';
+$string['privacy:metadata:mnet_session'] = 'The details of each MNet user session in a remote system is stored temporarily.';
+$string['privacy:metadata:mnet_session:expires'] = 'Time when the session expires.';
+$string['privacy:metadata:mnet_session:mnethostid'] = 'Remote system MNet ID.';
+$string['privacy:metadata:mnet_session:token'] = 'Unique session identifier.';
+$string['privacy:metadata:mnet_session:useragent'] = 'String denoting the user agent being which is accessing the page.';
+$string['privacy:metadata:mnet_session:userid'] = 'ID of the user jumping to remote system.';
+$string['privacy:metadata:mnet_session:username'] = 'Username of the user jumping to remote system.';
+$string['unknownhost'] = 'Unknown host';
\ 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 the authentication mnet
+ *
+ * @package auth_mnet
+ * @category test
+ * @copyright 2018 Victor Deniz <victor@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+use \auth_mnet\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;
+
+/**
+ * Privacy test for the authentication mnet
+ *
+ * @package auth_mnet
+ * @category test
+ * @copyright 2018 Victor Deniz <victor@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class auth_mnet_privacy_testcase extends provider_testcase {
+ /**
+ * Set up method.
+ */
+ public function setUp() {
+ $this->resetAfterTest();
+ $this->setAdminUser();
+ }
+
+ /**
+ * Check that a user context is returned if there is any user data for this user.
+ */
+ public function test_get_contexts_for_userid() {
+ global $DB;
+
+ $user = $this->getDataGenerator()->create_user(['auth' => 'mnet']);
+ $this->assertEmpty(provider::get_contexts_for_userid($user->id));
+
+ // Insert mnet_log record.
+ $logrecord = new stdClass();
+ $logrecord->hostid = '';
+ $logrecord->remoteid = 65;
+ $logrecord->time = time();
+ $logrecord->userid = $user->id;
+
+ $DB->insert_record('mnet_log', $logrecord);
+
+ $contextlist = provider::get_contexts_for_userid($user->id);
+
+ // Check that we only get back one context.
+ $this->assertCount(1, $contextlist);
+
+ // Check that a context is returned is the expected.
+ $usercontext = \context_user::instance($user->id);
+ $this->assertEquals($usercontext->id, $contextlist->get_contextids()[0]);
+ }
+
+ /**
+ * Test that user data is exported correctly.
+ */
+ public function test_export_user_data() {
+ global $DB;
+
+ $user = $this->getDataGenerator()->create_user(['auth' => 'mnet']);
+
+ // Insert mnet_host record.
+ $hostrecord = new stdClass();
+ $hostrecord->wwwroot = 'https://external.moodle.com';
+ $hostrecord->name = 'External Moodle';
+ $hostrecord->public_key = '-----BEGIN CERTIFICATE-----';
+
+ $hostid = $DB->insert_record('mnet_host', $hostrecord);
+
+ // Insert mnet_log record.
+ $logrecord = new stdClass();
+ $logrecord->hostid = $hostid;
+ $logrecord->remoteid = 65;
+ $logrecord->time = time();
+ $logrecord->userid = $user->id;
+ $logrecord->course = 3;
+ $logrecord->coursename = 'test course';
+
+ $DB->insert_record('mnet_log', $logrecord);
+
+ $usercontext = \context_user::instance($user->id);
+
+ $writer = writer::with_context($usercontext);
+ $this->assertFalse($writer->has_any_data());
+ $approvedlist = new approved_contextlist($user, 'auth_mnet', [$usercontext->id]);
+ provider::export_user_data($approvedlist);
+
+ $data = $writer->get_data([get_string('pluginname', 'auth_mnet'), $hostrecord->name, $logrecord->coursename]);
+
+ $this->assertEquals($logrecord->remoteid, reset($data)->remoteid);
+ $this->assertEquals(transform::datetime($logrecord->time), reset($data)->time);
+ }
+
+ /**
+ * Test deleting all user data for a specific context.
+ */
+ public function test_delete_data_for_all_users_in_context() {
+ global $DB;
+
+ $user1 = $this->getDataGenerator()->create_user(['auth' => 'mnet']);
+
+ // Insert mnet_log record.
+ $logrecord1 = new stdClass();
+ $logrecord1->hostid = '';
+ $logrecord1->remoteid = 65;
+ $logrecord1->time = time();
+ $logrecord1->userid = $user1->id;
+
+ $DB->insert_record('mnet_log', $logrecord1);
+
+ $user1context = \context_user::instance($user1->id);
+
+ $user2 = $this->getDataGenerator()->create_user(['auth' => 'mnet']);
+
+ // Insert mnet_log record.
+ $logrecord2 = new stdClass();
+ $logrecord2->hostid = '';
+ $logrecord2->remoteid = 65;
+ $logrecord2->time = time();
+ $logrecord2->userid = $user2->id;
+
+ $DB->insert_record('mnet_log', $logrecord2);
+
+ // Get all mnet log records.
+ $mnetlogrecords = $DB->get_records('mnet_log', array());
+ // There should be two.
+ $this->assertCount(2, $mnetlogrecords);
+
+ // Delete everything for the first user context.
+ provider::delete_data_for_all_users_in_context($user1context);
+
+ // Get all user1 mnet log records.
+ $mnetlogrecords = $DB->get_records('mnet_log', ['userid' => $user1->id]);
+ $this->assertCount(0, $mnetlogrecords);
+
+ // Get all mnet log records.
+ $mnetlogrecords = $DB->get_records('mnet_log', array());
+ // There should be one (user2).
+ $this->assertCount(1, $mnetlogrecords);
+ }
+
+ /**
+ * This should work identical to the above test.
+ */
+ public function test_delete_data_for_user() {
+ global $DB;
+
+ $user1 = $this->getDataGenerator()->create_user(['auth' => 'mnet']);
+
+ // Insert mnet_log record.
+ $logrecord1 = new stdClass();
+ $logrecord1->hostid = '';
+ $logrecord1->remoteid = 65;
+ $logrecord1->time = time();
+ $logrecord1->userid = $user1->id;
+
+ $DB->insert_record('mnet_log', $logrecord1);
+
+ $user1context = \context_user::instance($user1->id);
+
+ $user2 = $this->getDataGenerator()->create_user(['auth' => 'mnet']);
+
+ // Insert mnet_log record.
+ $logrecord2 = new stdClass();
+ $logrecord2->hostid = '';
+ $logrecord2->remoteid = 65;
+ $logrecord2->time = time();
+ $logrecord2->userid = $user2->id;
+
+ $DB->insert_record('mnet_log', $logrecord2);
+
+ // Get all mnet log records.
+ $mnetlogrecords = $DB->get_records('mnet_log', array());
+ // There should be two.
+ $this->assertCount(2, $mnetlogrecords);
+
+ // Delete everything for the first user.
+ $approvedlist = new approved_contextlist($user1, 'auth_mnet', [$user1context->id]);
+ provider::delete_data_for_user($approvedlist);
+
+ // Get all user1 mnet log records.
+ $mnetlogrecords = $DB->get_records('mnet_log', ['userid' => $user1->id]);
+ $this->assertCount(0, $mnetlogrecords);
+
+ // Get all mnet log records.
+ $mnetlogrecords = $DB->get_records('mnet_log', array());
+ // There should be one (user2).
+ $this->assertCount(1, $mnetlogrecords);
+ }
+}
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 = 'auth_mnet'; // 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 = 'auth_nologin'; // 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 = 'auth_none'; // Full name of the plugin (used for diagnostics)
$string['plugindescription'] = 'This authentication plugin displays a list of the configured identity providers on the login page. Selecting an identity provider allows users to login with their credentials from an OAuth 2 provider.';
$string['pluginname'] = 'OAuth 2';
$string['alreadylinked'] = 'This external account is already linked to an account on this site';
-$string['privacy:metadata:auth_oauth2'] = 'OAuth2 authentication';
+$string['privacy:metadata:auth_oauth2'] = 'OAuth 2 authentication';
$string['privacy:metadata:auth_oauth2:authsubsystem'] = 'This plugin is connected to the authentication subsystem.';
$string['privacy:metadata:auth_oauth2:confirmtoken'] = 'The confirmation token.';
-$string['privacy:metadata:auth_oauth2:confirmtokenexpires'] = 'Indicates the timestamp the confirmation token expires.';
+$string['privacy:metadata:auth_oauth2:confirmtokenexpires'] = 'The timestamp when the confirmation token expires.';
$string['privacy:metadata:auth_oauth2:email'] = 'The external email that maps to this account.';
-$string['privacy:metadata:auth_oauth2:issuerid'] = 'The identifier of the OAuth2 issuer for this OAuth2 login.';
-$string['privacy:metadata:auth_oauth2:tableexplanation'] = 'OAuth2 accounts linked to a user\'s Moodle account are being stored here.';
-$string['privacy:metadata:auth_oauth2:timecreated'] = 'Indicates the timestamp when the user account was linked to the OAuth2 login.';
-$string['privacy:metadata:auth_oauth2:timemodified'] = 'Indicates the timestamp when this record was modified.';
-$string['privacy:metadata:auth_oauth2:userid'] = 'The user ID of the user account this OAuth2 login is linked to.';
+$string['privacy:metadata:auth_oauth2:issuerid'] = 'The identifier of the OAuth 2 issuer for this OAuth 2 login.';
+$string['privacy:metadata:auth_oauth2:tableexplanation'] = 'OAuth 2 accounts linked to a user\'s Moodle account.';
+$string['privacy:metadata:auth_oauth2:timecreated'] = 'The timestamp when the user account was linked to the OAuth 2 login.';
+$string['privacy:metadata:auth_oauth2:timemodified'] = 'The timestamp when this record was modified.';
+$string['privacy:metadata:auth_oauth2:userid'] = 'The ID of the user account which the OAuth 2 login is linked to.';
$string['privacy:metadata:auth_oauth2:usermodified'] = 'The ID of the user who modified this account.';
$string['privacy:metadata:auth_oauth2:username'] = 'The external username that maps to this account.';
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 = 'auth_oauth2'; // 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 = 'auth_shibboleth'; // 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 = 'auth_webservice'; // 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 = 'availability_completion';
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
$plugin->component = 'availability_date';
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
$plugin->component = 'availability_grade';
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
$plugin->component = 'availability_group';
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
$plugin->component = 'availability_grouping';
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017111300;
-$plugin->requires = 2017110800;
+$plugin->version = 2018051400;
+$plugin->requires = 2018050800;
$plugin->component = 'availability_profile';
// Create DIV structure (without kids).
this.node = Y.Node.create('<div class="availability-list"><h3 class="accesshide"></h3>' +
'<div class="availability-inner">' +
- '<div class="availability-header"><span class="p-l-1">' +
+ '<div class="availability-header m-b-1"><span>' +
M.util.get_string('listheader_sign_before', 'availability') + '</span>' +
' <label><span class="accesshide">' + M.util.get_string('label_sign', 'availability') +
' </span><select class="availability-neg custom-select m-x-1"' +
'<div class="clearfix m-t-1"></div>' +
'<div class="availability-button"></div></div><div class="clearfix"></div></div>');
if (!root) {
- this.node.addClass('availability-childlist');
+ this.node.addClass('availability-childlist d-sm-flex align-items-center');
}
this.inner = this.node.one('> .availability-inner');
// Add entry for plugin.
li = Y.Node.create('<li class="clearfix row"></li>');
id = 'availability_addrestriction_' + type;
- button = Y.Node.create('<button type="button" class="btn btn-default col-xs-6"' +
- 'id="' + id + '">' + M.util.get_string('title', 'availability_' + type) + '</button>');
+ button = Y.Node.create('<div class="col-6"><button type="button" class="btn btn-default w-100"' +
+ 'id="' + id + '">' + M.util.get_string('title', 'availability_' + type) + '</button></div>');
button.on('click', this.getAddHandler(type, dialogRef), this);
li.appendChild(button);
- label = Y.Node.create('<label for="' + id + '" class="col-xs-6">' +
- M.util.get_string('description', 'availability_' + type) + '</label>');
+ label = Y.Node.create('<div class="col-6"><label for="' + id + '">' +
+ M.util.get_string('description', 'availability_' + type) + '</label></div>');
li.appendChild(label);
ul.appendChild(li);
}
// Extra entry for lists.
li = Y.Node.create('<li class="clearfix row"></li>');
id = 'availability_addrestriction_list_';
- button = Y.Node.create('<button type="button" class="btn btn-default col-xs-6"' +
- 'id="' + id + '">' + M.util.get_string('condition_group', 'availability') + '</button>');
+ button = Y.Node.create('<div class="col-6"><button type="button" class="btn btn-default w-100"' +
+ 'id="' + id + '">' + M.util.get_string('condition_group', 'availability') + '</button></div>');
button.on('click', this.getAddHandler(null, dialogRef), this);
li.appendChild(button);
- label = Y.Node.create('<label for="' + id + '" class="col-xs-6">' +
- M.util.get_string('condition_group_info', 'availability') + '</label>');
+ label = Y.Node.create('<div class="col-6"><label for="' + id + '">' +
+ M.util.get_string('condition_group_info', 'availability') + '</label></div>');
li.appendChild(label);
ul.appendChild(li);
this.pluginNode.addClass('availability_' + json.type);
}
- this.node = Y.Node.create('<div class="availability-item d-inline-block"><h3 class="accesshide"></h3></div>');
+ this.node = Y.Node.create('<div class="availability-item d-sm-flex align-items-center"><h3 class="accesshide"></h3></div>');
// Add eye icon if required. This icon is added for root items, but may be
// hidden depending on the selected list operator.
// Add the invalid marker (empty).
this.node.appendChild(document.createTextNode(' '));
- this.node.appendChild(Y.Node.create('<span class="m-t-1 label label-warning"/>'));
+ this.node.appendChild(Y.Node.create('<span class="label label-warning"/>'));
};
/**
* point is backup when some behavior/approach channged, in order to allow
* conditional coding based on it.
*/
- const VERSION = 2017111300;
+ const VERSION = 2018051400;
/**
* Usually same than major release zero version, mainly for informative/historic purposes.
*/
--- /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 provider tests.
+ *
+ * @package core_backup
+ * @copyright 2018 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+use core_backup\privacy\provider;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy provider tests class.
+ *
+ * @copyright 2018 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_backup_privacy_provider_testcase extends \core_privacy\tests\provider_testcase {
+
+ /**
+ * @var stdClass The user
+ */
+ protected $user = null;
+
+ /**
+ * @var stdClass The course
+ */
+ protected $course = null;
+
+ /**
+ * Basic setup for these tests.
+ */
+ public function setUp() {
+ global $DB;
+
+ $this->resetAfterTest();
+
+ $this->course = $this->getDataGenerator()->create_course();
+
+ $this->user = $this->getDataGenerator()->create_user();
+
+ // Just insert directly into the 'backup_controllers' table.
+ $bcdata = (object) [
+ 'backupid' => 1,
+ 'operation' => 'restore',
+ 'type' => 'course',
+ 'itemid' => $this->course->id,
+ 'format' => 'moodle2',
+ 'interactive' => 1,
+ 'purpose' => 10,
+ 'userid' => $this->user->id,
+ 'status' => 1000,
+ 'execution' => 1,
+ 'executiontime' => 0,
+ 'checksum' => 'checksumyolo',
+ 'timecreated' => time(),
+ 'timemodified' => time(),
+ 'controller' => ''
+ ];
+ $DB->insert_record('backup_controllers', $bcdata);
+
+ // Create another user who will perform a backup operation.
+ $user = $this->getDataGenerator()->create_user();
+ $bcdata->backupid = 2;
+ $bcdata->userid = $user->id;
+ $DB->insert_record('backup_controllers', $bcdata);
+ }
+
+ /**
+ * Test getting the context for the user ID related to this plugin.
+ */
+ public function test_get_contexts_for_userid() {
+ $contextlist = provider::get_contexts_for_userid($this->user->id);
+ $this->assertCount(1, $contextlist);
+ $contextforuser = $contextlist->current();
+ $context = context_course::instance($this->course->id);
+ $this->assertEquals($context->id, $contextforuser->id);
+ }
+
+ /**
+ * Test for provider::export_user_data().
+ */
+ public function test_export_for_context() {
+ global $DB;
+
+ // Create another backup_controllers record.
+ $bcdata = (object) [
+ 'backupid' => 3,
+ 'operation' => 'backup',
+ 'type' => 'course',
+ 'itemid' => $this->course->id,
+ 'format' => 'moodle2',
+ 'interactive' => 1,
+ 'purpose' => 10,
+ 'userid' => $this->user->id,
+ 'status' => 1000,
+ 'execution' => 1,
+ 'executiontime' => 0,
+ 'checksum' => 'checksumyolo',
+ 'timecreated' => time() + DAYSECS,
+ 'timemodified' => time() + DAYSECS,
+ 'controller' => ''
+ ];
+ $DB->insert_record('backup_controllers', $bcdata);
+
+ $coursecontext = context_course::instance($this->course->id);
+
+ // Export all of the data for the context.
+ $this->export_context_data_for_user($this->user->id, $coursecontext, 'core_backup');
+ $writer = \core_privacy\local\request\writer::with_context($coursecontext);
+ $this->assertTrue($writer->has_any_data());
+
+ $data = (array) $writer->get_data([get_string('backup'), $this->course->id]);
+
+ $this->assertCount(2, $data);
+
+ $bc1 = array_shift($data);
+ $this->assertEquals('restore', $bc1['operation']);
+
+ $bc2 = array_shift($data);
+ $this->assertEquals('backup', $bc2['operation']);
+ }
+
+ /**
+ * Test for provider::delete_data_for_all_users_in_context().
+ */
+ public function test_delete_data_for_all_users_in_context() {
+ global $DB;
+
+ // Before deletion, we should have 2 operations.
+ $count = $DB->count_records('backup_cont