Helper function to export Calendar Subscriptions data by a User's contextlist.

@param approved_contextlist $contextlist
@throws \coding_exception
760972
protected static function export_user_calendar_subscription_data(approved_contextlist $contextlist) {
        // Calendar Subscriptions can exist at Site, Course Category, Course, Course Group, or User contexts.
        $subscriptiondetails = self::get_calendar_subscription_details_by_contextlist($contextlist);

        // Multiple Calendar Subscriptions of the same eventtype can exist for a context, so collate them for export.
        $subscriptionrecords = [];
        foreach ($subscriptiondetails as $subscriptiondetail) {
            // Create an array key based on the contextid and eventtype.
            $key = $subscriptiondetail->contextid . $subscriptiondetail->eventtype;

            if (array_key_exists($key, $subscriptionrecords) === false) {
                $subscriptionrecords[$key] = [ $subscriptiondetail ];
            } else {
                $subscriptionrecords[$key] = array_merge($subscriptionrecords[$key], [$subscriptiondetail]);
            }
        }
        $subscriptiondetails->close();

        // Export Calendar Subscription data.
        foreach ($subscriptionrecords as $subscriptionrecord) {
            $index = (count($subscriptionrecord) > 1) ? 1 : 0;

            foreach ($subscriptionrecord as $subscription) {
                // Export the events using the structure Calendar/Subscriptions/{eventtype}-subscription.json.
                $subcontexts = [
                    get_string('calendar', 'calendar'),
                    get_string('subscriptions', 'calendar')
                ];
                $name = $subscription->eventtype . '-subscription';

                // Use name {eventtype}-subscription-{index}.json if multiple eventtypes exists at the same context.
                if ($index != 0) {
                    $name .= '-' . $index;
                    $index++;
                }

                $context = \context::instance_by_id($subscription->contextid);
                writer::with_context($context)->export_related_data($subcontexts, $name, $subscription);
            }
        }
    }
500710
public static function export_user_data(approved_contextlist $contextlist) {
        global $DB;

        if (empty($contextlist->count())) {
            return;
        }

        $user = $contextlist->get_user();

        list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);

        $sql = "SELECT bc.*
                  FROM {backup_controllers} bc
                  JOIN {context} ctx
                    ON ctx.instanceid = bc.itemid AND ctx.contextlevel = :contextlevel
                 WHERE ctx.id {$contextsql}
                   AND bc.userid = :userid
              ORDER BY bc.timecreated ASC";
        $params = ['contextlevel' => CONTEXT_COURSE, 'userid' => $user->id] + $contextparams;
        $backupcontrollers = $DB->get_recordset_sql($sql, $params);
        self::recordset_loop_and_export($backupcontrollers, 'itemid', [], function($carry, $record) {
            $carry[] = [
                'operation' => $record->operation,
                'type' => $record->type,
                'itemid' => $record->itemid,
                'timecreated' => transform::datetime($record->timecreated),
                'timemodified' => transform::datetime($record->timemodified),
            ];
            return $carry;
        }, function($courseid, $data) {
            $context = \context_course::instance($courseid);
            $finaldata = (object) $data;
            writer::with_context($context)->export_data([get_string('backup'), $courseid], $finaldata);
        });
    }
227282
public static function export_user_data(approved_contextlist $contextlist) {
        $rssdata = [];
        $results = static::get_records($contextlist->get_user()->id);
        foreach ($results as $result) {
            $rssdata[] = (object) [
                'title' => $result->title,
                'preferredtitle' => $result->preferredtitle,
                'description' => $result->description,
                'shared' => \core_privacy\local\request\transform::yesno($result->shared),
                'url' => $result->url
            ];
        }
        if (!empty($rssdata)) {
            $data = (object) [
                'feeds' => $rssdata,
            ];
            \core_privacy\local\request\writer::with_context($contextlist->current())->export_data([
                    get_string('pluginname', 'block_rss_client')], $data);
        }
    }
194658
public static function delete_data_for_user(approved_contextlist $contextlist) {
        global $DB;

        $userid = $contextlist->get_user()->id;

        list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
        $select = "contextid $contextsql AND (userid = :userid OR owneruserid = :owneruserid)";
        $params = ['userid' => $userid, 'owneruserid' => $userid] + $contextparams;
        $DB->delete_records_select('search_simpledb_index', $select, $params);
    }
741890
public static function export_user_data(approved_contextlist $contextlist) {
        global $DB;

        if (empty($contextlist)) {
            return;
        }

        $user = $contextlist->get_user();
        $userid = $user->id;

        list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
        $params = $contextparams;

        // Digested forums.
        $sql = "SELECT
                    c.id AS contextid,
                    dig.maildigest AS maildigest
                  FROM {context} c
                  JOIN {course_modules} cm ON cm.id = c.instanceid
                  JOIN {forum} f ON f.id = cm.instance
                  JOIN {forum_digests} dig ON dig.forum = f.id
                 WHERE (
                    dig.userid = :userid AND
                    c.id {$contextsql}
                )
        ";
        $params['userid'] = $userid;
        $digests = $DB->get_records_sql_menu($sql, $params);

        // Forum subscriptions.
        $sql = "SELECT
                    c.id AS contextid,
                    sub.userid AS subscribed
                  FROM {context} c
                  JOIN {course_modules} cm ON cm.id = c.instanceid
                  JOIN {forum} f ON f.id = cm.instance
                  JOIN {forum_subscriptions} sub ON sub.forum = f.id
                 WHERE (
                    sub.userid = :userid AND
                    c.id {$contextsql}
                )
        ";
        $params['userid'] = $userid;
        $subscriptions = $DB->get_records_sql_menu($sql, $params);

        // Tracked forums.
        $sql = "SELECT
                    c.id AS contextid,
                    pref.userid AS tracked
                  FROM {context} c
                  JOIN {course_modules} cm ON cm.id = c.instanceid
                  JOIN {forum} f ON f.id = cm.instance
                  JOIN {forum_track_prefs} pref ON pref.forumid = f.id
                 WHERE (
                    pref.userid = :userid AND
                    c.id {$contextsql}
                )
        ";
        $params['userid'] = $userid;
        $tracked = $DB->get_records_sql_menu($sql, $params);

        $sql = "SELECT
                    c.id AS contextid,
                    f.*,
                    cm.id AS cmid
                  FROM {context} c
                  JOIN {course_modules} cm ON cm.id = c.instanceid
                  JOIN {forum} f ON f.id = cm.instance
                 WHERE (
                    c.id {$contextsql}
                )
        ";

        $params += $contextparams;

        // Keep a mapping of forumid to contextid.
        $mappings = [];

        $forums = $DB->get_recordset_sql($sql, $params);
        foreach ($forums as $forum) {
            $mappings[$forum->id] = $forum->contextid;

            $context = \context::instance_by_id($mappings[$forum->id]);

            // Store the main forum data.
            $data = request_helper::get_context_data($context, $user);
            writer::with_context($context)
                ->export_data([], $data);
            request_helper::export_context_files($context, $user);

            // Store relevant metadata about this forum instance.
            if (isset($digests[$forum->contextid])) {
                static::export_digest_data($userid, $forum, $digests[$forum->contextid]);
            }
            if (isset($subscriptions[$forum->contextid])) {
                static::export_subscription_data($userid, $forum, $subscriptions[$forum->contextid]);
            }
            if (isset($tracked[$forum->contextid])) {
                static::export_tracking_data($userid, $forum, $tracked[$forum->contextid]);
            }
        }
        $forums->close();

        if (!empty($mappings)) {
            // Store all discussion data for this forum.
            static::export_discussion_data($userid, $mappings);

            // Store all post data for this forum.
            static::export_all_posts($userid, $mappings);
        }
    }
1242757
public static function export_user_data(approved_contextlist $contextlist) {
        if (empty($contextlist->count())) {
            return;
        }

        $user = $contextlist->get_user();
        $datarequests = api::get_data_requests($user->id);
        $context = context_user::instance($user->id);
        $contextdatatowrite = [];
        foreach ($datarequests as $request) {
            $record = $request->to_record();
            $data = new stdClass();

            // The user ID that made the request/the request is made for.
            if ($record->requestedby != $record->userid) {
                if ($user->id != $record->requestedby) {
                    // This request is done by this user for another user.
                    $data->userid = fullname($user);
                } else if ($user->id != $record->userid) {
                    // This request was done by another user on behalf of this user.
                    $data->requestedby = fullname($user);
                }
            }

            // Request type.
            $data->type = tool_helper::get_shortened_request_type_string($record->type);
            // Status.
            $data->status = tool_helper::get_request_status_string($record->status);
            // Creation method.
            $data->creationmethod = tool_helper::get_request_creation_method_string($record->creationmethod);
            // Comments.
            $data->comments = $record->comments;
            // The DPO's comment about this request.
            $data->dpocomment = $record->dpocomment;
            // The date and time this request was lodged.
            $data->timecreated = transform::datetime($record->timecreated);
            $contextdatatowrite[] = $data;
        }

        // User context / Privacy and policies / Data requests.
        $subcontext = [
            get_string('privacyandpolicies', 'admin'),
            get_string('datarequests', 'tool_dataprivacy'),
        ];
        writer::with_context($context)->export_data($subcontext, (object)$contextdatatowrite);

        // Write generic module intro files.
        helper::export_context_files($context, $user);
    }
840555
public static function delete_data_for_user(approved_contextlist $contextlist) {
        global $DB;

        $user = $contextlist->get_user();

        foreach ($contextlist as $context) {
            if ($context->contextlevel != CONTEXT_MODULE) {
                continue;
            }
            // Get the assign object.
            $assign = new \assign($context, null, null);
            $assignid = $assign->get_instance()->id;

            $submissions = $DB->get_records('assign_submission', ['assignment' => $assignid, 'userid' => $user->id]);
            foreach ($submissions as $submission) {
                $requestdata = new assign_plugin_request_data($context, $assign, $submission, [], $user);
                manager::plugintype_class_callback('assignsubmission', self::ASSIGNSUBMISSION_INTERFACE,
                        'delete_submission_for_userid', [$requestdata]);
            }

            $grades = $DB->get_records('assign_grades', ['assignment' => $assignid, 'userid' => $user->id]);
            $gradingmanager = get_grading_manager($context, 'mod_assign', 'submissions');
            $controller = $gradingmanager->get_active_controller();
            foreach ($grades as $grade) {
                $requestdata = new assign_plugin_request_data($context, $assign, $grade, [], $user);
                manager::plugintype_class_callback('assignfeedback', self::ASSIGNFEEDBACK_INTERFACE,
                        'delete_feedback_for_grade', [$requestdata]);
                // Delete advanced grading information.
                if (isset($controller)) {
                    \core_grading\privacy\provider::delete_instance_data($context, $grade->id);
                }
            }

            static::delete_overrides_for_users($assign, [$user->id]);
            $DB->delete_records('assign_user_flags', ['assignment' => $assignid, 'userid' => $user->id]);
            $DB->delete_records('assign_user_mapping', ['assignment' => $assignid, 'userid' => $user->id]);
            $DB->delete_records('assign_grades', ['assignment' => $assignid, 'userid' => $user->id]);
            $DB->delete_records('assign_submission', ['assignment' => $assignid, 'userid' => $user->id]);
        }
    }
418589
public static function delete_data_for_user(approved_contextlist $contextlist) {
        foreach ($contextlist as $context) {
            // Let's be super certain that we have the right information for this user here.
            if ($context->contextlevel == CONTEXT_USER && $contextlist->get_user()->id == $context->instanceid) {
                static::delete_user_data($contextlist->get_user()->id, $contextlist->current());
            }
        }
    }
902419
protected static function validate_contextlist_contexts(approved_contextlist $contextlist) {
        return array_reduce($contextlist->get_contexts(), function($carry, $context) {
            if ($context->contextlevel == CONTEXT_COURSE) {
                $carry[] = $context;
            }
            return $carry;
        }, []);
    }
1240349
public static function export_user_data(approved_contextlist $contextlist) {
        global $DB;

        $userid = intval($contextlist->get_user()->id);

        $models = self::get_models_with_user_data();
        $modelids = array_keys($models);

        list ($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);

        $rootpath = [get_string('analytics', 'analytics')];
        $ctxfields = \context_helper::get_preload_record_columns_sql('ctx');

        foreach ($models as $modelid => $model) {

            $analyser = $model->get_analyser(['notimesplitting' => true]);

            // Analytics predictions.
            $joinusersql = $analyser->join_sample_user('ap');
            $sql = "SELECT ap.*, $ctxfields FROM {analytics_predictions} ap
                      JOIN {context} ctx ON ctx.id = ap.contextid
                      {$joinusersql}
                     WHERE u.id = :userid AND ap.modelid = :modelid AND ap.contextid {$contextsql}";
            $params = ['userid' => $userid, 'modelid' => $modelid] + $contextparams;
            $predictions = $DB->get_recordset_sql($sql, $params);

            foreach ($predictions as $prediction) {
                \context_helper::preload_from_record($prediction);
                $context = \context::instance_by_id($prediction->contextid);
                $path = $rootpath;
                $path[] = get_string('privacy:metadata:analytics:predictions', 'analytics');
                $path[] = $prediction->id;

                $data = (object)[
                    'target' => $model->get_target()->get_name()->out(),
                    'context' => $context->get_context_name(true, true),
                    'prediction' => $model->get_target()->get_display_value($prediction->prediction),
                    'timestart' => transform::datetime($prediction->timestart),
                    'timeend' => transform::datetime($prediction->timeend),
                    'timecreated' => transform::datetime($prediction->timecreated),
                ];
                writer::with_context($context)->export_data($path, $data);
            }
            $predictions->close();

            // Indicator calculations.
            $joinusersql = $analyser->join_sample_user('aic');
            $sql = "SELECT aic.*, $ctxfields FROM {analytics_indicator_calc} aic
                      JOIN {context} ctx ON ctx.id = aic.contextid
                      {$joinusersql}
                     WHERE u.id = :userid AND aic.sampleorigin = :analysersamplesorigin AND aic.contextid {$contextsql}";
            $params = ['userid' => $userid, 'analysersamplesorigin' => $analyser->get_samples_origin()] + $contextparams;
            $indicatorcalculations = $DB->get_recordset_sql($sql, $params);
            foreach ($indicatorcalculations as $calculation) {
                \context_helper::preload_from_record($calculation);
                $context = \context::instance_by_id($calculation->contextid);
                $path = $rootpath;
                $path[] = get_string('privacy:metadata:analytics:indicatorcalc', 'analytics');
                $path[] = $calculation->id;

                $indicator = \core_analytics\manager::get_indicator($calculation->indicator);
                $data = (object)[
                    'indicator' => $indicator::get_name()->out(),
                    'context' => $context->get_context_name(true, true),
                    'calculation' => $indicator->get_display_value($calculation->value),
                    'starttime' => transform::datetime($calculation->starttime),
                    'endtime' => transform::datetime($calculation->endtime),
                    'timecreated' => transform::datetime($calculation->timecreated),
                ];
                writer::with_context($context)->export_data($path, $data);
            }
            $indicatorcalculations->close();
        }

        // Analytics predictions.
        // Provided contexts are ignored as we export all user-related stuff.
        list($sql, $params) = self::analytics_prediction_actions_user_sql($userid, $modelids, $contextsql);
        $sql = "SELECT apa.*, ap.modelid, ap.contextid, $ctxfields" . $sql;
        $predictionactions = $DB->get_recordset_sql($sql, $params + $contextparams);
        foreach ($predictionactions as $predictionaction) {

            \context_helper::preload_from_record($predictionaction);
            $context = \context::instance_by_id($predictionaction->contextid);
            $path = $rootpath;
            $path[] = get_string('privacy:metadata:analytics:predictionactions', 'analytics');
            $path[] = $predictionaction->id;

            $data = (object)[
                'target' => $models[$predictionaction->modelid]->get_target()->get_name()->out(),
                'context' => $context->get_context_name(true, true),
                'action' => $predictionaction->actionname,
                'timecreated' => transform::datetime($predictionaction->timecreated),
            ];
            writer::with_context($context)->export_data($path, $data);
        }
        $predictionactions->close();
    }
231499
public static function delete_data_for_user(approved_contextlist $contextlist) {
        // Only delete data from contexts which are at the CONTEXT_COURSE contextlevel.
        $contexts = self::validate_contextlist_contexts($contextlist);
        if (empty($contexts)) {
            return;
        }

        // Get the course ids based on the provided contexts.
        $contextinstanceids = array_map(function($context) {
            return $context->instanceid;
        }, $contextlist->get_contexts());

        global $DB;
        $user = $contextlist->get_user();
        list($insql, $inparams) = $DB->get_in_or_equal($contextinstanceids, SQL_PARAMS_NAMED);
        $params = array_merge(['userid' => $user->id], $inparams);
        $sql = "userid = :userid AND courseid $insql";
        $DB->delete_records_select('enrol_flatfile', $sql, $params);
    }
1095159
public static function export_user_data(approved_contextlist $contextlist) {
        global $DB;
        $context = \context_system::instance();
        if (!$contextlist->count() || !in_array($context->id, $contextlist->get_contextids())) {
            return;
        }

        $user = $contextlist->get_user();
        $sql = "SELECT id, userid, tagcollid, name, rawname, isstandard, description, descriptionformat, flag, timemodified
            FROM {tag} WHERE userid = ?";
        $rs = $DB->get_recordset_sql($sql, [$user->id]);
        foreach ($rs as $record) {
            $subcontext = [get_string('tags', 'tag'), $record->id];
            $tag = (object)[
                'id' => $record->id,
                'userid' => transform::user($record->userid),
                'name' => $record->name,
                'rawname' => $record->rawname,
                'isstandard' => transform::yesno($record->isstandard),
                'description' => writer::with_context($context)->rewrite_pluginfile_urls($subcontext,
                    'tag', 'description', $record->id, strval($record->description)),
                'descriptionformat' => $record->descriptionformat,
                'flag' => $record->flag,
                'timemodified' => transform::datetime($record->timemodified),

            ];
            writer::with_context($context)->export_data($subcontext, $tag);
            writer::with_context($context)->export_area_files($subcontext, 'tag', 'description', $record->id);
        }
        $rs->close();
    }
17551
public static function delete_data_for_user(approved_contextlist $contextlist) {
        if (empty($contextlist->count())) {
            return;
        }

        $userid = $contextlist->get_user()->id;
        foreach ($contextlist->get_contexts() as $context) {
            if (!$context instanceof \context_system && !$context instanceof \context_coursecat) {
                continue;
            }
            static::delete_data($context, $userid);
        }
    }
420309
public static function export_user_data(approved_contextlist $contextlist) {
        global $DB;

        $user = $contextlist->get_user();
        $userid = $user->id;
        $contextids = array_map(function($context) {
            return $context->id;
        }, array_filter($contextlist->get_contexts(), function($context) {
            return $context->contextlevel == CONTEXT_MODULE;
        }));

        if (empty($contextids)) {
            return;
        }

        $flushdata = function($context, $data) use ($user) {
            $contextdata = helper::get_context_data($context, $user);
            helper::export_context_files($context, $user);
            $mergeddata = array_merge((array) $contextdata, (array) $data);

            // Drop the temporary keys.
            if (array_key_exists('submissions', $mergeddata)) {
                $mergeddata['submissions'] = array_values($mergeddata['submissions']);
            }

            writer::with_context($context)->export_data([], (object) $mergeddata);
        };

        $lastctxid = null;
        $data = (object) [];
        list($sql, $params) = static::prepare_export_query($contextids, $userid);
        $recordset = $DB->get_recordset_sql($sql, $params);
        foreach ($recordset as $record) {
            if ($lastctxid && $lastctxid != $record->contextid) {
                $flushdata(context::instance_by_id($lastctxid), $data);
                $data = (object) [];
            }

            context_helper::preload_from_record($record);
            $id = ($record->istmp ? 'tmp' : 'notmp') . $record->submissionid;

            if (!isset($data->submissions)) {
                $data->submissions = [];
            }

            if (!isset($data->submissions[$id])) {
                $data->submissions[$id] = [
                    'inprogress' => transform::yesno($record->istmp),
                    'anonymousresponse' => transform::yesno($record->anonymousresponse == FEEDBACK_ANONYMOUS_YES),
                    'timemodified' => transform::datetime($record->timemodified),
                    'answers' => []
                ];
            }
            $item = static::extract_item_record_from_record($record);
            $value = static::extract_value_record_from_record($record);
            $itemobj = feedback_get_item_class($record->itemtyp);
            $data->submissions[$id]['answers'][] = [
                'question' => format_text($record->itemname, FORMAT_HTML, [
                    'context' => context::instance_by_id($record->contextid),
                    'para' => false,
                    'noclean' => true,
                ]),
                'answer' => $itemobj->get_printval($item, $value)
            ];

            $lastctxid = $record->contextid;
        }

        if (!empty($lastctxid)) {
            $flushdata(context::instance_by_id($lastctxid), $data);
        }

        $recordset->close();
    }
34730
public static function export_user_data(approved_contextlist $contextlist) {
        global $DB;

        // Plugin search_simpledb uses the default document object (core_search\document) which uses FORMAT_PLAIN.
        $textformat = FORMAT_PLAIN;

        $userid = $contextlist->get_user()->id;

        $ctxfields = \context_helper::get_preload_record_columns_sql('ctx');
        list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
        $sql = "SELECT ssi.*, $ctxfields FROM {search_simpledb_index} ssi
                  JOIN {context} ctx ON ctx.id = ssi.contextid
                 WHERE ssi.contextid $contextsql AND (ssi.userid = :userid OR ssi.owneruserid = :owneruserid)";
        $params = ['userid' => $userid, 'owneruserid' => $userid] + $contextparams;

        $records = $DB->get_recordset_sql($sql, $params);
        foreach ($records as $record) {

            \context_helper::preload_from_record($record);
            $context = \context::instance_by_id($record->contextid);
            $document = (object)[
                'title' => format_string($record->title, true, ['context' => $context]),
                'content' => format_text($record->content, $textformat, ['context' => $context]),
                'description1' => format_text($record->description1, $textformat, ['context' => $context]),
                'description2' => format_text($record->description2, $textformat, ['context' => $context]),
                'context' => $context->get_context_name(true, true),
                'modified' => transform::datetime($record->modified),

            ];

            $path = [get_string('search', 'search'), $record->docid];
            writer::with_context($context)->export_data($path, $document);
        }
        $records->close();
    }
597214
protected static function export_assessments(approved_contextlist $contextlist) {
        global $DB;

        list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
        $user = $contextlist->get_user();

        $sql = "SELECT ws.authorid, ws.example, ws.timecreated, ws.timemodified, ws.title, ws.content, ws.contentformat,
                       wa.id, wa.submissionid, wa.reviewerid, wa.weight, wa.timecreated, wa.timemodified, wa.grade,
                       wa.gradinggrade, wa.gradinggradeover, wa.feedbackauthor, wa.feedbackauthorformat, wa.feedbackreviewer,
                       wa.feedbackreviewerformat, cm.id AS cmid, ".\context_helper::get_preload_record_columns_sql('ctx')."
                  FROM {course_modules} cm
                  JOIN {modules} m ON cm.module = m.id AND m.name = :module
                  JOIN {context} ctx ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextlevel
                  JOIN {workshop} w ON cm.instance = w.id
                  JOIN {workshop_submissions} ws ON ws.workshopid = w.id
                  JOIN {workshop_assessments} wa ON wa.submissionid = ws.id AND wa.reviewerid = :reviewerid
                 WHERE ctx.id {$contextsql}";

        $params = $contextparams + [
            'module' => 'workshop',
            'contextlevel' => CONTEXT_MODULE,
            'reviewerid' => $user->id,
        ];

        $rs = $DB->get_recordset_sql($sql, $params);

        foreach ($rs as $record) {
            \context_helper::preload_from_record($record);
            $context = \context_module::instance($record->cmid);
            $writer = \core_privacy\local\request\writer::with_context($context);
            $subcontext = [get_string('myassessments', 'mod_workshop'), $record->id];

            $data = (object) [
                'weight' => $record->weight,
                'timecreated' => transform::datetime($record->timecreated),
                'timemodified' => $record->timemodified ? transform::datetime($record->timemodified) : null,
                'grade' => $record->grade,
                'gradinggrade' => $record->gradinggrade,
                'gradinggradeover' => $record->gradinggradeover,
                'feedbackauthor' => $writer->rewrite_pluginfile_urls($subcontext, 'mod_workshop',
                    'overallfeedback_content', $record->id, $record->feedbackauthor),
                'feedbackauthorformat' => $record->feedbackauthorformat,
                'feedbackreviewer' => $record->feedbackreviewer,
                'feedbackreviewerformat' => $record->feedbackreviewerformat,
            ];

            $submission = (object) [
                'myownsubmission' => transform::yesno($record->authorid == $user->id),
                'example' => transform::yesno($record->example),
                'timecreated' => transform::datetime($record->timecreated),
                'timemodified' => $record->timemodified ? transform::datetime($record->timemodified) : null,
                'title' => $record->title,
                'content' => $writer->rewrite_pluginfile_urls($subcontext, 'mod_workshop',
                    'submission_content', $record->submissionid, $record->content),
                'contentformat' => $record->contentformat,
            ];

            $writer->export_data($subcontext, $data);
            $writer->export_related_data($subcontext, 'submission', $submission);
            $writer->export_area_files($subcontext, 'mod_workshop', 'overallfeedback_content', $record->id);
            $writer->export_area_files($subcontext, 'mod_workshop', 'overallfeedback_attachment', $record->id);
            $writer->export_area_files($subcontext, 'mod_workshop', 'submission_content', $record->submissionid);
            $writer->export_area_files($subcontext, 'mod_workshop', 'submission_attachment', $record->submissionid);

            // Export details of how the assessment forms were filled.
            static::export_assessment_forms($user, $context, $subcontext, $record->id);
        }

        $rs->close();
    }
1013036
public static function export_user_data(approved_contextlist $contextlist) {
        if (empty($contextlist->count())) {
            return;
        }

        $userid = $contextlist->get_user()->id;

        // Remove non-user and invalid contexts. If it ends up empty then early return.
        $contexts = array_filter($contextlist->get_contexts(), function($context) use($userid) {
            return $context->contextlevel == CONTEXT_USER && $context->instanceid == $userid;
        });

        if (empty($contexts)) {
            return;
        }

        // Export the contacts.
        self::export_user_data_contacts($userid);

        // Export the contact requests.
        self::export_user_data_contact_requests($userid);

        // Export the blocked users.
        self::export_user_data_blocked_users($userid);

        // Export the notifications.
        self::export_user_data_notifications($userid);

        // Conversations with empty contextid should be exported here because they are not related to any component/itemid.
        $context = reset($contexts);
        self::export_conversations($userid, '', '', $context);
    }