From 5edfaeaa3628cca06f299fad481a1859357c8187 Mon Sep 17 00:00:00 2001 From: Matthew Hilton Date: Tue, 21 Mar 2023 11:18:52 +1000 Subject: [PATCH 1/3] [#69] Update grade me tests for 4.1 --- tests/grade_me_test.php | 51 ++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/tests/grade_me_test.php b/tests/grade_me_test.php index e3b6a86..c6843c1 100644 --- a/tests/grade_me_test.php +++ b/tests/grade_me_test.php @@ -52,8 +52,9 @@ class block_grade_me_testcase extends advanced_testcase { */ protected function create_grade_me_data($file) { // Read the datafile and get the table names. - $dataset = $this->createXMLDataSet(__DIR__ . '/fixtures/' . $file); - $names = array_flip($dataset->getTableNames()); + $dataset = $this->dataset_from_files([__DIR__ . '/fixtures/' . $file]); + $datasetrows = $dataset->get_rows(); + $names = array_keys($datasetrows); // Generate Data. $generator = $this->getDataGenerator(); @@ -64,12 +65,11 @@ protected function create_grade_me_data($file) { $gradeables = array('assign', 'assignment', 'forum', 'glossary', 'quiz'); foreach ($gradeables as $gradeable) { - if (array_key_exists($gradeable, $names)) { + if (in_array($gradeable, $names)) { $pgen = $generator->get_plugin_generator("mod_{$gradeable}"); - $table = $dataset->getTable($gradeable); - $rows = $table->getRowCount(); - for ($row = 0; $row < $rows; $row += 1) { - $fields = $table->getRow($row); + $gradeablerows = $datasetrows[$gradeable]; + for ($row = 0; $row < count($gradeablerows); $row += 1) { + $fields = $gradeablerows[$row]; unset($fields['id']); $fields['course'] = $courses[$fields['course']]->id; $instance = $pgen->create_instance($fields); @@ -151,7 +151,7 @@ protected function create_grade_me_data($file) { foreach ($overrides as $field => $override) { foreach ($override['tables'] as $tablename) { // Skip tables that aren't in the dataset. - if (array_key_exists($tablename, $names)) { + if (in_array($tablename, $names)) { if (!array_key_exists($tablename, $tables)) { $tables[$tablename] = array($field => array()); } @@ -162,26 +162,31 @@ protected function create_grade_me_data($file) { // Perform the overrides. foreach ($tables as $tablename => $translations) { - $table = $dataset->getTable($tablename); - $rows = $table->getRowCount(); foreach ($translations as $column => $values) { foreach ($values as $value) { $list = $value['list']; $field = $value['field']; - for ($row = 0; $row < $rows; $row += 1) { - $index = $table->getValue($row, $column); + $tablerows = $datasetrows[$tablename]; + for ($row = 0; $row < count($tablerows); $row += 1) { + $index = $tablerows[$row][$column]; if (isset(${$list}[$index])) { - $table->setValue($row, $column, ${$list}[$index]->$field); + $datasetrows[$tablename][$row][$column] = ${$list}[$index]->$field; } } } } } - // Load the data. - $filtered = new \PHPUnit\DbUnit\DataSet\Filter($dataset); - $filtered->addExcludeTables($excludes); - $this->loadDataSet($filtered); + // Remove any empty tables (otherwise dataset_from_array breaks). + foreach (array_keys($datasetrows) as $tablename) { + if (empty($datasetrows[$tablename])) { + unset($datasetrows[$tablename]); + } + } + + // Load back in the modified dataset and send to the db. + $finaldataset = $this->dataset_from_array($datasetrows); + $finaldataset->to_database(); // Return the generated users and courses because the tests often need them for result calculations. return array($users, $courses, $plugins); @@ -756,7 +761,7 @@ public function provider_get_content_single_user() { $matches = array( 1 => '/Go to assign/', 2 => '|mod/assign/view.php|', - 3 => '/action=grade&userid=[user0]/', + 3 => '/action=grader&userid=[user0]/', 5 => '/testassignment3/', 6 => '/testassignment4/', ); @@ -829,7 +834,7 @@ public function test_get_content_single_user($plugin, $expectedvalues) { foreach ($expectedvalues as $expected) { $match = str_replace('[user0]', $users[0]->id, $expected); - $this->assertRegExp($match, $content->text); + $this->assertMatchesRegularExpression($match, $content->text); } } @@ -854,8 +859,8 @@ public function provider_get_content_multiple_user() { $matches = array( 1 => '/Go to assign/', 2 => '|mod/assign/view.php|', - 3 => '/action=grade&userid=[user0]/', - 4 => '/action=grade&userid=[user1]/', + 3 => '/action=grader&userid=[user0]/', + 4 => '/action=grader&userid=[user1]/', 5 => '/testassignment3/', 6 => '/testassignment4/' ); @@ -957,7 +962,7 @@ public function test_get_content_multiple_user($plugin, $expectedvalues) { foreach ($expectedvalues as $expected) { $match = str_replace('[user0]', $users[0]->id, $expected); $match = str_replace('[user1]', $users[1]->id, $match); - $this->assertRegExp($match, $content->text); + $this->assertMatchesRegularExpression($match, $content->text); } } @@ -982,7 +987,7 @@ public function test_tree_uses_correct_forum_discussion_id() { $this->assertFalse(empty($gradeables), 'Expected results not found.'); $actual = block_grade_me_tree($gradeables); - $this->assertRegExp('/mod\/forum\/discuss.php\?d=100\#p1/', $actual); + $this->assertMatchesRegularExpression('/mod\/forum\/discuss.php\?d=100\#p1/', $actual); } /** From c5bc589f1dbd584fecede606671be34a98b2585d Mon Sep 17 00:00:00 2001 From: Mikhail Golenkov Date: Sun, 14 Nov 2021 19:51:16 +1100 Subject: [PATCH 2/3] Issue 54: Add GDPR support --- classes/privacy/provider.php | 202 +++++++++++++++++++++++++++++++++++ lang/en/block_grade_me.php | 8 ++ 2 files changed, 210 insertions(+) create mode 100644 classes/privacy/provider.php diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php new file mode 100644 index 0000000..997a82e --- /dev/null +++ b/classes/privacy/provider.php @@ -0,0 +1,202 @@ +. + +/** + * Privacy Subsystem implementation for block_grade_me. + * + * @package block_grade_me + * @copyright 2019 Nathan Nguyen + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace block_grade_me\privacy; + +use core_privacy\local\metadata\collection; +use core_privacy\local\request\approved_contextlist; +use core_privacy\local\request\context; +use core_privacy\local\request\contextlist; +use core_privacy\local\request\approved_userlist; +use core_privacy\local\request\userlist; + +defined('MOODLE_INTERNAL') || die(); + +/** + * The block_grade_me does not store any data. + * + */ +class provider implements + // This plugin has data. + \core_privacy\local\metadata\provider, + + // This plugin is capable of determining which users have data within it. + \core_privacy\local\request\core_userlist_provider, + + // This plugin currently implements the original plugin\provider interface. + \core_privacy\local\request\plugin\provider { + + /** + * Get the list of contexts that contain user information for the specified user. + * + * @param int $userid The user to search. + * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. + */ + public static function get_contexts_for_userid(int $userid): contextlist { + $contextlist = new contextlist(); + + $params = [ + 'contextuser' => CONTEXT_USER, + 'userid' => $userid + ]; + + $sql = "SELECT c.id + FROM {block_grade_me_quiz_ngrade} gme + JOIN {context} c ON c.instanceid = gme.userid AND c.contextlevel = :contextuser + WHERE gme.userid = :userid + GROUP BY c.id"; + + $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 approved contexts to export information for. + */ + public static function export_user_data(approved_contextlist $contextlist) { + global $DB; + + $contexts = $contextlist->get_contexts(); + if (count($contexts) == 0) { + return; + } + $context = reset($contexts); + + if ($context->contextlevel !== CONTEXT_USER) { + return; + } + $userid = $context->instanceid; + + $params = [ + 'userid' => $userid + ]; + + $sql = "SELECT * + FROM {block_grade_me_quiz_ngrade} gme + WHERE gme.userid = :userid"; + + $grademe = $DB->get_records_sql($sql, $params); + + $data = (object) [ + 'grade_me' => $grademe, + ]; + + $subcontext = [ + get_string('pluginname', 'block_grade_me'), + get_string('privacydata', 'block_grade_me') + ]; + + writer::with_context($context)->export_data($subcontext, $data); + } + + /** + * Delete all data for all users in the specified context. + * + * @param context $context The specific context to delete data for. + */ + public static function delete_data_for_all_users_in_context(\context $context) { + global $DB; + + if ($context->contextlevel !== CONTEXT_USER) { + return; + } + $userid = $context->instanceid; + + $DB->delete_records('block_grade_me_quiz_ngrade', ['userid' => $userid]); + } + + /** + * 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; + + $contexts = $contextlist->get_contexts(); + if (count($contexts) == 0) { + return; + } + $context = reset($contexts); + + if ($context->contextlevel !== CONTEXT_USER) { + return; + } + $userid = $context->instanceid; + + $DB->delete_records('block_grade_me_quiz_ngrade', ['userid' => $userid]); + } + + /** + * Returns meta data about this system. + * + * @param collection $collection The initialised collection to add items to. + * @return collection A listing of user data stored through this system. + */ + public static function get_metadata(collection $collection): collection { + $collection->add_database_table('block_grade_me_quiz_ngrade', [ + 'userid' => 'privacy:metadata:block_grade_me_quiz_ngrade:userid', + 'quizid' => 'privacy:metadata:block_grade_me_quiz_ngrade:quizid', + 'questionattemptstepid' => 'privacy:metadata:block_grade_me_quiz_ngrade:questionattemptstepid', + 'courseid' => 'privacy:metadata:block_grade_me_quiz_ngrade:courseid', + 'attemptid' => 'privacy:metadata:block_grade_me_quiz_ngrade:attemptid', + ], 'privacy:metadata:block_grade_me_quiz_ngrade'); + + return $collection; + } + + /** + * Get the list of users who have data within a context. + * + * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. + */ + public static function get_users_in_context(userlist $userlist) { + $context = $userlist->get_context(); + + if (!$context instanceof \context_course) { + return; + } + + $sql = "SELECT * FROM {block_grade_me_quiz_ngrade}"; + $userlist->add_from_sql('userid', $sql, ['courseid' => $context->instanceid]); + } + + /** + * Delete multiple users within a single context. + * + * @param \core_privacy\local\request\approved_userlist $userlist + */ + public static function delete_data_for_users(approved_userlist $userlist) { + $users = $userlist->get_users(); + foreach ($users as $user) { + // Create a contextlist with only system context. + $contextlist = new approved_contextlist($user, 'block_grade_me', [\context_user::instance($user->id)->id]); + self::delete_data_for_user($contextlist); + } + } + +} diff --git a/lang/en/block_grade_me.php b/lang/en/block_grade_me.php index 7481cb7..a00cf7e 100644 --- a/lang/en/block_grade_me.php +++ b/lang/en/block_grade_me.php @@ -52,3 +52,11 @@ $string['quiz_update_ngrade_complete'] = 'Update complete'; $string['quiz_update_ngrade_success'] = 'Quiz attempt list successfully updated, currently there is {$a} questions needing grading.'; + +$string['privacy:metadata:block_grade_me_quiz_ngrade'] = 'Caches information about quizes needing grades.'; +$string['privacy:metadata:block_grade_me_quiz_ngrade:userid'] = 'User id'; +$string['privacy:metadata:block_grade_me_quiz_ngrade:quizid'] = 'Quiz id'; +$string['privacy:metadata:block_grade_me_quiz_ngrade:questionattemptstepid'] = 'Question Attempt'; +$string['privacy:metadata:block_grade_me_quiz_ngrade:courseid'] = 'Course id'; +$string['privacy:metadata:block_grade_me_quiz_ngrade:attemptid'] = 'Attempt id'; +$string['privacydata'] = 'Grade me'; From 94ad3b736d634185f8369d15fd7cddea44e2daf8 Mon Sep 17 00:00:00 2001 From: Sirisha Garapati Date: Mon, 22 May 2023 15:30:32 +1000 Subject: [PATCH 3/3] Fix unit tests to expect correct action url parameter --- tests/grade_me_test.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/grade_me_test.php b/tests/grade_me_test.php index c6843c1..30917cc 100644 --- a/tests/grade_me_test.php +++ b/tests/grade_me_test.php @@ -761,7 +761,7 @@ public function provider_get_content_single_user() { $matches = array( 1 => '/Go to assign/', 2 => '|mod/assign/view.php|', - 3 => '/action=grader&userid=[user0]/', + 3 => '/action=grade&userid=[user0]/', 5 => '/testassignment3/', 6 => '/testassignment4/', ); @@ -859,8 +859,8 @@ public function provider_get_content_multiple_user() { $matches = array( 1 => '/Go to assign/', 2 => '|mod/assign/view.php|', - 3 => '/action=grader&userid=[user0]/', - 4 => '/action=grader&userid=[user1]/', + 3 => '/action=grade&userid=[user0]/', + 4 => '/action=grade&userid=[user1]/', 5 => '/testassignment3/', 6 => '/testassignment4/' );