From 58435433383d65b9473b8113a68aeaa5c3d60f35 Mon Sep 17 00:00:00 2001 From: Adrian Greeve Date: Wed, 31 Jan 2018 11:49:53 +0800 Subject: [PATCH] MDL-61308 assign_submission: Privacy code for user rights. --- .../privacy/assignsubmission_provider.php | 88 ++++++ .../privacy/submission_legacy_polyfill.php | 101 +++++++ .../comments/classes/privacy/provider.php | 131 +++++++++ .../lang/en/assignsubmission_comments.php | 1 + .../comments/tests/privacy_test.php | 253 ++++++++++++++++++ .../file/classes/privacy/provider.php | 143 ++++++++++ .../file/lang/en/assignsubmission_file.php | 1 + .../submission/file/tests/privacy_test.php | 175 ++++++++++++ .../onlinetext/classes/privacy/provider.php | 162 +++++++++++ .../lang/en/assignsubmission_onlinetext.php | 8 +- .../onlinetext/tests/privacy_test.php | 164 ++++++++++++ ...rivacy_submission_legacy_polyfill_test.php | 226 ++++++++++++++++ 12 files changed, 1452 insertions(+), 1 deletion(-) create mode 100644 mod/assign/classes/privacy/assignsubmission_provider.php create mode 100644 mod/assign/classes/privacy/submission_legacy_polyfill.php create mode 100644 mod/assign/submission/comments/classes/privacy/provider.php create mode 100644 mod/assign/submission/comments/tests/privacy_test.php create mode 100644 mod/assign/submission/file/classes/privacy/provider.php create mode 100644 mod/assign/submission/file/tests/privacy_test.php create mode 100644 mod/assign/submission/onlinetext/classes/privacy/provider.php create mode 100644 mod/assign/submission/onlinetext/tests/privacy_test.php create mode 100644 mod/assign/tests/privacy_submission_legacy_polyfill_test.php diff --git a/mod/assign/classes/privacy/assignsubmission_provider.php b/mod/assign/classes/privacy/assignsubmission_provider.php new file mode 100644 index 0000000000000..84c545eeb3cd5 --- /dev/null +++ b/mod/assign/classes/privacy/assignsubmission_provider.php @@ -0,0 +1,88 @@ +. + +/** + * This file contains the assignsubmission_provider interface. + * + * Assignment Sub plugins should implement this if they store personal information. + * + * @package mod_assign + * @copyright 2018 Adrian Greeve + * + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace mod_assign\privacy; + +use core_privacy\local\request\contextlist; + +defined('MOODLE_INTERNAL') || die(); + +interface assignsubmission_provider extends \core_privacy\local\request\plugin\subplugin_provider { + + /** + * Retrieves the contextids associated with the provided userid for this subplugin. + * NOTE if your subplugin must have an entry in the assign_submission table to work, then this + * method can be empty. + * + * @param int $userid The user ID to get context IDs for. + * @param \core_privacy\local\request\contextlist $contextlist Use add_from_sql with this object to add your context IDs. + */ + public static function get_context_for_userid_within_submission(int $userid, contextlist $contextlist); + + /** + * Returns student user ids related to the provided teacher ID. If it is possible that a student ID will not be returned by + * the sql query in \mod_assign\privacy\provider::find_grader_info() Then you need to provide some sql to retrive those + * student IDs. This is highly likely if you had to fill in get_context_for_userid_within_submission above. + * + * @param useridlist $useridlist A user ID list object that you can append your user IDs to. + */ + public static function get_student_user_ids(useridlist $useridlist); + + /** + * This method is used to export any user data this sub-plugin has using the assign_plugin_request_data object to get the + * context and userid. + * assign_plugin_request_data contains: + * - context + * - submission object + * - current path (subcontext) + * - user object + * + * @param assign_plugin_request_data $exportdata Information to use to export user data for this sub-plugin. + */ + public static function export_submission_user_data(assign_plugin_request_data $exportdata); + + /** + * Any call to this method should delete all user data for the context defined in the deletion_criteria. + * assign_plugin_request_data contains: + * - context + * - assign object + * + * @param assign_plugin_request_data $requestdata Information to use to delete user data for this submission. + */ + public static function delete_submission_for_context(assign_plugin_request_data $requestdata); + + /** + * A call to this method should delete user data (where practicle) from the userid and context. + * assign_plugin_request_data contains: + * - context + * - submission object + * - user object + * - assign object + * + * @param assign_plugin_request_data $exportdata Details about the user and context to focus the deletion. + */ + public static function delete_submission_for_userid(assign_plugin_request_data $exportdata); +} diff --git a/mod/assign/classes/privacy/submission_legacy_polyfill.php b/mod/assign/classes/privacy/submission_legacy_polyfill.php new file mode 100644 index 0000000000000..9b088d95f02b4 --- /dev/null +++ b/mod/assign/classes/privacy/submission_legacy_polyfill.php @@ -0,0 +1,101 @@ +. + +/** + * This file contains the polyfill to allow a plugin to operate with Moodle 3.3 up. + * + * @package mod_assign + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace mod_assign\privacy; + +use core_privacy\local\request\contextlist; + +defined('MOODLE_INTERNAL') || die(); + +/** + * The trait used to provide backwards compatability for third-party plugins. + * + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +trait submission_legacy_polyfill { + + /** + * Retrieves the contextids associated with the provided userid for this subplugin. + * NOTE if your subplugin must have an entry in the assign_submission table to work, then this + * method can be empty. + * + * @param int $userid The user ID to get context IDs for. + * @param \core_privacy\local\request\contextlist $contextlist Use add_from_sql with this object to add your context IDs. + */ + public static function get_context_for_userid_within_submission(int $userid, contextlist $contextlist) { + return static::_get_context_for_userid_within_submission($userid, $contextlist); + } + + /** + * Returns student user ids related to the provided teacher ID. If it is possible that a student ID will not be returned by + * the sql query in \mod_assign\privacy\provider::find_grader_info() Then you need to provide some sql to retrive those + * student IDs. This is highly likely if you had to fill in get_context_for_userid_within_submission above. + * + * @param useridlist $useridlist A user ID list object that you can append your user IDs to. + */ + public static function get_student_user_ids(useridlist $useridlist) { + return static::_get_student_user_ids($useridlist); + } + + /** + * This method is used to export any user data this sub-plugin has using the assign_plugin_request_data object to get the + * context and userid. + * assign_plugin_request_data contains: + * - context + * - submission object + * - current path (subcontext) + * - user object + * + * @param assign_plugin_request_data $exportdata Information to use to export user data for this sub-plugin. + */ + public static function export_submission_user_data(assign_plugin_request_data $exportdata) { + return static::_export_submission_user_data($exportdata); + } + + /** + * Any call to this method should delete all user data for the context defined in the deletion_criteria. + * assign_plugin_request_data contains: + * - context + * - assign object + * + * @param assign_plugin_request_data $requestdata Information to use to delete user data for this submission. + */ + public static function delete_submission_for_context(assign_plugin_request_data $requestdata) { + return static::_delete_submission_for_context($requestdata); + } + + /** + * A call to this method should delete user data (where practicle) from the userid and context. + * assign_plugin_request_data contains: + * - context + * - submission object + * - user object + * - assign object + * + * @param assign_plugin_request_data $exportdata Details about the user and context to focus the deletion. + */ + public static function delete_submission_for_userid(assign_plugin_request_data $exportdata) { + return static::_delete_submission_for_userid($exportdata); + } +} diff --git a/mod/assign/submission/comments/classes/privacy/provider.php b/mod/assign/submission/comments/classes/privacy/provider.php new file mode 100644 index 0000000000000..6f95fd2487d25 --- /dev/null +++ b/mod/assign/submission/comments/classes/privacy/provider.php @@ -0,0 +1,131 @@ +. + +/** + * Privacy class for requesting user data. + * + * @package assignsubmission_comments + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace assignsubmission_comments\privacy; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/assign/locallib.php'); + +use \core_privacy\local\metadata\collection; +use \core_privacy\local\metadata\provider as metadataprovider; +use \core_comment\privacy\provider as comments_provider; +use \core_privacy\local\request\contextlist; +use \mod_assign\privacy\assign_plugin_request_data; + +/** + * Privacy class for requesting user data. + * + * @package assignsubmission_comments + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements metadataprovider, \mod_assign\privacy\assignsubmission_provider { + + /** + * Return meta data about this plugin. + * + * @param collection $collection A list of information to add to. + * @return collection Return the collection after adding to it. + */ + public static function get_metadata(collection $collection) : collection { + $collection->link_subsystem('core_comment', 'privacy:metadata:commentpurpose'); + return $collection; + } + + /** + * It is possible to make a comment as a teacher without creating an entry in the submission table, so this is required + * to find those entries. + * + * @param int $userid The user ID that we are finding contexts for. + * @param contextlist $contextlist A context list to add sql and params to for contexts. + */ + public static function get_context_for_userid_within_submission(int $userid, contextlist $contextlist) { + $sql = "SELECT contextid + FROM {comments} + WHERE component = :component + AND commentarea = :commentarea + AND userid = :userid"; + $params = ['userid' => $userid, 'component' => 'assignsubmission_comments', 'commentarea' => 'submission_comments']; + $contextlist->add_from_sql($sql, $params); + } + + /** + * Due to the fact that we can't rely on the queries in the mod_assign provider we have to add some additional sql. + * + * @param \mod_assign\privacy\useridlist $useridlist An object for obtaining user IDs of students. + */ + public static function get_student_user_ids(\mod_assign\privacy\useridlist $useridlist) { + $params = ['assignid' => $useridlist->get_assignid(), 'commentuserid' => $useridlist->get_teacherid(), + 'commentuserid2' => $useridlist->get_teacherid()]; + $sql = "SELECT DISTINCT c.userid AS id + FROM {comments} c + JOIN (SELECT c.itemid + FROM {comments} c + JOIN {assign_submission} s ON s.id = c.itemid AND s.assignment = :assignid + WHERE c.userid = :commentuserid) aa ON aa.itemid = c.itemid + WHERE c.userid NOT IN (:commentuserid2)"; + $useridlist->add_from_sql($sql, $params); + } + + /** + * Export all user data for this plugin. + * + * @param assign_plugin_request_data $exportdata Data used to determine which context and user to export and other useful + * information to help with exporting. + */ + public static function export_submission_user_data(assign_plugin_request_data $exportdata) { + $component = 'assignsubmission_comments'; + $commentarea = 'submission_comments'; + + $userid = ($exportdata->get_user() != null); + $submission = $exportdata->get_pluginobject(); + + // For the moment we are only showing the comments made by this user. + comments_provider::export_comments($exportdata->get_context(), $component, $commentarea, $submission->id, + $exportdata->get_subcontext(), $userid); + } + + /** + * Delete all the comments made for this context. + * + * @param assign_plugin_request_data $requestdata Data to fulfill the deletion request. + */ + public static function delete_submission_for_context(assign_plugin_request_data $requestdata) { + comments_provider::delete_comments_for_all_users($requestdata->get_context(), 'assignsubmission_comments', + 'submission_comments'); + } + + /** + * A call to this method should delete user data (where practical) using the userid and submission. + * + * @param assign_plugin_request_data $exportdata Details about the user and context to focus the deletion. + */ + public static function delete_submission_for_userid(assign_plugin_request_data $exportdata) { + // Create an approved context list to delete the comments. + $contextlist = new \core_privacy\local\request\approved_contextlist($exportdata->get_user(), 'assignsubmission_comments', + [$exportdata->get_context()->id]); + comments_provider::delete_comments_for_user($contextlist, 'assignsubmission_comments', 'submission_comments'); + } +} diff --git a/mod/assign/submission/comments/lang/en/assignsubmission_comments.php b/mod/assign/submission/comments/lang/en/assignsubmission_comments.php index d63312b126b30..78da642ecbcf0 100644 --- a/mod/assign/submission/comments/lang/en/assignsubmission_comments.php +++ b/mod/assign/submission/comments/lang/en/assignsubmission_comments.php @@ -24,6 +24,7 @@ $string['blindmarkingname'] = 'Participant {$a}'; $string['blindmarkingviewfullname'] = 'Participant {$a->participantnumber} ({$a->participantfullname})'; +$string['privacy:metadata:commentpurpose'] = 'Comments between the student and teacher about a submission.'; $string['default'] = 'Enabled by default'; $string['default_help'] = 'If set, this submission method will be enabled by default for all new assignments.'; $string['enabled'] = 'Submission comments'; diff --git a/mod/assign/submission/comments/tests/privacy_test.php b/mod/assign/submission/comments/tests/privacy_test.php new file mode 100644 index 0000000000000..471174ba8cf7f --- /dev/null +++ b/mod/assign/submission/comments/tests/privacy_test.php @@ -0,0 +1,253 @@ +. + +/** + * Unit tests for assignsubmission_comments. + * + * @package assignsubmission_comments + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/mod/assign/tests/privacy_test.php'); + +/** + * Unit tests for mod/assign/submission/comments/classes/privacy/ + * + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class assignsubmission_comments_privacy_testcase extends \mod_assign\tests\mod_assign_privacy_testcase { + + /** + * Convenience function for creating feedback data. + * + * @param object $assign assign object + * @param stdClass $student user object + * @param string $submissiontext Submission text + * @return array Submission plugin object and the submission object and the comment object. + */ + protected function create_comment_submission($assign, $student, $submissiontext) { + + $submission = $assign->get_user_submission($student->id, true); + + $plugin = $assign->get_submission_plugin_by_type('comments'); + + $context = $assign->get_context(); + $options = new stdClass(); + $options->area = 'submission_comments'; + $options->course = $assign->get_course(); + $options->context = $context; + $options->itemid = $submission->id; + $options->component = 'assignsubmission_comments'; + $options->showcount = true; + $options->displaycancel = true; + + $comment = new comment($options); + $comment->set_post_permission(true); + + $this->setUser($student); + + $comment->add($submissiontext); + + return [$plugin, $submission, $comment]; + } + + /** + * Quick test to make sure that get_metadata returns something. + */ + public function test_get_metadata() { + $collection = new \core_privacy\local\metadata\collection('assignsubmission_comments'); + $collection = \assignsubmission_comments\privacy\provider::get_metadata($collection); + $this->assertNotEmpty($collection); + } + + /** + * Test returning the context for a user who has made a comment in an assignment. + */ + public function test_get_context_for_userid_within_submission() { + $this->resetAfterTest(); + // Create course, assignment, submission, and then a feedback comment. + $course = $this->getDataGenerator()->create_course(); + // Student. + $user1 = $this->getDataGenerator()->create_user(); + // Teacher. + $user2 = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); + $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'editingteacher'); + $assign = $this->create_instance(['course' => $course]); + + $context = $assign->get_context(); + + $studentcomment = 'Comment from user 1'; + list($plugin, $submission, $comment) = $this->create_comment_submission($assign, $user1, $studentcomment); + $teachercomment = 'From the teacher'; + $this->setUser($user2); + $comment->add($teachercomment); + + $contextlist = new \core_privacy\local\request\contextlist(); + \assignsubmission_comments\privacy\provider::get_context_for_userid_within_submission($user2->id, $contextlist); + $this->assertEquals($context->id, $contextlist->get_contextids()[0]); + } + + /** + * Test returning student ids given a user ID. + */ + public function test_get_student_user_ids() { + $this->resetAfterTest(); + // Create course, assignment, submission, and then a feedback comment. + $course = $this->getDataGenerator()->create_course(); + // Student. + $user1 = $this->getDataGenerator()->create_user(); + // Teacher. + $user2 = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); + $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'editingteacher'); + $assign = $this->create_instance(['course' => $course]); + + $context = $assign->get_context(); + + $studentcomment = 'Comment from user 1'; + list($plugin, $submission, $comment) = $this->create_comment_submission($assign, $user1, $studentcomment); + $teachercomment = 'From the teacher'; + $this->setUser($user2); + $comment->add($teachercomment); + + $useridlist = new mod_assign\privacy\useridlist($user2->id, $assign->get_instance()->id); + \assignsubmission_comments\privacy\provider::get_student_user_ids($useridlist); + $this->assertEquals($user1->id, $useridlist->get_userids()[0]->id); + } + + /** + * Test that comments are exported for a user. + */ + public function test_export_submission_user_data() { + $this->resetAfterTest(); + // Create course, assignment, submission, and then a feedback comment. + $course = $this->getDataGenerator()->create_course(); + // Student. + $user1 = $this->getDataGenerator()->create_user(); + // Teacher. + $user2 = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); + $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'editingteacher'); + $assign = $this->create_instance(['course' => $course]); + + $context = $assign->get_context(); + + $studentcomment = 'Comment from user 1'; + list($plugin, $submission, $comment) = $this->create_comment_submission($assign, $user1, $studentcomment); + $teachercomment = 'From the teacher'; + $this->setUser($user2); + $comment->add($teachercomment); + + $writer = \core_privacy\local\request\writer::with_context($context); + $this->assertFalse($writer->has_any_data()); + + // The student should be able to see the teachers feedback. + $exportdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign, $submission); + \assignsubmission_comments\privacy\provider::export_submission_user_data($exportdata); + $exportedcomments = $writer->get_data(['Comments']); + $this->assertCount(2, $exportedcomments->comments); + $this->assertContains($studentcomment, $exportedcomments->comments[0]->content); + $this->assertContains($teachercomment, $exportedcomments->comments[1]->content); + } + + /** + * Test that all comments are deleted for this context. + */ + public function test_delete_submission_for_context() { + global $DB; + $this->resetAfterTest(); + + // Create course, assignment, submission, and then a feedback comment. + $course = $this->getDataGenerator()->create_course(); + // Student. + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + // Teacher. + $user3 = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); + $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student'); + $this->getDataGenerator()->enrol_user($user3->id, $course->id, 'editingteacher'); + $assign = $this->create_instance(['course' => $course]); + + $context = $assign->get_context(); + + $studentcomment = 'Comment from user 1'; + list($plugin, $submission, $comment) = $this->create_comment_submission($assign, $user1, $studentcomment); + $studentcomment = 'Comment from user 2'; + list($plugin2, $submission2, $comment2) = $this->create_comment_submission($assign, $user2, $studentcomment); + $teachercomment1 = 'From the teacher'; + $teachercomment2 = 'From the teacher for second student.'; + $this->setUser($user3); + $comment->add($teachercomment1); + $comment2->add($teachercomment2); + + // Only need the context in this plugin for this operation. + $requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign); + \assignsubmission_comments\privacy\provider::delete_submission_for_context($requestdata); + + $results = $DB->get_records('comments', ['contextid' => $context->id]); + $this->assertEmpty($results); + } + + /** + * Test that the comments for a user are deleted. + */ + public function test_delete_submission_for_userid() { + global $DB; + $this->resetAfterTest(); + // Create course, assignment, submission, and then a feedback comment. + $course = $this->getDataGenerator()->create_course(); + // Student. + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + // Teacher. + $user3 = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); + $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student'); + $this->getDataGenerator()->enrol_user($user3->id, $course->id, 'editingteacher'); + $assign = $this->create_instance(['course' => $course]); + + $context = $assign->get_context(); + + $studentcomment = 'Comment from user 1'; + list($plugin, $submission, $comment) = $this->create_comment_submission($assign, $user1, $studentcomment); + $studentcomment = 'Comment from user 2'; + list($plugin2, $submission2, $comment2) = $this->create_comment_submission($assign, $user2, $studentcomment); + $teachercomment1 = 'From the teacher'; + $teachercomment2 = 'From the teacher for second student.'; + $this->setUser($user3); + $comment->add($teachercomment1); + $comment2->add($teachercomment2); + + // Provide full details to delete the comments. + $requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign, null, [], $user1); + \assignsubmission_comments\privacy\provider::delete_submission_for_userid($requestdata); + + $results = $DB->get_records('comments', ['contextid' => $context->id]); + // We are only deleting the comments for user1 (one comment) so we should have three left. + $this->assertCount(3, $results); + foreach ($results as $result) { + // Check that none of the comments are from user1. + $this->assertNotEquals($user1->id, $result->userid); + } + } +} diff --git a/mod/assign/submission/file/classes/privacy/provider.php b/mod/assign/submission/file/classes/privacy/provider.php new file mode 100644 index 0000000000000..a10e67a281b31 --- /dev/null +++ b/mod/assign/submission/file/classes/privacy/provider.php @@ -0,0 +1,143 @@ +. + +/** + * Privacy class for requesting user data. + * + * @package assignsubmission_file + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace assignsubmission_file\privacy; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/assign/locallib.php'); + +use \core_privacy\local\metadata\collection; +use \core_privacy\local\metadata\provider as metadataprovider; +use \core_privacy\local\request\writer; +use \core_privacy\local\request\contextlist; +use \mod_assign\privacy\assign_plugin_request_data; + +/** + * Privacy class for requesting user data. + * + * @package assignsubmission_file + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements metadataprovider, \mod_assign\privacy\assignsubmission_provider { + + /** + * Return meta data about this plugin. + * + * @param collection $collection A list of information to add to. + * @return collection Return the collection after adding to it. + */ + public static function get_metadata(collection $collection) : collection { + $collection->link_subsystem('core_files', 'privacy:metadata:filepurpose'); + return $collection; + } + + /** + * This is covered by mod_assign provider and the query on assign_submissions. + * + * @param int $userid The user ID that we are finding contexts for. + * @param contextlist $contextlist A context list to add sql and params to for contexts. + */ + public static function get_context_for_userid_within_submission(int $userid, contextlist $contextlist) { + // This is already fetched from mod_assign. + } + + /** + * This is also covered by the mod_assign provider and it's queries. + * + * @param \mod_assign\privacy\useridlist $useridlist An object for obtaining user IDs of students. + */ + public static function get_student_user_ids(\mod_assign\privacy\useridlist $useridlist) { + // No need. + } + + /** + * Export all user data for this plugin. + * + * @param assign_plugin_request_data $exportdata Data used to determine which context and user to export and other useful + * information to help with exporting. + */ + public static function export_submission_user_data(assign_plugin_request_data $exportdata) { + // We currently don't show submissions to teachers when exporting their data. + $context = $exportdata->get_context(); + if ($exportdata->get_user() != null) { + return null; + } + $user = new \stdClass(); + $assign = $exportdata->get_assign(); + $plugin = $assign->get_plugin_by_type('assignsubmission', 'file'); + $files = $plugin->get_files($exportdata->get_pluginobject(), $user); + foreach ($files as $file) { + $userid = $exportdata->get_pluginobject()->userid; + writer::with_context($exportdata->get_context())->export_file($exportdata->get_subcontext(), $file); + + // Plagiarism data. + $coursecontext = $context->get_course_context(); + \core_plagiarism\privacy\provider::export_plagiarism_user_data($userid, $context, $exportdata->get_subcontext(), [ + 'cmid' => $context->instanceid, + 'course' => $coursecontext->instanceid, + 'userid' => $userid, + 'file' => $file + ]); + } + } + + /** + * Any call to this method should delete all user data for the context defined in the deletion_criteria. + * + * @param assign_plugin_request_data $requestdata Information useful for deleting user data. + */ + public static function delete_submission_for_context(assign_plugin_request_data $requestdata) { + global $DB; + + \core_plagiarism\privacy\provider::delete_plagiarism_for_context($requestdata->get_context()); + + $fs = get_file_storage(); + $fs->delete_area_files($requestdata->get_context()->id, 'assignsubmission_file', ASSIGNSUBMISSION_FILE_FILEAREA); + + // Delete records from assignsubmission_file table. + $DB->delete_records('assignsubmission_file', ['assignment' => $requestdata->get_assign()->get_instance()->id]); + } + + /** + * A call to this method should delete user data (where practicle) using the userid and submission. + * + * @param assign_plugin_request_data $deletedata Details about the user and context to focus the deletion. + */ + public static function delete_submission_for_userid(assign_plugin_request_data $deletedata) { + global $DB; + + \core_plagiarism\privacy\provider::delete_plagiarism_for_user($deletedata->get_user()->id, $deletedata->get_context()); + + $submissionid = $deletedata->get_pluginobject()->id; + + $fs = get_file_storage(); + $fs->delete_area_files($deletedata->get_context()->id, 'assignsubmission_file', ASSIGNSUBMISSION_FILE_FILEAREA, + $submissionid); + + $DB->delete_records('assignsubmission_file', ['assignment' => $deletedata->get_assign()->get_instance()->id, + 'submission' => $submissionid]); + } +} diff --git a/mod/assign/submission/file/lang/en/assignsubmission_file.php b/mod/assign/submission/file/lang/en/assignsubmission_file.php index 45dd3ff9f02c7..f566ef52c5ad7 100644 --- a/mod/assign/submission/file/lang/en/assignsubmission_file.php +++ b/mod/assign/submission/file/lang/en/assignsubmission_file.php @@ -43,6 +43,7 @@ $string['maximumsubmissionsize_help'] = 'Files uploaded by students may be up to this size.'; $string['numfilesforlog'] = 'The number of file(s) : {$a} file(s).'; $string['pluginname'] = 'File submissions'; +$string['privacy:metadata:filepurpose'] = 'The files loaded for this assignment submission'; $string['siteuploadlimit'] = 'Site upload limit'; $string['submissionfilearea'] = 'Uploaded submission files'; // Deprecated since Moodle 3.4. diff --git a/mod/assign/submission/file/tests/privacy_test.php b/mod/assign/submission/file/tests/privacy_test.php new file mode 100644 index 0000000000000..61f68412ad702 --- /dev/null +++ b/mod/assign/submission/file/tests/privacy_test.php @@ -0,0 +1,175 @@ +. + +/** + * Unit tests for assignsubmission_file. + * + * @package assignsubmission_file + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/mod/assign/tests/privacy_test.php'); + +/** + * Unit tests for mod/assign/submission/file/classes/privacy/ + * + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class assignsubmission_file_privacy_testcase extends \mod_assign\tests\mod_assign_privacy_testcase { + + /** + * Convenience function for creating feedback data. + * + * @param object $assign assign object + * @param stdClass $student user object + * @param string $filename filename for the file submission + * @return array Submission plugin object and the submission object. + */ + protected function create_file_submission($assign, $student, $filename) { + global $CFG; + // Create a file submission with the test pdf. + $submission = $assign->get_user_submission($student->id, true); + + $this->setUser($student->id); + + $fs = get_file_storage(); + $pdfsubmission = (object) array( + 'contextid' => $assign->get_context()->id, + 'component' => 'assignsubmission_file', + 'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA, + 'itemid' => $submission->id, + 'filepath' => '/', + 'filename' => $filename + ); + $sourcefile = $CFG->dirroot.'/mod/assign/feedback/editpdf/tests/fixtures/submission.pdf'; + $fi = $fs->create_file_from_pathname($pdfsubmission, $sourcefile); + + $data = new \stdClass(); + $plugin = $assign->get_submission_plugin_by_type('file'); + $plugin->save($submission, $data); + + return [$plugin, $submission]; + } + + /** + * Quick test to make sure that get_metadata returns something. + */ + public function test_get_metadata() { + $collection = new \core_privacy\local\metadata\collection('assignsubmission_file'); + $collection = \assignsubmission_file\privacy\provider::get_metadata($collection); + $this->assertNotEmpty($collection); + } + + /** + * Test that submission files are exported for a user. + */ + public function test_export_submission_user_data() { + $this->resetAfterTest(); + // Create course, assignment, submission, and then a feedback comment. + $course = $this->getDataGenerator()->create_course(); + // Student. + $user1 = $this->getDataGenerator()->create_user(); + // Teacher. + $user2 = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); + $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'editingteacher'); + $assign = $this->create_instance(['course' => $course]); + + $context = $assign->get_context(); + + $studentfilename = 'user1file.pdf'; + list($plugin, $submission) = $this->create_file_submission($assign, $user1, $studentfilename); + + $writer = \core_privacy\local\request\writer::with_context($context); + $this->assertFalse($writer->has_any_data()); + + // The student should have a file submission. + $exportdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign, $submission, ['Attempt 1']); + \assignsubmission_file\privacy\provider::export_submission_user_data($exportdata); + // print_object($writer); + $storedfile = $writer->get_files(['Attempt 1'])['user1file.pdf']; + $this->assertInstanceOf('stored_file', $storedfile); + $this->assertEquals($studentfilename, $storedfile->get_filename()); + } + + /** + * Test that all submission files are deleted for this context. + */ + public function test_delete_submission_for_context() { + $this->resetAfterTest(); + // Create course, assignment, submission, and then a feedback comment. + $course = $this->getDataGenerator()->create_course(); + // Student. + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); + $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student'); + + $assign = $this->create_instance(['course' => $course]); + + $context = $assign->get_context(); + + $studentfilename = 'user1file.pdf'; + list($plugin, $submission) = $this->create_file_submission($assign, $user1, $studentfilename); + $student2filename = 'user2file.pdf'; + list($plugin2, $submission2) = $this->create_file_submission($assign, $user2, $studentfilename); + + // Only need the context and assign object in this plugin for this operation. + $requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign); + \assignsubmission_file\privacy\provider::delete_submission_for_context($requestdata); + // This checks that there are no files in this submission. + $this->assertTrue($plugin->is_empty($submission)); + $this->assertTrue($plugin2->is_empty($submission2)); + } + + /** + * Test that the comments for a user are deleted. + */ + public function test_delete_submission_for_userid() { + $this->resetAfterTest(); + // Create course, assignment, submission, and then a feedback comment. + $course = $this->getDataGenerator()->create_course(); + // Student. + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); + $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student'); + + $assign = $this->create_instance(['course' => $course]); + + $context = $assign->get_context(); + + $studentfilename = 'user1file.pdf'; + list($plugin, $submission) = $this->create_file_submission($assign, $user1, $studentfilename); + $student2filename = 'user2file.pdf'; + list($plugin2, $submission2) = $this->create_file_submission($assign, $user2, $studentfilename); + + // Only need the context and assign object in this plugin for this operation. + $requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign, $submission, [], $user1); + \assignsubmission_file\privacy\provider::delete_submission_for_userid($requestdata); + // This checks that there are no files in this submission. + $this->assertTrue($plugin->is_empty($submission)); + // There should be files here. + $this->assertFalse($plugin2->is_empty($submission2)); + } +} diff --git a/mod/assign/submission/onlinetext/classes/privacy/provider.php b/mod/assign/submission/onlinetext/classes/privacy/provider.php new file mode 100644 index 0000000000000..27a7d5668f730 --- /dev/null +++ b/mod/assign/submission/onlinetext/classes/privacy/provider.php @@ -0,0 +1,162 @@ +. + +/** + * Privacy class for requesting user data. + * + * @package assignsubmission_onlinetext + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace assignsubmission_onlinetext\privacy; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/assign/locallib.php'); + +use \core_privacy\local\metadata\collection; +use \core_privacy\local\metadata\provider as metadataprovider; +use \core_privacy\local\request\writer; +use \core_privacy\local\request\contextlist; +use \mod_assign\privacy\assign_plugin_request_data; + +/** + * Privacy class for requesting user data. + * + * @package assignsubmission_onlinetext + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements metadataprovider, \mod_assign\privacy\assignsubmission_provider { + + /** + * Return meta data about this plugin. + * + * @param collection $collection A list of information to add to. + * @return collection Return the collection after adding to it. + */ + public static function get_metadata(collection $collection) : collection { + $detail = [ + 'assignment' => 'privacy:metadata:assignmentid', + 'submission' => 'privacy:metadata:submissionpurpose', + 'onlinetext' => 'privacy:metadata:textpurpose' + ]; + $collection->add_database_table('assignsubmission_onlinetext', $detail, 'privacy:metadata:tablepurpose'); + $collection->link_subsystem('core_files', 'privacy:metadata:filepurpose'); + return $collection; + } + + /** + * This is covered by mod_assign provider and the query on assign_submissions. + * + * @param int $userid The user ID that we are finding contexts for. + * @param contextlist $contextlist A context list to add sql and params to for contexts. + */ + public static function get_context_for_userid_within_submission(int $userid, contextlist $contextlist) { + // This is already fetched from mod_assign. + } + + /** + * This is also covered by the mod_assign provider and it's queries. + * + * @param \mod_assign\privacy\useridlist $useridlist An object for obtaining user IDs of students. + */ + public static function get_student_user_ids(\mod_assign\privacy\useridlist $useridlist) { + // No need. + } + + /** + * Export all user data for this plugin. + * + * @param assign_plugin_request_data $exportdata Data used to determine which context and user to export and other useful + * information to help with exporting. + */ + public static function export_submission_user_data(assign_plugin_request_data $exportdata) { + // We currently don't show submissions to teachers when exporting their data. + if ($exportdata->get_user() != null) { + return null; + } + // Retrieve text for this submission. + $assign = $exportdata->get_assign(); + $plugin = $assign->get_plugin_by_type('assignsubmission', 'onlinetext'); + $submission = $exportdata->get_pluginobject(); + $editortext = $plugin->get_editor_text('onlinetext', $submission->id); + $context = $exportdata->get_context(); + if (!empty($editortext)) { + $submissiontext = new \stdClass(); + $submissiontext->text = writer::with_context($context)->rewrite_pluginfile_urls([], '', '', '', $editortext); + $currentpath = $exportdata->get_subcontext(); + $currentpath[] = get_string('privacy:path', 'assignsubmission_onlinetext'); + writer::with_context($context) + ->export_area_files($currentpath, 'assignsubmission_onlinetext', 'submissions_onlinetext', $submission->id) + // Add the text to the exporter. + ->export_data($currentpath, $submissiontext); + + // Handle plagiarism data. + $coursecontext = $context->get_course_context(); + $userid = $submission->userid; + \core_plagiarism\privacy\provider::export_plagiarism_user_data($userid, $context, $currentpath, [ + 'cmid' => $context->instanceid, + 'course' => $coursecontext->instanceid, + 'userid' => $userid, + 'content' => $editortext, + 'assignment' => $submission->assignment + ]); + } + } + + /** + * Any call to this method should delete all user data for the context defined in the deletion_criteria. + * + * @param assign_plugin_request_data $requestdata Data useful for deleting user data from this sub-plugin. + */ + public static function delete_submission_for_context(assign_plugin_request_data $requestdata) { + global $DB; + + \core_plagiarism\privacy\provider::delete_plagiarism_for_context($requestdata->get_context()); + + // Delete related files. + $fs = get_file_storage(); + $fs->delete_area_files($requestdata->get_context()->id, 'assignsubmission_onlinetext', + ASSIGNSUBMISSION_ONLINETEXT_FILEAREA); + + // Delete the records in the table. + $DB->delete_records('assignsubmission_onlinetext', ['assignment' => $requestdata->get_assign()->get_instance()->id]); + } + + /** + * A call to this method should delete user data (where practicle) from the userid and context. + * + * @param assign_plugin_request_data $deletedata Details about the user and context to focus the deletion. + */ + public static function delete_submission_for_userid(assign_plugin_request_data $deletedata) { + global $DB; + + \core_plagiarism\privacy\provider::delete_plagiarism_for_user($deletedata->get_user()->id, $deletedata->get_context()); + + $submissionid = $deletedata->get_pluginobject()->id; + + // Delete related files. + $fs = get_file_storage(); + $fs->delete_area_files($deletedata->get_context()->id, 'assignsubmission_onlinetext', ASSIGNSUBMISSION_ONLINETEXT_FILEAREA, + $submissionid); + + // Delete the records in the table. + $DB->delete_records('assignsubmission_onlinetext', ['assignment' => $deletedata->get_assign()->get_instance()->id, + 'submission' => $submissionid]); + } +} diff --git a/mod/assign/submission/onlinetext/lang/en/assignsubmission_onlinetext.php b/mod/assign/submission/onlinetext/lang/en/assignsubmission_onlinetext.php index bb4e383fc9152..1b655783f7416 100644 --- a/mod/assign/submission/onlinetext/lang/en/assignsubmission_onlinetext.php +++ b/mod/assign/submission/onlinetext/lang/en/assignsubmission_onlinetext.php @@ -32,9 +32,15 @@ $string['onlinetext'] = 'Online text'; $string['onlinetextfilename'] = 'onlinetext.html'; $string['onlinetextsubmission'] = 'Allow online text submission'; -$string['pluginname'] = 'Online text submissions'; $string['numwords'] = '({$a} words)'; $string['numwordsforlog'] = 'Submission word count: {$a} words'; +$string['pluginname'] = 'Online text submissions'; +$string['privacy:metadata:assignmentid'] = 'Assignment identifier'; +$string['privacy:metadata:filepurpose'] = 'Files that are embedded in the text submission.'; +$string['privacy:metadata:submissionpurpose'] = 'The submission ID that links to submissions for the user.'; +$string['privacy:metadata:tablepurpose'] = 'Stores the text submission for each attempt.'; +$string['privacy:metadata:textpurpose'] = 'The actual text submitted for this attempt of the assignment.'; +$string['privacy:path'] = 'Submission Text'; $string['wordlimit'] = 'Word limit'; $string['wordlimit_help'] = 'If online text submissions are enabled, this is the maximum number ' . 'of words that each student will be allowed to submit.'; diff --git a/mod/assign/submission/onlinetext/tests/privacy_test.php b/mod/assign/submission/onlinetext/tests/privacy_test.php new file mode 100644 index 0000000000000..ce1dd5062ad56 --- /dev/null +++ b/mod/assign/submission/onlinetext/tests/privacy_test.php @@ -0,0 +1,164 @@ +. + +/** + * Unit tests for assignsubmission_onlinetext. + * + * @package assignsubmission_onlinetext + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/mod/assign/tests/privacy_test.php'); + +/** + * Unit tests for mod/assign/submission/onlinetext/classes/privacy/ + * + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class assignsubmission_online_privacy_testcase extends \mod_assign\tests\mod_assign_privacy_testcase { + + /** + * Convenience function for creating feedback data. + * + * @param object $assign assign object + * @param stdClass $student user object + * @param string $text Submission text. + * @return array Submission plugin object and the submission object. + */ + protected function create_online_submission($assign, $student, $text) { + global $CFG; + + $this->setUser($student->id); + $submission = $assign->get_user_submission($student->id, true); + $data = new stdClass(); + $data->onlinetext_editor = array( + 'itemid' => file_get_unused_draft_itemid(), + 'text' => $text, + 'format' => FORMAT_PLAIN + ); + + $submission = $assign->get_user_submission($student->id, true); + + $plugin = $assign->get_submission_plugin_by_type('onlinetext'); + $plugin->save($submission, $data); + + return [$plugin, $submission]; + } + + /** + * Quick test to make sure that get_metadata returns something. + */ + public function test_get_metadata() { + $collection = new \core_privacy\local\metadata\collection('assignsubmission_file'); + $collection = \assignsubmission_onlinetext\privacy\provider::get_metadata($collection); + $this->assertNotEmpty($collection); + } + + /** + * Test that submission files and text are exported for a user. + */ + public function test_export_submission_user_data() { + $this->resetAfterTest(); + // Create course, assignment, submission, and then a feedback comment. + $course = $this->getDataGenerator()->create_course(); + // Student. + $user1 = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); + $assign = $this->create_instance(['course' => $course]); + + $context = $assign->get_context(); + + $submissiontext = 'Just some text'; + list($plugin, $submission) = $this->create_online_submission($assign, $user1, $submissiontext); + + $writer = \core_privacy\local\request\writer::with_context($context); + $this->assertFalse($writer->has_any_data()); + + // The student should have some text submitted. + $exportdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign, $submission, ['Attempt 1']); + \assignsubmission_onlinetext\privacy\provider::export_submission_user_data($exportdata); + $this->assertEquals($submissiontext, $writer->get_data(['Attempt 1', + get_string('privacy:path', 'assignsubmission_onlinetext')])->text); + } + + /** + * Test that all submission files are deleted for this context. + */ + public function test_delete_submission_for_context() { + $this->resetAfterTest(); + // Create course, assignment, submission, and then a feedback comment. + $course = $this->getDataGenerator()->create_course(); + // Student. + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); + $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student'); + + $assign = $this->create_instance(['course' => $course]); + + $context = $assign->get_context(); + + $studenttext = 'Student one\'s text.'; + list($plugin, $submission) = $this->create_online_submission($assign, $user1, $studenttext); + $studenttext2 = 'Student two\'s text.'; + list($plugin2, $submission2) = $this->create_online_submission($assign, $user2, $studenttext2); + + // Only need the context and assign object in this plugin for this operation. + $requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign); + \assignsubmission_onlinetext\privacy\provider::delete_submission_for_context($requestdata); + // This checks that there is no content for these submissions. + $this->assertTrue($plugin->is_empty($submission)); + $this->assertTrue($plugin2->is_empty($submission2)); + } + + /** + * Test that the comments for a user are deleted. + */ + public function test_delete_submission_for_userid() { + $this->resetAfterTest(); + // Create course, assignment, submission, and then a feedback comment. + $course = $this->getDataGenerator()->create_course(); + // Student. + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); + $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student'); + + $assign = $this->create_instance(['course' => $course]); + + $context = $assign->get_context(); + + $studenttext = 'Student one\'s text.'; + list($plugin, $submission) = $this->create_online_submission($assign, $user1, $studenttext); + $studenttext2 = 'Student two\'s text.'; + list($plugin2, $submission2) = $this->create_online_submission($assign, $user2, $studenttext2); + + // Need more data for this operation. + $requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign, $submission, [], $user1); + \assignsubmission_onlinetext\privacy\provider::delete_submission_for_userid($requestdata); + // This checks that there is no content for the first submission. + $this->assertTrue($plugin->is_empty($submission)); + // But there is for the second submission. + $this->assertFalse($plugin2->is_empty($submission2)); + } +} diff --git a/mod/assign/tests/privacy_submission_legacy_polyfill_test.php b/mod/assign/tests/privacy_submission_legacy_polyfill_test.php new file mode 100644 index 0000000000000..91401395dc55f --- /dev/null +++ b/mod/assign/tests/privacy_submission_legacy_polyfill_test.php @@ -0,0 +1,226 @@ +. +/** + * Unit tests for the privacy legacy polyfill for mod_assign. + * + * @package mod_assign + * @category test + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/mod/assign/submissionplugin.php'); +require_once($CFG->dirroot . '/mod/assign/submission/comments/locallib.php'); + +/** + * Unit tests for the assignment submission subplugins API's privacy legacy_polyfill. + * + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class mod_assignsubmission_privacy_legacy_polyfill_test extends advanced_testcase { + + /** + * Convenience function to create an instance of an assignment. + * + * @param array $params Array of parameters to pass to the generator + * @return assign The assign class. + */ + protected function create_instance($params = array()) { + $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign'); + $instance = $generator->create_instance($params); + $cm = get_coursemodule_from_instance('assign', $instance->id); + $context = \context_module::instance($cm->id); + return new \assign($context, $cm, $params['course']); + } + + /** + * Test the get_context_for_userid_within_submission shim. + */ + public function test_get_context_for_userid_within_submission() { + + $userid = 21; + $contextlist = new \core_privacy\local\request\contextlist(); + $mock = $this->createMock(test_assignsubmission_legacy_polyfill_mock_wrapper::class); + $mock->expects($this->once()) + ->method('get_return_value') + ->with('_get_context_for_userid_within_submission', [$userid, $contextlist]); + test_legacy_polyfill_submission_provider::$mock = $mock; + test_legacy_polyfill_submission_provider::get_context_for_userid_within_submission($userid, $contextlist); + } + + /** + * Test the get_student_user_ids shim. + */ + public function test_get_student_user_ids() { + $teacherid = 107; + $assignid = 15; + $useridlist = new \mod_assign\privacy\useridlist($teacherid, $assignid); + $mock = $this->createMock(test_assignsubmission_legacy_polyfill_mock_wrapper::class); + $mock->expects($this->once()) + ->method('get_return_value') + ->with('_get_student_user_ids', [$useridlist]); + test_legacy_polyfill_submission_provider::$mock = $mock; + test_legacy_polyfill_submission_provider::get_student_user_ids($useridlist); + } + + /** + * Test the export_submission_user_data shim. + */ + public function test_export_submission_user_data() { + $this->resetAfterTest(); + $course = $this->getDataGenerator()->create_course(); + $assign = $this->create_instance(['course' => $course]); + $context = context_system::instance(); + $subplugin = new assign_submission_comments($assign, 'comment'); + $requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign); + $mock = $this->createMock(test_assignsubmission_legacy_polyfill_mock_wrapper::class); + $mock->expects($this->once()) + ->method('get_return_value') + ->with('_export_submission_user_data', [$requestdata]); + test_legacy_polyfill_submission_provider::$mock = $mock; + test_legacy_polyfill_submission_provider::export_submission_user_data($requestdata); + } + + /** + * Test the delete_submission_for_context shim. + */ + public function test_delete_submission_for_context() { + $this->resetAfterTest(); + $course = $this->getDataGenerator()->create_course(); + $assign = $this->create_instance(['course' => $course]); + $context = context_system::instance(); + $subplugin = new assign_submission_comments($assign, 'comment'); + $requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign); + $mock = $this->createMock(test_assignsubmission_legacy_polyfill_mock_wrapper::class); + $mock->expects($this->once()) + ->method('get_return_value') + ->with('_delete_submission_for_context', [$requestdata]); + test_legacy_polyfill_submission_provider::$mock = $mock; + test_legacy_polyfill_submission_provider::delete_submission_for_context($requestdata); + } + + /** + * Test the delete submission for grade shim. + */ + public function test_delete_submission_for_userid() { + $this->resetAfterTest(); + $course = $this->getDataGenerator()->create_course(); + $assign = $this->create_instance(['course' => $course]); + $context = context_system::instance(); + $subplugin = new assign_submission_comments($assign, 'comment'); + $requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign); + $mock = $this->createMock(test_assignsubmission_legacy_polyfill_mock_wrapper::class); + $mock->expects($this->once()) + ->method('get_return_value') + ->with('_delete_submission_for_userid', [$requestdata]); + test_legacy_polyfill_submission_provider::$mock = $mock; + test_legacy_polyfill_submission_provider::delete_submission_for_userid($requestdata); + } +} +/** + * Legacy polyfill test class for the assignsubmission_provider. + * + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class test_legacy_polyfill_submission_provider implements \mod_assign\privacy\assignsubmission_provider { + use \mod_assign\privacy\submission_legacy_polyfill; + /** + * @var test_legacy_polyfill_submission_provider $mock. + */ + public static $mock = null; + + /** + * Retrieves the contextids associated with the provided userid for this subplugin. + * NOTE if your subplugin must have an entry in the assign_grade table to work, then this + * method can be empty. + * + * @param int $userid The user ID to get context IDs for. + * @param contextlist $contextlist Use add_from_sql with this object to add your context IDs. + */ + public static function _get_context_for_userid_within_submission(int $userid, + \core_privacy\local\request\contextlist $contextlist) { + static::$mock->get_return_value(__FUNCTION__, func_get_args()); + } + + /** + * Returns student user ids related to the provided teacher ID. If it is possible that a student ID will not be returned by + * the sql query in \mod_assign\privacy\provider::find_grader_info() Then you need to provide some sql to retrive those + * student IDs. This is highly likely if you had to fill in get_context_for_userid_within_submission above. + * + * @param useridlist $useridlist A list of user IDs of students graded by this user. + */ + public static function _get_student_user_ids(\mod_assign\privacy\useridlist $useridlist) { + static::$mock->get_return_value(__FUNCTION__, func_get_args()); + } + + /** + * This method is used to export any user data this sub-plugin has using the assign_plugin_request_data object to get the + * context and userid. + * assign_plugin_request_data contains: + * - context + * - grade object + * - current path (subcontext) + * - user object + * + * @param assign_plugin_request_data $exportdata Contains data to help export the user information. + */ + public static function _export_submission_user_data(\mod_assign\privacy\assign_plugin_request_data $exportdata) { + static::$mock->get_return_value(__FUNCTION__, func_get_args()); + } + + /** + * Any call to this method should delete all user data for the context defined in the deletion_criteria. + * assign_plugin_request_data contains: + * - context + * - assign object + * + * @param assign_plugin_request_data $requestdata Data useful for deleting user data from this sub-plugin. + */ + public static function _delete_submission_for_context(\mod_assign\privacy\assign_plugin_request_data $requestdata) { + static::$mock->get_return_value(__FUNCTION__, func_get_args()); + } + + /** + * A call to this method should delete user data (where practicle) from the userid and context. + * assign_plugin_request_data contains: + * - context + * - grade object + * - user object + * - assign object + * + * @param assign_plugin_request_data $requestdata Data useful for deleting user data. + */ + public static function _delete_submission_for_userid(\mod_assign\privacy\assign_plugin_request_data $requestdata) { + static::$mock->get_return_value(__FUNCTION__, func_get_args()); + } +} +/** + * Called inside the polyfill methods in the test polyfill provider, allowing us to ensure these are called with correct params. + * + * @copyright 2018 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class test_assignsubmission_legacy_polyfill_mock_wrapper { + /** + * Get the return value for the specified item. + */ + public function get_return_value() { + } +}