Export personal data stored in the given contexts.

@param approved_contextlist $contextlist List of contexts approved for export.
196171
public static function export_user_data(approved_contextlist $contextlist) {
        global $DB;

        if (!count($contextlist)) {
            return;
        }

        $user = $contextlist->get_user();

        // Export general information about all workshops.
        foreach ($contextlist->get_contexts() as $context) {
            if ($context->contextlevel != CONTEXT_MODULE) {
                continue;
            }
            $data = helper::get_context_data($context, $user);
            static::append_extra_workshop_data($context, $user, $data, []);
            writer::with_context($context)->export_data([], $data);
            helper::export_context_files($context, $user);
        }

        // Export the user's own submission and all example submissions he/she created.
        static::export_submissions($contextlist);

        // Export all given assessments.
        static::export_assessments($contextlist);
    }
252089
public static function export_user_data(approved_contextlist $contextlist) {
        global $DB;

        $sysctx = context_system::instance();
        $fs = get_file_storage();
        $userid = $contextlist->get_user()->id;
        $ctxfields = context_helper::get_preload_record_columns_sql('ctx');
        $rootpath = [get_string('blog', 'core_blog')];
        $associations = [];

        foreach ($contextlist as $context) {
            switch ($context->contextlevel) {
                case CONTEXT_USER:
                    $contextuserid = $context->instanceid;
                    $insql = ' > 0';
                    $inparams = [];

                    if ($contextuserid != $userid) {
                        // We will only be exporting comments, so fetch the IDs of the relevant entries.
                        $entryids = $DB->get_fieldset_sql("
                            SELECT DISTINCT c.itemid
                              FROM {comments} c
                             WHERE c.contextid = :contextid
                               AND c.userid = :userid
                               AND c.component = :component
                               AND c.commentarea = :commentarea", [
                            'contextid' => $context->id,
                            'userid' => $userid,
                            'component' => 'blog',
                            'commentarea' => 'format_blog'
                        ]);

                        if (empty($entryids)) {
                            // This should not happen, as the user context should not have been reported then.
                            continue 2;
                        }

                        list($insql, $inparams) = $DB->get_in_or_equal($entryids, SQL_PARAMS_NAMED);
                    }

                    // Loop over each blog entry in context.
                    $sql = "userid = :userid AND module IN (:blog, :blogext) AND id $insql";
                    $params = array_merge($inparams, ['userid' => $contextuserid, 'blog' => 'blog', 'blogext' => 'blog_external']);
                    $recordset = $DB->get_recordset_select('post', $sql, $params, 'id');
                    foreach ($recordset as $record) {

                        $subject = format_string($record->subject);
                        $path = array_merge($rootpath, [get_string('blogentries', 'core_blog'), $subject . " ({$record->id})"]);

                        // If the context is not mine, then we ONLY export the comments made by the exporting user.
                        if ($contextuserid != $userid) {
                            \core_comment\privacy\provider::export_comments($context, 'blog', 'format_blog',
                                $record->id, $path, true);
                            continue;
                        }

                        // Manually export the files as they reside in the system context so we can't use
                        // the write's helper methods. The same happens for attachments.
                        foreach ($fs->get_area_files($sysctx->id, 'blog', 'post', $record->id) as $f) {
                            writer::with_context($context)->export_file($path, $f);
                        }
                        foreach ($fs->get_area_files($sysctx->id, 'blog', 'attachment', $record->id) as $f) {
                            writer::with_context($context)->export_file($path, $f);
                        }

                        // Rewrite the summary files.
                        $summary = writer::with_context($context)->rewrite_pluginfile_urls($path, 'blog', 'post',
                            $record->id, $record->summary);

                        // Fetch associations.
                        $assocs = [];
                        $sql = "SELECT ba.contextid, $ctxfields
                                  FROM {blog_association} ba
                                  JOIN {context} ctx
                                    ON ba.contextid = ctx.id
                                 WHERE ba.blogid = :blogid";
                        $assocset = $DB->get_recordset_sql($sql, ['blogid' => $record->id]);
                        foreach ($assocset as $assocrec) {
                            context_helper::preload_from_record($assocrec);
                            $assocctx = context::instance_by_id($assocrec->contextid);
                            $assocs[] = $assocctx->get_context_name();
                        }
                        $assocset->close();

                        // Export associated tags.
                        \core_tag\privacy\provider::export_item_tags($userid, $context, $path, 'core', 'post', $record->id);

                        // Export all comments made on my post.
                        \core_comment\privacy\provider::export_comments($context, 'blog', 'format_blog',
                            $record->id, $path, false);

                        // Add blog entry data.
                        $entry = (object) [
                            'subject' => $subject,
                            'summary' => format_text($summary, $record->summaryformat),
                            'uniquehash' => $record->uniquehash,
                            'publishstate' => static::transform_publishstate($record->publishstate),
                            'created' => transform::datetime($record->created),
                            'lastmodified' => transform::datetime($record->lastmodified),
                            'associations' => $assocs
                        ];

                        writer::with_context($context)->export_data($path, $entry);
                    }
                    $recordset->close();

                    // Export external blogs.
                    $recordset = $DB->get_recordset('blog_external', ['userid' => $userid]);
                    foreach ($recordset as $record) {

                        $path = array_merge($rootpath, [get_string('externalblogs', 'core_blog'),
                            $record->name . " ({$record->id})"]);

                        // Export associated tags.
                        \core_tag\privacy\provider::export_item_tags($userid, $context, $path, 'core',
                            'blog_external', $record->id);

                        // Add data.
                        $external = (object) [
                            'name' => $record->name,
                            'description' => $record->description,
                            'url' => $record->url,
                            'filtertags' => $record->filtertags,
                            'modified' => transform::datetime($record->timemodified),
                            'lastfetched' => transform::datetime($record->timefetched),
                        ];

                        writer::with_context($context)->export_data($path, $external);
                    }
                    $recordset->close();
                    break;

                case CONTEXT_COURSE:
                case CONTEXT_MODULE:
                    $associations[] = $context->id;
                    break;
            }
        }

        // Export associations.
        if (!empty($associations)) {
            list($insql, $inparams) = $DB->get_in_or_equal($associations, SQL_PARAMS_NAMED);
            $sql = "
                SELECT ba.contextid, p.subject, $ctxfields
                  FROM {post} p
                  JOIN {blog_association} ba
                    ON ba.blogid = p.id
                  JOIN {context} ctx
                    ON ctx.id = ba.contextid
                 WHERE ba.contextid $insql
                   AND p.userid = :userid
              ORDER BY ba.contextid ASC";
            $params = array_merge($inparams, ['userid' => $userid]);

            $path = [get_string('privacy:path:blogassociations', 'core_blog')];

            $flushassocs = function($context, $assocs) use ($path) {
                writer::with_context($context)->export_data($path, (object) [
                    'associations' => $assocs
                ]);
            };

            $lastcontextid = null;
            $assocs = [];
            $recordset = $DB->get_recordset_sql($sql, $params);
            foreach ($recordset as $record) {
                context_helper::preload_from_record($record);

                if ($lastcontextid && $record->contextid != $lastcontextid) {
                    $flushassocs(context::instance_by_id($lastcontextid), $assocs);
                    $assocs = [];
                }
                $assocs[] = format_string($record->subject);
                $lastcontextid = $record->contextid;
            }

            if ($lastcontextid) {
                $flushassocs(context::instance_by_id($lastcontextid), $assocs);
            }

            $recordset->close();
        }
    }
328027
public static function delete_data_for_user(approved_contextlist $contextlist) {
        if (empty($contextlist)) {
            return;
        }

        // Delete all Calendar Events for the owner and specified contexts in batches.
        $eventdetails = self::get_calendar_event_details_by_contextlist($contextlist);
        $eventids = [];
        foreach ($eventdetails as $eventdetail) {
            $eventids[] = $eventdetail->eventid;
        }
        $eventdetails->close();
        self::delete_batch_records('event', 'id', $eventids);

        // Delete all Calendar Subscriptions for the owner and specified contexts in batches.
        $subscriptiondetails = self::get_calendar_subscription_details_by_contextlist($contextlist);
        $subscriptionids = [];
        foreach ($subscriptiondetails as $subscriptiondetail) {
            $subscriptionids[] = $subscriptiondetail->subscriptionid;
        }
        $subscriptiondetails->close();
        self::delete_batch_records('event_subscriptions', 'id', $subscriptionids);
    }
139260
public static function export_user_data(approved_contextlist $contextlist) {
        global $DB;

        $user = $contextlist->get_user();

        // Firstly export all autosave records from all contexts in the list owned by the given user.

        list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
        $contextparams['userid'] = $user->id;

        $sql = "SELECT *
                  FROM {editor_atto_autosave}
                 WHERE userid = :userid AND contextid {$contextsql}";

        $autosaves = $DB->get_recordset_sql($sql, $contextparams);
        self::export_autosaves($user, $autosaves);

        // Additionally export all eventual records in the given user's context regardless the actual owner.
        // We still consider them to be the user's personal data even when edited by someone else.

        list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
        $contextparams['userid'] = $user->id;
        $contextparams['contextuser'] = CONTEXT_USER;

        $sql = "SELECT eas.*
                  FROM {editor_atto_autosave} eas
                  JOIN {context} c ON c.id = eas.contextid
                 WHERE c.id {$contextsql} AND c.contextlevel = :contextuser AND c.instanceid = :userid";

        $autosaves = $DB->get_recordset_sql($sql, $contextparams);
        self::export_autosaves($user, $autosaves);
    }
123464
public static function delete_data_for_user(approved_contextlist $contextlist) {
        global $DB;

        // If the user has repository_instances data, then only the User context should be present so get the first context.
        $contexts = $contextlist->get_contexts();
        if (count($contexts) == 0) {
            return;
        }
        $context = reset($contexts);

        // Sanity check that context is at the User context level, then get the userid.
        if ($context->contextlevel !== CONTEXT_USER) {
            return;
        }
        $userid = $context->instanceid;

        // Delete the repository_instances records created for the userid.
        $DB->delete_records('repository_instances', ['userid' => $userid]);
    }
606047
public static function export_user_data(approved_contextlist $contextlist) {
        global $DB;

        $user = $contextlist->get_user();
        $userid = $user->id;
        $cmids = array_reduce($contextlist->get_contexts(), function($carry, $context) {
            if ($context->contextlevel == CONTEXT_MODULE) {
                $carry[] = $context->instanceid;
            }
            return $carry;
        }, []);
        if (empty($cmids)) {
            return;
        }

        $chatidstocmids = static::get_chat_ids_to_cmids_from_cmids($cmids);
        $chatids = array_keys($chatidstocmids);

        // Export the messages.
        list($insql, $inparams) = $DB->get_in_or_equal($chatids, SQL_PARAMS_NAMED);
        $params = array_merge($inparams, ['userid' => $userid]);
        $recordset = $DB->get_recordset_select('chat_messages', "chatid $insql AND userid = :userid", $params, 'timestamp, id');
        static::recordset_loop_and_export($recordset, 'chatid', [], function($carry, $record) use ($user, $chatidstocmids) {
            $message = $record->message;
            if ($record->issystem) {
                $message = get_string('message' . $record->message, 'mod_chat', fullname($user));
            }
            $carry[] = [
                'message' => $message,
                'sent_at' => transform::datetime($record->timestamp),
                'is_system_generated' => transform::yesno($record->issystem),
            ];
            return $carry;

        }, function($chatid, $data) use ($user, $chatidstocmids) {
            $context = context_module::instance($chatidstocmids[$chatid]);
            $contextdata = helper::get_context_data($context, $user);
            $finaldata = (object) array_merge((array) $contextdata, ['messages' => $data]);
            helper::export_context_files($context, $user);
            writer::with_context($context)->export_data([], $finaldata);
        });
    }
325994
public static function export_user_data(approved_contextlist $contextlist) {
        global $DB;

        $user = $contextlist->get_user();

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

        $sql = "SELECT
                    c.id AS contextid,
                    bi.*
                  FROM {context} c
            INNER JOIN {block_instances} bi ON bi.id = c.instanceid AND c.contextlevel = :contextlevel
                 WHERE bi.blockname = 'html'
                   AND(
                    c.id {$contextsql}
                )
        ";

        $params = [
            'contextlevel' => CONTEXT_BLOCK,
        ];
        $params += $contextparams;

        $instances = $DB->get_recordset_sql($sql, $params);
        foreach ($instances as $instance) {
            $context = \context_block::instance($instance->id);
            $block = block_instance('html', $instance);
            if (empty($block->config)) {
                // Skip this block. It has not been configured.
                continue;
            }

            $html = writer::with_context($context)
                ->rewrite_pluginfile_urls([], 'block_html', 'content', null, $block->config->text);

            // Default to FORMAT_HTML which is what will have been used before the
            // editor was properly implemented for the block.
            $format = isset($block->config->format) ? $block->config->format : FORMAT_HTML;

            $filteropt = (object) [
                'overflowdiv' => true,
                'noclean' => true,
            ];
            $html = format_text($html, $format, $filteropt);

            $data = helper::get_context_data($context, $user);
            helper::export_context_files($context, $user);
            $data->title = $block->config->title;
            $data->content = $html;

            writer::with_context($context)->export_data([], $data);
        }
        $instances->close();
    }
1128408
public static function export_user_data(approved_contextlist $contextlist) {
        $context = $contextlist->current();
        $user = \core_user::get_user($contextlist->get_user()->id);
        static::export_user($user, $context);
        static::export_password_history($user->id, $context);
        static::export_password_resets($user->id, $context);
        static::export_lastaccess($user->id, $context);
        static::export_course_requests($user->id, $context);
        static::export_user_devices($user->id, $context);
        static::export_user_session_data($user->id, $context);
    }
344147
public static function delete_data_for_user(approved_contextlist $contextlist) {
        global $DB;
        list($sql, $params) = static::get_sql_where_from_contexts($contextlist->get_contexts());
        if (empty($sql)) {
            return;
        }
        $userid = $contextlist->get_user()->id;
        $DB->delete_records_select('log', "$sql AND userid = :userid", array_merge($params, ['userid' => $userid]));
    }
544950
public static function export_user_data(approved_contextlist $contextlist) {
        global $DB;

        foreach ($contextlist as $context) {
            if ($context->contextlevel != CONTEXT_MODULE) {
                continue;
            }
            $user = $contextlist->get_user();

            $rs = $DB->get_recordset_sql('SELECT w.wikimode, s.id AS subwikiid,
                    s.groupid AS subwikigroupid, s.userid AS subwikiuserid,
                    p.id AS pageid, p.userid AS pageuserid, p.title, p.cachedcontent, p.timecreated AS pagetimecreated,
                    p.timemodified AS pagetimemodified, p.timerendered AS pagetimerendered, p.pageviews, p.readonly,
                    v.id AS versionid, v.content, v.contentformat, v.version, v.timecreated AS versiontimecreated,
                    l.id AS lockid, l.sectionname, l.lockedat
                FROM {course_modules} cm
                JOIN {wiki} w ON w.id = cm.instance
                JOIN {wiki_subwikis} s ON cm.instance = s.wikiid
                LEFT JOIN {wiki_pages} p ON p.subwikiid = s.id
                LEFT JOIN {wiki_versions} v ON v.pageid = p.id AND v.userid = :user4
                LEFT JOIN {wiki_locks} l ON l.pageid = p.id AND l.userid = :user5
                WHERE cm.id = :cmid AND (s.userid = :user1 OR p.userid = :user2 OR v.userid = :user3 OR l.userid = :user6 OR
                     EXISTS (SELECT 1 FROM {comments} com WHERE com.itemid = p.id AND com.commentarea = :commentarea
                          AND com.contextid = :ctxid AND com.userid = :user7)
                )
                ORDER BY s.id, p.id, v.id',
                ['cmid' => $context->instanceid,
                    'user1' => $user->id, 'user2' => $user->id, 'user3' => $user->id, 'user4' => $user->id,
                    'user5' => $user->id, 'user6' => $user->id, 'user7' => $user->id, 'commentarea' => 'wiki_page',
                    'ctxid' => $context->id]);

            if (!$rs->current()) {
                $rs->close();
                continue;
            }

            $subwiki = [];
            $wikimode = null;
            foreach ($rs as $record) {
                if ($wikimode === null) {
                    $wikimode = $record->wikimode;
                }
                if (!isset($subwiki[$record->subwikiid])) {
                    self::export_subwiki($user, $context, $subwiki, $wikimode);
                    $subwiki = [$record->subwikiid => [
                        'groupid' => $record->subwikigroupid,
                        'userid' => $record->subwikiuserid ? transform::user($record->subwikiuserid) : 0,
                        'pages' => []
                    ]];
                }

                if (!$record->pageid) {
                    // This is an empty individual wiki.
                    continue;
                }

                // Prepend page title with the page id to guarantee uniqueness.
                $pagetitle = format_string($record->title, true, ['context' => $context]);
                $page = $record->pageid . ' ' . $pagetitle;
                if (!isset($subwiki[$record->subwikiid]['pages'][$page])) {
                    // Export basic details about the page.
                    $subwiki[$record->subwikiid]['pages'][$page] = ['page' => [
                        'id' => $record->pageid,
                        'title' => $pagetitle,
                        'cachedcontent' => $record->cachedcontent,
                    ]];
                    if ($record->pageuserid == $user->id) {
                        // This page belongs to this user. Export all details.
                        $subwiki[$record->subwikiid]['pages'][$page]['page'] += [
                            'userid' => transform::user($user->id),
                            'timecreated' => transform::datetime($record->pagetimecreated),
                            'timemodified' => transform::datetime($record->pagetimemodified),
                            'timerendered' => transform::datetime($record->pagetimerendered),
                            'pageviews' => $record->pageviews,
                            'readonly' => $record->readonly,
                        ];

                        $subwiki[$record->subwikiid]['pages'][$page]['page']['userid'] = transform::user($user->id);
                    }
                }

                if ($record->versionid) {
                    $subwiki[$record->subwikiid]['pages'][$page]['revisions'][$record->versionid] = [
                        'content' => $record->content,
                        'contentformat' => $record->contentformat,
                        'version' => $record->version,
                        'timecreated' => transform::datetime($record->versiontimecreated)
                    ];
                }

                if ($record->lockid) {
                    $subwiki[$record->subwikiid]['pages'][$page]['locks'][$record->lockid] = [
                        'sectionname' => $record->sectionname,
                        'lockedat' => transform::datetime($record->lockedat),
                    ];
                }

            }
            self::export_subwiki($user, $context, $subwiki, $wikimode);

            if ($subwiki) {
                // Export wiki itself.
                $contextdata = helper::get_context_data($context, $user);
                helper::export_context_files($context, $user);
                writer::with_context($context)->export_data([], $contextdata);
            }

            $rs->close();
        }
    }
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);
    }
832653
public static function delete_data_for_user(approved_contextlist $contextlist) {
        global $DB;

        $userid = $contextlist->get_user()->id;
        $cmids = array_reduce($contextlist->get_contexts(), function($carry, $context) {
            if ($context->contextlevel == CONTEXT_MODULE) {
                $carry[] = $context->instanceid;
            }
            return $carry;
        }, []);
        if (empty($cmids)) {
            return;
        }

        $chatidstocmids = static::get_chat_ids_to_cmids_from_cmids($cmids);
        $chatids = array_keys($chatidstocmids);

        list($insql, $inparams) = $DB->get_in_or_equal($chatids, SQL_PARAMS_NAMED);
        $sql = "chatid $insql AND userid = :userid";
        $params = array_merge($inparams, ['userid' => $userid]);

        $DB->delete_records_select('chat_messages', $sql, $params);
        $DB->delete_records_select('chat_messages_current', $sql, $params);
        $DB->delete_records_select('chat_users', $sql, $params);
    }
1117657
protected static function export_user_data_lti_types(approved_contextlist $contextlist) {
        global $DB;

        // Filter out any contexts that are not related to courses.
        $courseids = array_reduce($contextlist->get_contexts(), function($carry, $context) {
            if ($context->contextlevel == CONTEXT_COURSE) {
                $carry[] = $context->instanceid;
            }
            return $carry;
        }, []);

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

        $user = $contextlist->get_user();

        list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
        $params = array_merge($inparams, ['userid' => $user->id]);
        $ltitypes = $DB->get_recordset_select('lti_types', "course $insql AND createdby = :userid", $params, 'timecreated ASC');
        self::recordset_loop_and_export($ltitypes, 'course', [], function($carry, $record) {
            $context = \context_course::instance($record->course);
            $options = ['context' => $context];
            $carry[] = [
                'name' => format_string($record->name, true, $options),
                'createdby' => transform::user($record->createdby),
                'timecreated' => transform::datetime($record->timecreated),
                'timemodified' => transform::datetime($record->timemodified)
            ];
            return $carry;
        }, function($courseid, $data) {
            $context = \context_course::instance($courseid);
            $finaldata = (object) ['lti_types' => $data];
            writer::with_context($context)->export_data([], $finaldata);
        });
    }
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);
        }
    }
1186268
public static function export_user_data(approved_contextlist $contextlist) {
        global $DB;

        if ($contextlist->get_component() != 'core_portfolio') {
            return;
        }

        $correctusercontext = array_filter($contextlist->get_contexts(), function($context) use ($contextlist) {
            if ($context->contextlevel == CONTEXT_USER && $context->instanceid == $contextlist->get_user()->id) {
                return $context;
            }
        });

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

        $usercontext = array_shift($correctusercontext);

        $sql = "SELECT pi.id AS instanceid, pi.name,
                       piu.id AS preferenceid, piu.name AS preference, piu.value,
                       pl.id AS logid, pl.time AS logtime, pl.caller_class, pl.caller_file,
                       pl.caller_component, pl.returnurl, pl.continueurl
                  FROM {portfolio_instance} pi
             LEFT JOIN {portfolio_instance_user} piu ON piu.instance = pi.id AND piu.userid = :userid1
             LEFT JOIN {portfolio_log} pl ON pl.portfolio = pi.id AND pl.userid = :userid2
                 WHERE piu.id IS NOT NULL OR pl.id IS NOT NULL";
        $params = ['userid1' => $usercontext->instanceid, 'userid2' => $usercontext->instanceid];
        $instances = [];
        $rs = $DB->get_recordset_sql($sql, $params);
        foreach ($rs as $record) {
            $instances += [$record->name =>
                (object)[
                    'name' => $record->name,
                    'preferences' => [],
                    'logs' => [],
                ]
            ];
            if ($record->preferenceid) {
                $instances[$record->name]->preferences[$record->preferenceid] = (object)[
                    'name' => $record->preference,
                    'value' => $record->value,
                ];
            }
            if ($record->logid) {
                $instances[$record->name]->logs[$record->logid] = (object)[
                    'time' => transform::datetime($record->logtime),
                    'caller_class' => $record->caller_class,
                    'caller_file' => $record->caller_file,
                    'caller_component' => $record->caller_component,
                    'returnurl' => $record->returnurl,
                    'continueurl' => $record->continueurl
                ];
            }
        }
        $rs->close();

        if (!empty($instances)) {
            foreach ($instances as &$instance) {
                if (!empty($instance->preferences)) {
                    $instance->preferences = array_values($instance->preferences);
                } else {
                    unset($instance->preferences);
                }
                if (!empty($instance->logs)) {
                    $instance->logs = array_values($instance->logs);
                } else {
                    unset($instance->logs);
                }
            }
            \core_privacy\local\request\writer::with_context($contextlist->current())->export_data(
                    [get_string('privacy:path', 'portfolio')], (object) $instances);
        }
    }
592771
public static function delete_data_for_user(approved_contextlist $contextlist) {
        global $DB;

        if ($contextlist->get_component() != 'core_portfolio') {
            return;
        }

        $correctusercontext = array_filter($contextlist->get_contexts(), function($context) use ($contextlist) {
            if ($context->contextlevel == CONTEXT_USER && $context->instanceid == $contextlist->get_user()->id) {
                return $context;
            }
        });

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

        $usercontext = array_shift($correctusercontext);

        $DB->delete_records('portfolio_instance_user', ['userid' => $usercontext->instanceid]);
        $DB->delete_records('portfolio_tempdata', ['userid' => $usercontext->instanceid]);
        $DB->delete_records('portfolio_log', ['userid' => $usercontext->instanceid]);
    }
640107
public static function delete_data_for_user(approved_contextlist $contextlist) {
        if ($contextlist->get_component() != 'report_stats') {
            return;
        }
        foreach ($contextlist->get_contexts() as $context) {
            if ($context->contextlevel == CONTEXT_COURSE) {
                static::delete_stats($context->instanceid, $contextlist->get_user()->id);
            }
        }
    }