MDL-66074 mod_forum: Forum grading GDPR provider
authorMathew May <mathewm@hotmail.co.nz>
Mon, 28 Oct 2019 05:23:25 +0000 (13:23 +0800)
committerMathew May <mathewm@hotmail.co.nz>
Wed, 30 Oct 2019 05:03:59 +0000 (13:03 +0800)
mod/forum/classes/privacy/provider.php

index 367c253..06466e6 100644 (file)
@@ -24,6 +24,7 @@
 
 namespace mod_forum\privacy;
 
+use core_grades\component_gradeitem as gradeitem;
 use \core_privacy\local\request\userlist;
 use \core_privacy\local\request\approved_contextlist;
 use \core_privacy\local\request\approved_userlist;
@@ -32,6 +33,7 @@ use \core_privacy\local\request\writer;
 use \core_privacy\local\request\helper as request_helper;
 use \core_privacy\local\metadata\collection;
 use \core_privacy\local\request\transform;
+use tool_dataprivacy\context_instance;
 
 defined('MOODLE_INTERNAL') || die();
 
@@ -131,7 +133,7 @@ class provider implements
             'timemodified' => 'privacy:metadata:forum_queue:timemodified'
         ], 'privacy:metadata:forum_queue');
 
-        //The 'forum_grades' table stores grade data.
+        // The 'forum_grades' table stores grade data.
         $items->add_database_table('forum_grades', [
             'userid' => 'privacy:metadata:forum_grades:userid',
             'forum' => 'privacy:metadata:forum_grades:forum',
@@ -263,7 +265,16 @@ class provider implements
         $params += $ratingsql->params;
         $contextlist->add_from_sql($sql, $params);
 
-        // TODO MDL-66358 forum_grades
+        // Forum grades.
+        $sql = "SELECT c.id
+                  FROM {context} c
+                  JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
+                  JOIN {modules} m ON m.id = cm.module AND m.name = :modname
+                  JOIN {forum} f ON f.id = cm.instance
+                  JOIN {forum_grades} fg ON fg.forum = f.id
+                 WHERE fg.userid = :userid
+        ";
+        $contextlist->add_from_sql($sql, $params);
 
         return $contextlist;
     }
@@ -358,6 +369,15 @@ class provider implements
                   JOIN {forum_track_prefs} pref ON pref.forumid = f.id
                  WHERE cm.id = :instanceid";
         $userlist->add_from_sql('userid', $sql, $params);
+
+        // Forum grades.
+        $sql = "SELECT fg.userid
+                  FROM {course_modules} cm
+                  JOIN {modules} m ON m.id = cm.module AND m.name = :modulename
+                  JOIN {forum} f ON f.id = cm.instance
+                  JOIN {forum_grades} fg ON fg.forum = f.id
+                 WHERE cm.id = :instanceid";
+        $userlist->add_from_sql('userid', $sql, $params);
     }
 
     /**
@@ -520,6 +540,23 @@ class provider implements
         $params['userid'] = $userid;
         $tracked = $DB->get_records_sql_menu($sql, $params);
 
+        // Forum grades.
+        $sql = "SELECT
+                    c.id AS contextid,
+                    fg.grade AS grade,
+                    f.grade_forum AS gradetype
+                  FROM {context} c
+                  JOIN {course_modules} cm ON cm.id = c.instanceid
+                  JOIN {forum} f ON f.id = cm.instance
+                  JOIN {forum_grades} fg ON fg.forum = f.id
+                 WHERE (
+                    fg.userid = :userid AND
+                    c.id {$contextsql}
+                )
+        ";
+        $params['userid'] = $userid;
+        $grades = $DB->get_records_sql_menu($sql, $params);
+
         $sql = "SELECT
                     c.id AS contextid,
                     f.*,
@@ -559,6 +596,9 @@ class provider implements
             if (isset($tracked[$forum->contextid])) {
                 static::export_tracking_data($userid, $forum, $tracked[$forum->contextid]);
             }
+            if (isset($grades[$forum->contextid])) {
+                static::export_grading_data($userid, $forum, $grades[$forum->contextid]);
+            }
         }
         $forums->close();
 
@@ -978,6 +1018,46 @@ class provider implements
         return false;
     }
 
+    protected static function export_grading_data(int $userid, \stdClass $forum, int $grade) {
+        global $USER;
+        if (null !== $grade) {
+            $context = \context_module::instance($forum->cmid);
+            $exportpath = array_merge([],
+                [get_string('privacy:metadata:forum_grades', 'mod_forum')]);
+            $gradingmanager = get_grading_manager($context, 'mod_forum', 'forum');
+            $controller = $gradingmanager->get_active_controller();
+
+            // Check for advanced grading and retrieve that information.
+            if (isset($controller)) {
+                $gradeduser = \core_user::get_user($userid);
+                // Fetch the gradeitem instance.
+                $gradeitem = gradeitem::instance($controller->get_component(), $context, $controller->get_area());
+                $grade = $gradeitem->get_grade_for_user($gradeduser, $USER);
+                $controllercontext = $controller->get_context();
+                \core_grading\privacy\provider::export_item_data($controllercontext, $grade->id, $exportpath);
+            } else {
+                self::export_grade_data($grade, $context, $forum, $exportpath);
+            }
+            // The user has a grade for this forum.
+            writer::with_context(\context_module::instance($forum->cmid))
+                ->export_metadata($exportpath, 'gradingenabled', 1, get_string('privacy:metadata:forum_grades:grade', 'mod_forum'));
+
+            return true;
+        }
+
+        return false;
+    }
+
+    protected static function export_grade_data(int $grade, \context $context, \stdClass $forum, array $path) {
+        $gradedata = (object)[
+            'forum' => $forum->name,
+            'grade' => $grade,
+        ];
+
+        writer::with_context($context)
+            ->export_data($path, $gradedata);
+    }
+
     /**
      * Store read-tracking information about a particular forum post.
      *
@@ -1033,9 +1113,19 @@ class provider implements
 
         $DB->delete_records('forum_track_prefs', ['forumid' => $forumid]);
         $DB->delete_records('forum_subscriptions', ['forum' => $forumid]);
+        $DB->delete_records('forum_grades', ['forum' => $forumid]);
         $DB->delete_records('forum_read', ['forumid' => $forumid]);
         $DB->delete_records('forum_digests', ['forum' => $forumid]);
 
+        // Delete advanced grading information.
+        $gradingmanager = get_grading_manager($context, 'mod_forum', 'forum');
+        $controller = $gradingmanager->get_active_controller();
+        if (isset($controller)) {
+            \core_grading\privacy\provider::delete_instance_data($context);
+        }
+
+        $DB->delete_records('forum_grades', ['forum' => $forumid]);
+
         // Delete all discussion items.
         $DB->delete_records_select(
             'forum_queue',
@@ -1115,6 +1205,22 @@ class provider implements
                 'userid' => $userid,
             ]);
 
+            // Handle any advanced grading method data first.
+            $grades = $DB->get_records('forum_grades', ['forum' => $forum->id, 'userid' => $user->id]);
+            $gradingmanager = get_grading_manager($context, 'forum_grades', 'forum');
+            $controller = $gradingmanager->get_active_controller();
+            foreach ($grades as $grade) {
+                // Delete advanced grading information.
+                if (isset($controller)) {
+                    \core_grading\privacy\provider::delete_instance_data($context, $grade->id);
+                }
+            }
+            // Advanced grading methods have been cleared, lets clear our module now.
+            $DB->delete_records('forum_grades', [
+                'forum' => $forum->id,
+                'userid' => $userid,
+            ]);
+
             // Do not delete discussion or forum posts.
             // Instead update them to reflect that the content has been deleted.
             $postsql = "userid = :userid AND discussion IN (SELECT id FROM {forum_discussions} WHERE forum = :forum)";
@@ -1203,5 +1309,19 @@ class provider implements
         $fs = get_file_storage();
         $fs->delete_area_files_select($context->id, 'mod_forum', 'post', "IN ($postidsql)", $params);
         $fs->delete_area_files_select($context->id, 'mod_forum', 'attachment', "IN ($postidsql)", $params);
+
+        list($sql, $params) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
+        $params['forum'] = $forum->id;
+        // Delete advanced grading information.
+        $grades = $DB->get_records_select('forum_grades', "forum = :forum AND userid $sql", $params);
+        $gradeids = array_keys($grades);
+        $gradingmanager = get_grading_manager($context, 'mod_forum', 'forum');
+        $controller = $gradingmanager->get_active_controller();
+        if (isset($controller)) {
+            // Careful here, if no gradeids are provided then all data is deleted for the context.
+            if (!empty($gradeids)) {
+                \core_grading\privacy\provider::delete_data_for_instances($context, $gradeids);
+            }
+        }
     }
 }