Autoría | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>.namespace core_competency\privacy;use context;use context_course;use context_helper;use context_module;use context_system;use context_user;use moodle_recordset;use core_competency\api;use core_competency\competency;use core_competency\competency_framework;use core_competency\course_competency;use core_competency\course_competency_settings;use core_competency\course_module_competency;use core_competency\evidence;use core_competency\plan;use core_competency\plan_competency;use core_competency\related_competency;use core_competency\template;use core_competency\template_cohort;use core_competency\template_competency;use core_competency\user_competency;use core_competency\user_competency_course;use core_competency\user_competency_plan;use core_competency\user_evidence;use core_competency\user_evidence_competency;use core_competency\external\performance_helper;use core_privacy\local\metadata\collection;use core_privacy\local\request\approved_userlist;use core_privacy\local\request\contextlist;use core_privacy\local\request\approved_contextlist;use core_privacy\local\request\transform;use core_privacy\local\request\userlist;use core_privacy\local\request\writer;/*** Data provider class.** @package core_competency* @copyright 2018 Frédéric Massart* @author Frédéric Massart <fred@branchup.tech>* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class provider implements\core_privacy\local\metadata\provider,\core_privacy\local\request\core_userlist_provider,\core_privacy\local\request\subsystem\provider {/*** Returns metadata.** @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 {// Tables not related to users aside from the editing information.$collection->add_database_table('competency', ['timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency');$collection->add_database_table('competency_coursecompsetting', ['timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_coursecompsetting');$collection->add_database_table('competency_framework', ['timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_framework');$collection->add_database_table('competency_coursecomp', ['timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_coursecomp');$collection->add_database_table('competency_template', ['timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_template');$collection->add_database_table('competency_templatecomp', ['timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_templatecomp');$collection->add_database_table('competency_templatecohort', ['timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_templatecohort');$collection->add_database_table('competency_relatedcomp', ['timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_relatedcomp');$collection->add_database_table('competency_modulecomp', ['timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_modulecomp');// Tables containing user data.$collection->add_database_table('competency_plan', ['name' => 'privacy:metadata:plan:name','description' => 'privacy:metadata:plan:description','userid' => 'privacy:metadata:plan:userid','status' => 'privacy:metadata:plan:status','duedate' => 'privacy:metadata:plan:duedate','reviewerid' => 'privacy:metadata:plan:reviewerid','timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_plan');$collection->add_database_table('competency_usercomp', ['userid' => 'privacy:metadata:usercomp:userid','status' => 'privacy:metadata:usercomp:status','reviewerid' => 'privacy:metadata:usercomp:reviewerid','proficiency' => 'privacy:metadata:usercomp:proficiency','grade' => 'privacy:metadata:usercomp:grade','timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_usercomp');$collection->add_database_table('competency_usercompcourse', ['userid' => 'privacy:metadata:usercomp:userid','proficiency' => 'privacy:metadata:usercomp:proficiency','grade' => 'privacy:metadata:usercomp:grade','timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_usercompcourse');$collection->add_database_table('competency_usercompplan', ['userid' => 'privacy:metadata:usercomp:userid','proficiency' => 'privacy:metadata:usercomp:proficiency','grade' => 'privacy:metadata:usercomp:grade','timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_usercompplan');$collection->add_database_table('competency_plancomp', ['timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_plancomp');$collection->add_database_table('competency_evidence', ['action' => 'privacy:metadata:evidence:action','actionuserid' => 'privacy:metadata:evidence:actionuserid','descidentifier' => 'privacy:metadata:evidence:descidentifier','desccomponent' => 'privacy:metadata:evidence:desccomponent','desca' => 'privacy:metadata:evidence:desca','url' => 'privacy:metadata:evidence:url','grade' => 'privacy:metadata:evidence:grade','note' => 'privacy:metadata:evidence:note','timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_evidence');$collection->add_database_table('competency_userevidence', ['name' => 'privacy:metadata:userevidence:name','description' => 'privacy:metadata:userevidence:description','url' => 'privacy:metadata:userevidence:url','timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_userevidence');$collection->add_database_table('competency_userevidencecomp', ['timecreated' => 'privacy:metadata:timecreated','timemodified' => 'privacy:metadata:timemodified','usermodified' => 'privacy:metadata:usermodified',], 'privacy:metadata:competency_userevidencecomp');// Comments can be left on learning plans and competencies.$collection->link_subsystem('core_comment', 'privacy:metadata:core_comments');return $collection;}/*** 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 {global $DB;$contextlist = new \core_privacy\local\request\contextlist();// Find the contexts of the frameworks, and related data, modified by the user.$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . competency_framework::TABLE . "} cf ON cf.contextid = ctx.idWHERE cf.usermodified = :userid1";$params = ['userid1' => $userid];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . competency_framework::TABLE . "} cf ON cf.contextid = ctx.idJOIN {" . competency::TABLE . "} c ON c.competencyframeworkid = cf.idWHERE c.usermodified = :userid2";$params = ['userid2' => $userid];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . competency_framework::TABLE . "} cf ON cf.contextid = ctx.idJOIN {" . competency::TABLE . "} c ON c.competencyframeworkid = cf.idJOIN {" . related_competency::TABLE . "} cr ON cr.competencyid = c.idWHERE cr.usermodified = :userid3";$params = ['userid3' => $userid];$contextlist->add_from_sql($sql, $params);// Find the contexts of the templates, and related data, modified by the user.$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . template::TABLE . "} tplON tpl.contextid = ctx.idWHERE tpl.usermodified = :userid1";$params = ['userid1' => $userid];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . template::TABLE . "} tplON tpl.contextid = ctx.idJOIN {" . template_cohort::TABLE . "} tchON tch.templateid = tpl.idAND tch.usermodified = :userid2";$params = ['userid2' => $userid];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . template::TABLE . "} tplON tpl.contextid = ctx.idJOIN {" . template_competency::TABLE . "} tcON tc.templateid = tpl.idAND tc.usermodified = :userid3";$params = ['userid3' => $userid];$contextlist->add_from_sql($sql, $params);// Find the possible course contexts.$sql = "SELECT DISTINCT ctx.idFROM {" . course_competency::TABLE . "} ccJOIN {context} ctxON ctx.instanceid = cc.courseidAND ctx.contextlevel = :courselevelWHERE cc.usermodified = :userid";$params = ['courselevel' => CONTEXT_COURSE, 'userid' => $userid];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {" . course_competency_settings::TABLE . "} ccsJOIN {context} ctxON ctx.instanceid = ccs.courseidAND ctx.contextlevel = :courselevelWHERE ccs.usermodified = :userid";$params = ['courselevel' => CONTEXT_COURSE, 'userid' => $userid];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {" . user_competency_course::TABLE . "} uccJOIN {context} ctxON ctx.instanceid = ucc.courseidAND ctx.contextlevel = :courselevelWHERE ucc.usermodified = :userid";$params = ['courselevel' => CONTEXT_COURSE, 'userid' => $userid];$contextlist->add_from_sql($sql, $params);// Find the possible module contexts.$sql = "SELECT DISTINCT ctx.idFROM {" . course_module_competency::TABLE . "} cmcJOIN {context} ctxON ctx.instanceid = cmc.cmidAND ctx.contextlevel = :modulelevelWHERE cmc.usermodified = :userid";$params = ['modulelevel' => CONTEXT_MODULE, 'userid' => $userid];$contextlist->add_from_sql($sql, $params);// Add user contexts through usermodified/reviewing of plan related data.$sql = "SELECT DISTINCT ctx.idFROM {" . plan::TABLE . "} pJOIN {context} ctxON ctx.instanceid = p.useridAND ctx.contextlevel = :userlevelWHERE p.usermodified = :userid1";$params = ['userlevel' => CONTEXT_USER,'userid1' => $userid,];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {" . plan::TABLE . "} pJOIN {context} ctxON ctx.instanceid = p.useridAND ctx.contextlevel = :userlevelWHERE p.reviewerid = :userid2";$params = ['userlevel' => CONTEXT_USER,'userid2' => $userid,];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {" . plan::TABLE . "} pJOIN {context} ctxON ctx.instanceid = p.useridAND ctx.contextlevel = :userlevelJOIN {" . plan_competency::TABLE . "} pcON pc.planid = p.idAND pc.usermodified = :userid3";$params = ['userlevel' => CONTEXT_USER,'userid3' => $userid,];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {" . plan::TABLE . "} pJOIN {context} ctxON ctx.instanceid = p.useridAND ctx.contextlevel = :userlevelJOIN {" . user_competency_plan::TABLE . "} upcON upc.planid = p.idAND upc.usermodified = :userid4";$params = ['userlevel' => CONTEXT_USER,'userid4' => $userid,];$contextlist->add_from_sql($sql, $params);// Add user contexts through usermodified/reviewing of competency data.$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . user_competency::TABLE . "} ucON uc.userid = ctx.instanceidAND ctx.contextlevel = :userlevel1WHERE uc.usermodified = :userid1";$params = ['userlevel1' => CONTEXT_USER,'userid1' => $userid,];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . user_competency::TABLE . "} ucON uc.userid = ctx.instanceidAND ctx.contextlevel = :userlevel1WHERE uc.reviewerid = :userid2";$params = ['userlevel1' => CONTEXT_USER,'userid2' => $userid,];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . user_competency::TABLE . "} ucON uc.userid = ctx.instanceidAND ctx.contextlevel = :userlevel1JOIN {" . evidence::TABLE . "} eON e.usercompetencyid = uc.idAND e.usermodified = :userid3";$params = ['userlevel1' => CONTEXT_USER,'userid3' => $userid,];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . user_competency::TABLE . "} ucON uc.userid = ctx.instanceidAND ctx.contextlevel = :userlevel1JOIN {" . evidence::TABLE . "} eON e.usercompetencyid = uc.idAND e.actionuserid = :userid4";$params = ['userlevel1' => CONTEXT_USER,'userid4' => $userid,];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . user_evidence::TABLE . "} ueON ue.userid = ctx.instanceidAND ctx.contextlevel = :userlevel2AND ue.usermodified = :userid5";$params = ['userlevel2' => CONTEXT_USER,'userid5' => $userid,];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . user_evidence::TABLE . "} ueON ue.userid = ctx.instanceidAND ctx.contextlevel = :userlevel2JOIN {" . user_evidence_competency::TABLE . "} uecON uec.userevidenceid = ue.idAND uec.usermodified = :userid6";$params = ['userlevel2' => CONTEXT_USER,'userid6' => $userid,];$contextlist->add_from_sql($sql, $params);// Now, the easy part, we fetch the user context for user plans and competencies.// We also fetch the course context for the state of competencies for the user in courses.$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . plan::TABLE . "} pON p.userid = ctx.instanceidAND ctx.contextlevel = :userlevel1WHERE p.userid = :userid1";$params = ['userlevel1' => CONTEXT_USER,'userid1' => $userid,];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . user_competency::TABLE . "} ucON uc.userid = ctx.instanceidAND ctx.contextlevel = :userlevel2AND uc.userid = :userid2";$params = ['userlevel2' => CONTEXT_USER,'userid2' => $userid,];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . user_evidence::TABLE . "} ueON ue.userid = ctx.instanceidAND ctx.contextlevel = :userlevel3AND ue.userid = :userid3";$params = ['userlevel3' => CONTEXT_USER,'userid3' => $userid,];$contextlist->add_from_sql($sql, $params);$sql = "SELECT DISTINCT ctx.idFROM {context} ctxJOIN {" . user_competency_course::TABLE . "} uccON ucc.courseid = ctx.instanceidAND ctx.contextlevel = :courselevelAND ucc.userid = :userid4";$params = ['courselevel' => CONTEXT_COURSE,'userid4' => $userid,];$contextlist->add_from_sql($sql, $params);// Include the user contexts in which the user commented.$sql = "SELECT ctx.idFROM {context} ctxJOIN {comments} cON c.contextid = ctx.idWHERE c.component = :componentAND c.commentarea IN (:planarea, :usercomparea)AND c.userid = :userid";$params = ['component' => 'competency', // Must not be core_competency.'planarea' => 'plan','usercomparea' => 'user_competency','userid' => $userid];$contextlist->add_from_sql($sql, $params);return $contextlist;}/*** 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();$params = ['contextid' => $context->id];// Add users who have modified the frameworks and related data in the context.$sql = "SELECT cf.usermodifiedFROM {" . competency_framework::TABLE . "} cfWHERE cf.contextid = :contextid";$userlist->add_from_sql('usermodified', $sql, $params);$sql = "SELECT c.usermodifiedFROM {" . competency_framework::TABLE . "} cfJOIN {" . competency::TABLE . "} cON c.competencyframeworkid = cf.idWHERE cf.contextid = :contextid";$userlist->add_from_sql('usermodified', $sql, $params);$sql = "SELECT cr.usermodifiedFROM {" . competency_framework::TABLE . "} cfJOIN {" . competency::TABLE . "} cON c.competencyframeworkid = cf.idJOIN {" . related_competency::TABLE . "} crON cr.competencyid = c.idWHERE cf.contextid = :contextid";$userlist->add_from_sql('usermodified', $sql, $params);// Add users who have modified the templates and related data in the context.$sql = "SELECT tpl.usermodifiedFROM {" . template::TABLE . "} tplWHERE tpl.contextid = :contextid";$userlist->add_from_sql('usermodified', $sql, $params);$sql = "SELECT tch.usermodifiedFROM {" . template::TABLE . "} tplJOIN {" . template_cohort::TABLE . "} tchON tch.templateid = tpl.idWHERE tpl.contextid = :contextid";$userlist->add_from_sql('usermodified', $sql, $params);$sql = "SELECT tc.usermodifiedFROM {" . template::TABLE . "} tplJOIN {" . template_competency::TABLE . "} tcON tc.templateid = tpl.idWHERE tpl.contextid = :contextid";$userlist->add_from_sql('usermodified', $sql, $params);// Add users if userlist is in course context.if (is_a($context, \context_course::class)) {$params = ['courseid' => $context->instanceid];$sql = "SELECT cc.usermodifiedFROM {" . course_competency::TABLE . "} ccWHERE cc.courseid = :courseid";$userlist->add_from_sql('usermodified', $sql, $params);$sql = "SELECT ccs.usermodifiedFROM {" . course_competency_settings::TABLE . "} ccsWHERE ccs.courseid = :courseid";$userlist->add_from_sql('usermodified', $sql, $params);$sql = "SELECT ucc.userid, ucc.usermodifiedFROM {" . user_competency_course::TABLE . "} uccWHERE ucc.courseid = :courseid";$userlist->add_from_sql('userid', $sql, $params);$userlist->add_from_sql('usermodified', $sql, $params);} else if (is_a($context, \context_module::class)) {// Add users if userlist is in module context.$params = ['moduleid' => $context->instanceid];$sql = "SELECT cmc.usermodifiedFROM {" . course_module_competency::TABLE . "} cmcWHERE cmc.cmid = :moduleid";$userlist->add_from_sql('usermodified', $sql, $params);} else if (is_a($context, \context_user::class)) {$params = ['userid' => $context->instanceid];// Add users through plan related data.$sql = "SELECT p.userid, p.usermodified, p.revieweridFROM {" . plan::TABLE . "} pWHERE p.userid = :userid";$userlist->add_from_sql('userid', $sql, $params);$userlist->add_from_sql('usermodified', $sql, $params);$userlist->add_from_sql('reviewerid', $sql, $params);$sql = "SELECT pc.usermodifiedFROM {" . plan::TABLE . "} pJOIN {" . plan_competency::TABLE . "} pcON pc.planid = p.idWHERE p.userid = :userid";$userlist->add_from_sql('usermodified', $sql, $params);$sql = "SELECT upc.usermodifiedFROM {" . user_competency_plan::TABLE . "} upcWHERE upc.userid = :userid";$userlist->add_from_sql('usermodified', $sql, $params);// Add users through competency data.$sql = "SELECT uc.userid, uc.usermodified, uc.revieweridFROM {" . user_competency::TABLE . "} ucWHERE uc.userid = :userid";$userlist->add_from_sql('userid', $sql, $params);$userlist->add_from_sql('usermodified', $sql, $params);$userlist->add_from_sql('reviewerid', $sql, $params);$sql = "SELECT e.usermodified, e.actionuseridFROM {" . user_competency::TABLE . "} ucJOIN {" . evidence::TABLE . "} eON e.usercompetencyid = uc.idWHERE uc.userid = :userid";$userlist->add_from_sql('usermodified', $sql, $params);$userlist->add_from_sql('actionuserid', $sql, $params);// Add users through evidence data.$sql = "SELECT ue.userid, ue.usermodifiedFROM {" . user_evidence::TABLE . "} ueWHERE ue.userid = :userid";$userlist->add_from_sql('userid', $sql, $params);$userlist->add_from_sql('usermodified', $sql, $params);$sql = "SELECT ue.usermodifiedFROM {" . user_evidence::TABLE . "} ueJOIN {" . user_evidence_competency::TABLE . "} uecON uec.userevidenceid = ue.idWHERE ue.userid = :userid";$userlist->add_from_sql('usermodified', $sql, $params);}// Add users who commented in the context.// Note: Comment component must be competency and not core_competency.\core_comment\privacy\provider::get_users_in_context_from_sql($userlist, 'com', 'competency', 'plan', $context->id);\core_comment\privacy\provider::get_users_in_context_from_sql($userlist, 'com', 'competency', 'user_competency', $context->id);}/*** Export all user data for the specified user, in the specified contexts.** We skip the enabled check for competencies, as there could be historical data. This avoids exceptions thrown from* the {@see api::require_enabled} method, which is called at various points during export via the competency API** @param approved_contextlist $contextlist The approved contexts to export information for.*/public static function export_user_data(approved_contextlist $contextlist) {// Export even if competencies are not currently enabled.api::skip_enabled();$user = $contextlist->get_user();$userid = $user->id;// Re-arrange the contexts by context level.$groupedcontexts = array_reduce($contextlist->get_contexts(), function($carry, $context) {$contextlevel = $context->contextlevel;if (!in_array($contextlevel, [CONTEXT_USER, CONTEXT_COURSE, CONTEXT_MODULE, CONTEXT_SYSTEM, CONTEXT_COURSECAT])) {return $carry;}$carry[$contextlevel][] = $context;return $carry;}, [CONTEXT_COURSE => [],CONTEXT_COURSECAT => [],CONTEXT_MODULE => [],CONTEXT_SYSTEM => [],CONTEXT_USER => [],]);// Process module contexts.static::export_user_data_in_module_contexts($userid, $groupedcontexts[CONTEXT_MODULE]);// Process course contexts.static::export_user_data_in_course_contexts($userid, $groupedcontexts[CONTEXT_COURSE]);// Process course categories context.static::export_user_data_in_category_contexts($userid, $groupedcontexts[CONTEXT_COURSECAT]);// Process system context.if (!empty($groupedcontexts[CONTEXT_SYSTEM])) {static::export_user_data_in_system_context($userid);}// Process user contexts.static::export_user_data_in_user_contexts($userid, $groupedcontexts[CONTEXT_USER]);}/*** 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;switch ($context->contextlevel) {case CONTEXT_USER:$userid = $context->instanceid;static::delete_user_evidence_of_prior_learning($userid);static::delete_user_plans($userid);static::delete_user_competencies($userid);break;case CONTEXT_COURSE:static::delete_user_competencies_in_course($context->instanceid);break;}}/*** Delete all user data for the specified user, in the specified contexts.** Here we only delete the private data of user, not whether they modified, are reviewing,* or are associated with the record on at a second level. Only data directly linked to the* user will be affected.** @param approved_contextlist $contextlist The approved contexts and user information to delete information for.*/public static function delete_data_for_user(approved_contextlist $contextlist) {$user = $contextlist->get_user();$userid = $user->id;foreach ($contextlist as $context) {switch ($context->contextlevel) {case CONTEXT_USER:if ($context->instanceid == $userid) {// Only delete the user's information when they requested their context to be deleted. We// do not take any action on other user's contexts because we don't own the data there.static::delete_user_evidence_of_prior_learning($userid);static::delete_user_plans($userid);static::delete_user_competencies($userid);}break;case CONTEXT_COURSE:static::delete_user_competencies_in_course($context->instanceid, [$userid]);break;}}}/*** Delete multiple users within a single context.** Here we only delete the private data of users, not whether they modified, are reviewing,* or are associated with the record on at a second level. Only data directly linked to the* user will be affected.** @param approved_userlist $userlist The approved context and user information to delete information for.*/public static function delete_data_for_users(approved_userlist $userlist) {$context = $userlist->get_context();$userids = $userlist->get_userids();switch ($context->contextlevel) {case CONTEXT_USER:// Only delete the user's information when their context is being deleted.// We do not take any action on other user's contexts because we don't own the data there.if (in_array($context->instanceid, $userids)) {static::delete_user_evidence_of_prior_learning($context->instanceid);static::delete_user_plans($context->instanceid);static::delete_user_competencies($context->instanceid);}break;case CONTEXT_COURSE:static::delete_user_competencies_in_course($context->instanceid, $userids);break;}}/*** Delete evidence of prior learning.** @param int $userid The user ID.* @return void*/protected static function delete_user_evidence_of_prior_learning($userid) {global $DB;$usercontext = context_user::instance($userid);$ueids = $DB->get_fieldset_select(user_evidence::TABLE, 'id', 'userid = :userid', ['userid' => $userid]);if (empty($ueids)) {return;}list($insql, $inparams) = $DB->get_in_or_equal($ueids, SQL_PARAMS_NAMED);// Delete competencies associated with user evidence.$DB->delete_records_select(user_evidence_competency::TABLE, "userevidenceid $insql", $inparams);// Delete the user evidence.$DB->delete_records_select(user_evidence::TABLE, "id $insql", $inparams);// Delete the user evidence files.$fs = get_file_storage();$fs->delete_area_files($usercontext->id, 'core_competency', 'userevidence');}/*** User plans.** @param int $userid The user ID.* @return void*/protected static function delete_user_plans($userid) {global $DB;$usercontext = context_user::instance($userid);// Remove all the comments made on plans.\core_comment\privacy\provider::delete_comments_for_all_users($usercontext, 'competency', 'plan');// Find the user plan IDs.$planids = $DB->get_fieldset_select(plan::TABLE, 'id', 'userid = :userid', ['userid' => $userid]);if (empty($planids)) {return;}list($insql, $inparams) = $DB->get_in_or_equal($planids, SQL_PARAMS_NAMED);// Delete all the competencies proficiency in the plans.$DB->delete_records_select(user_competency_plan::TABLE, "planid $insql", $inparams);// Delete all the competencies in the plans.$DB->delete_records_select(plan_competency::TABLE, "planid $insql", $inparams);// Delete all the plans.$DB->delete_records_select(plan::TABLE, "id $insql", $inparams);}/*** Delete user competency data.** @param int $userid The user ID.* @return void*/protected static function delete_user_competencies($userid) {global $DB;$usercontext = context_user::instance($userid);// Remove all the comments made on user competencies.\core_comment\privacy\provider::delete_comments_for_all_users($usercontext, 'competency', 'user_competency');// Find the user competency IDs.$ucids = $DB->get_fieldset_select(user_competency::TABLE, 'id', 'userid = :userid', ['userid' => $userid]);if (empty($ucids)) {return;}list($insql, $inparams) = $DB->get_in_or_equal($ucids, SQL_PARAMS_NAMED);// Delete all the evidence associated with competencies.$DB->delete_records_select(evidence::TABLE, "usercompetencyid $insql", $inparams);// Delete all the record of competency.$DB->delete_records_select(user_competency::TABLE, "id $insql", $inparams);}/*** Delete the record of competencies for user(s) in a course.** @param int $courseid The course ID.* @param int[] $userids The user IDs, if deleting for specific user(s).* @return void*/protected static function delete_user_competencies_in_course($courseid, $userids = []) {global $DB;$params = ['courseid' => $courseid];$where = "courseid = :courseid";if (!empty($userids)) {list($insql, $inparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);$params = $params + $inparams;$where .= " AND userid {$insql}";}$DB->delete_records_select(user_competency_course::TABLE, $where, $params);}/*** Export the user data in user context.** @param int $userid The user ID.* @param array $contexts The contexts.* @return void*/protected static function export_user_data_in_user_contexts($userid, array $contexts) {global $DB;$mycontext = context_user::instance($userid);$contextids = array_map(function($context) {return $context->id;}, $contexts);$exportowncontext = in_array($mycontext->id, $contextids);$othercontexts = array_filter($contextids, function($contextid) use ($mycontext) {return $contextid != $mycontext->id;});if ($exportowncontext) {static::export_user_data_learning_plans($mycontext);static::export_user_data_competencies($mycontext);static::export_user_data_user_evidence($mycontext);}foreach ($othercontexts as $contextid) {static::export_user_data_learning_plans_related_to_me($userid, context::instance_by_id($contextid));static::export_user_data_competencies_related_to_me($userid, context::instance_by_id($contextid));static::export_user_data_user_evidence_related_to_me($userid, context::instance_by_id($contextid));}}/*** Export the user data in systen context.** @param int $userid The user ID.* @return void*/protected static function export_user_data_in_system_context($userid) {static::export_user_data_frameworks_in_context($userid, context_system::instance());static::export_user_data_templates_in_context($userid, context_system::instance());}/*** Export the user data in category contexts.** @param int $userid The user ID.* @param array $contexts The contexts.* @return void*/protected static function export_user_data_in_category_contexts($userid, array $contexts) {$contexts = array_filter($contexts, function($context) {return $context->contextlevel == CONTEXT_COURSECAT;});if (empty($contexts)) {return;}foreach ($contexts as $context) {static::export_user_data_frameworks_in_context($userid, $context);static::export_user_data_templates_in_context($userid, $context);}}/*** Export the user data in course contexts.** @param int $userid The user whose data we're exporting.* @param array $contexts A list of contexts.* @return void*/protected static function export_user_data_in_course_contexts($userid, array $contexts) {global $DB;$contexts = array_filter($contexts, function($context) {return $context->contextlevel == CONTEXT_COURSE;});if (empty($contexts)) {return;}$helper = new performance_helper();$path = [get_string('competencies', 'core_competency')];$courseids = array_map(function($context) {return $context->instanceid;}, $contexts);// Fetch all the records of competency proficiency in the course.$ffields = competency_framework::get_sql_fields('f', 'f_');$compfields = competency::get_sql_fields('c', 'c_');$uccfields = user_competency_course::get_sql_fields('ucc', 'ucc_');$ctxfields = context_helper::get_preload_record_columns_sql('ctx');list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);$sql = "SELECT $ffields, $compfields, $uccfields, $ctxfieldsFROM {" . user_competency_course::TABLE . "} uccJOIN {" . competency::TABLE . "} cON c.id = ucc.competencyidJOIN {" . competency_framework::TABLE . "} fON f.id = c.competencyframeworkidJOIN {context} ctxON ctx.id = f.contextidWHERE ucc.userid = :useridAND ucc.courseid $insqlORDER BY ucc.courseid, c.id";$params = array_merge($inparams, ['userid' => $userid]);// Export data.$recordset = $DB->get_recordset_sql($sql, $params);static::recordset_loop_and_export($recordset, 'ucc_courseid', [], function($carry, $record) use ($helper) {context_helper::preload_from_record($record);$framework = new competency_framework(null, competency_framework::extract_record($record, 'f_'));$competency = new competency(null, competency::extract_record($record, 'c_'));$ucc = new user_competency_course(null, user_competency_course::extract_record($record, 'ucc_'));$helper->ingest_framework($framework);$carry[] = array_merge(static::transform_competency_brief($competency), ['rating' => ['rating' => static::transform_competency_grade($competency, $ucc->get('grade'), $helper),'proficient' => static::transform_proficiency($ucc->get('proficiency')),'timecreated' => transform::datetime($ucc->get('timecreated')),'timemodified' => transform::datetime($ucc->get('timemodified')),]]);return $carry;}, function($courseid, $data) use ($path) {$context = context_course::instance($courseid);writer::with_context($context)->export_data($path, (object) ['ratings' => $data]);});// Export usermodified data.static::export_user_data_in_course_contexts_associations($userid, $courseids, $path);static::export_user_data_in_course_contexts_settings($userid, $courseids, $path);static::export_user_data_in_course_contexts_rated_by_me($userid, $courseids, $path, $helper);}/*** Export the ratings given in a course.** @param int $userid The user ID.* @param array $courseids The course IDs.* @param array $path The root path.* @param performance_helper $helper The performance helper.* @return void*/protected static function export_user_data_in_course_contexts_rated_by_me($userid, $courseids, $path,performance_helper $helper) {global $DB;// Fetch all the records of competency proficiency in the course.$ffields = competency_framework::get_sql_fields('f', 'f_');$compfields = competency::get_sql_fields('c', 'c_');$uccfields = user_competency_course::get_sql_fields('ucc', 'ucc_');$ctxfields = context_helper::get_preload_record_columns_sql('ctx');list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);$sql = "SELECT $ffields, $compfields, $uccfields, $ctxfieldsFROM {" . user_competency_course::TABLE . "} uccJOIN {" . competency::TABLE . "} cON c.id = ucc.competencyidJOIN {" . competency_framework::TABLE . "} fON f.id = c.competencyframeworkidJOIN {context} ctxON ctx.id = f.contextidWHERE ucc.usermodified = :useridAND ucc.courseid $insqlORDER BY ucc.courseid, ucc.id";$params = array_merge($inparams, ['userid' => $userid]);// Export the data.static::recordset_loop_and_export($DB->get_recordset_sql($sql, $params), 'ucc_courseid', [],function($carry, $record) use ($helper) {context_helper::preload_from_record($record);$framework = new competency_framework(null, competency_framework::extract_record($record, 'f_'));$competency = new competency(null, competency::extract_record($record, 'c_'));$ucc = new user_competency_course(null, user_competency_course::extract_record($record, 'ucc_'));$helper->ingest_framework($framework);$carry[] = array_merge(static::transform_competency_brief($competency), ['rating' => ['userid' => transform::user($ucc->get('userid')),'rating' => static::transform_competency_grade($competency, $ucc->get('grade'), $helper),'proficient' => static::transform_proficiency($ucc->get('proficiency')),'timemodified' => transform::datetime($ucc->get('timemodified')),]]);return $carry;}, function($courseid, $data) use ($path) {$context = context_course::instance($courseid);writer::with_context($context)->export_related_data($path, 'rated_by_me', (object) ['ratings' => $data]);});}/*** Export user data in course contexts related to linked competencies.** @param int $userid The user ID.* @param array $courseids The course IDs.* @param array $path The root path to export at.* @return void*/protected static function export_user_data_in_course_contexts_associations($userid, $courseids, $path) {global $DB;// Fetch all the courses with associations we created or modified.$compfields = competency::get_sql_fields('c', 'c_');$ccfields = course_competency::get_sql_fields('cc', 'cc_');$ctxfields = context_helper::get_preload_record_columns_sql('ctx');list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);$sql = "SELECT $compfields, $ccfields, $ctxfieldsFROM {" . course_competency::TABLE . "} ccJOIN {" . competency::TABLE . "} cON c.id = cc.competencyidJOIN {" . competency_framework::TABLE . "} fON f.id = c.competencyframeworkidJOIN {context} ctxON ctx.id = f.contextidWHERE cc.usermodified = :useridAND cc.courseid $insqlORDER BY cc.courseid, c.id";$params = array_merge($inparams, ['userid' => $userid]);$recordset = $DB->get_recordset_sql($sql, $params);// Export the data.static::recordset_loop_and_export($recordset, 'cc_courseid', [], function($carry, $record) {context_helper::preload_from_record($record);$competency = new competency(null, competency::extract_record($record, 'c_'));$cc = new course_competency(null, course_competency::extract_record($record, 'cc_'));$carry[] = array_merge(static::transform_competency_brief($competency), ['timemodified' => transform::datetime($cc->get('timemodified')),'created_or_modified_by_you' => transform::yesno(true)]);return $carry;}, function($courseid, $data) use ($path, $userid, $DB) {$context = context_course::instance($courseid);writer::with_context($context)->export_related_data($path, 'associations', (object) ['competencies' => $data]);});}/*** Export user data in course contexts related to course settings.** @param int $userid The user ID.* @param array $courseids The course IDs.* @param array $path The root path to export at.* @return void*/protected static function export_user_data_in_course_contexts_settings($userid, $courseids, $path) {global $DB;// Fetch all the courses with associations we created or modified.$ccsfields = course_competency_settings::get_sql_fields('ccs', 'ccs_');list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);$sql = "SELECT $ccsfieldsFROM {" . course_competency_settings::TABLE . "} ccsWHERE ccs.usermodified = :useridAND ccs.courseid $insqlORDER BY ccs.courseid";$params = array_merge($inparams, ['userid' => $userid]);$recordset = $DB->get_recordset_sql($sql, $params);// Export the data.static::recordset_loop_and_export($recordset, 'ccs_courseid', [], function($carry, $record) {$ccs = new course_competency_settings(null, course_competency_settings::extract_record($record, 'ccs_'));return ['timemodified' => transform::datetime($ccs->get('timemodified')),'created_or_modified_by_you' => transform::yesno(true)];}, function($courseid, $data) use ($path, $userid, $DB) {$context = context_course::instance($courseid);writer::with_context($context)->export_related_data($path, 'settings', (object) $data);});}/*** Export the user data in module contexts.** @param int $userid The user whose data we're exporting.* @param array $contexts A list of contexts.* @return void*/protected static function export_user_data_in_module_contexts($userid, array $contexts) {global $DB;$contexts = array_filter($contexts, function($context) {return $context->contextlevel == CONTEXT_MODULE;});if (empty($contexts)) {return;}$path = [get_string('competencies', 'core_competency')];$cmids = array_map(function($context) {return $context->instanceid;}, $contexts);// Fetch all the modules with associations we created or modified.$compfields = competency::get_sql_fields('c', 'c_');$cmcfields = course_module_competency::get_sql_fields('cmc', 'cmc_');$ctxfields = context_helper::get_preload_record_columns_sql('ctx');list($insql, $inparams) = $DB->get_in_or_equal($cmids, SQL_PARAMS_NAMED);$sql = "SELECT $compfields, $cmcfields, $ctxfieldsFROM {" . course_module_competency::TABLE . "} cmcJOIN {" . competency::TABLE . "} cON c.id = cmc.competencyidJOIN {" . competency_framework::TABLE . "} fON f.id = c.competencyframeworkidJOIN {context} ctxON ctx.id = f.contextidWHERE cmc.usermodified = :useridAND cmc.cmid $insqlORDER BY cmc.cmid";$params = array_merge($inparams, ['userid' => $userid]);// Export the data.$recordset = $DB->get_recordset_sql($sql, $params);static::recordset_loop_and_export($recordset, 'cmc_cmid', [], function($carry, $record) {context_helper::preload_from_record($record);$competency = new competency(null, competency::extract_record($record, 'c_'));$cmc = new course_module_competency(null, course_module_competency::extract_record($record, 'cmc_'));$carry[] = array_merge(static::transform_competency_brief($competency), ['timecreated' => transform::datetime($cmc->get('timecreated')),'timemodified' => transform::datetime($cmc->get('timemodified')),'created_or_modified_by_you' => transform::yesno(true)]);return $carry;}, function($cmid, $data) use ($path) {$context = context_module::instance($cmid);writer::with_context($context)->export_data($path, (object) ['associations' => $data]);});}/*** Export a user's competencies.** @param context_user $context The context of the user requesting the export.* @return void*/protected static function export_user_data_competencies(context_user $context) {global $DB;$userid = $context->instanceid;$path = [get_string('competencies', 'core_competency'), get_string('competencies', 'core_competency')];$helper = new performance_helper();$cfields = competency::get_sql_fields('c', 'c_');$ucfields = user_competency::get_sql_fields('uc', 'uc_');$efields = evidence::get_sql_fields('e', 'e_');$makecomppath = function($competencyid, $data) use ($path) {return array_merge($path, [$data['name'] . ' (' . $competencyid . ')']);};$sql = "SELECT $cfields, $ucfields, $efieldsFROM {" . user_competency::TABLE . "} ucJOIN {" . competency::TABLE . "} cON c.id = uc.competencyidLEFT JOIN {" . evidence::TABLE . "} eON uc.id = e.usercompetencyidWHERE uc.userid = :useridORDER BY c.id, e.timecreated DESC, e.id DESC";$params = ['userid' => $userid];$recordset = $DB->get_recordset_sql($sql, $params);static::recordset_loop_and_export($recordset, 'c_id', null, function($carry, $record)use ($context, $userid, $helper, $makecomppath) {$competency = new competency(null, competency::extract_record($record, 'c_'));if ($carry === null) {$uc = new user_competency(null, user_competency::extract_record($record, 'uc_'));$carry = array_merge(static::transform_competency_brief($competency), ['rating' => static::transform_user_competency($userid, $uc, $competency, $helper),'evidence' => []]);\core_comment\privacy\provider::export_comments($context, 'competency', 'user_competency',$uc->get('id'), $makecomppath($competency->get('id'), $carry), false);}// There is an evidence in this record.if (!empty($record->e_id)) {$evidence = new evidence(null, evidence::extract_record($record, 'e_'));$carry['evidence'][] = static::transform_evidence($userid, $evidence, $competency, $helper);}return $carry;}, function($competencyid, $data) use ($makecomppath, $context) {writer::with_context($context)->export_data($makecomppath($competencyid, $data), (object) $data);});}/*** Export a user's learning plans.** @param context_user $context The context of the user requesting the export.* @return void*/protected static function export_user_data_learning_plans(context_user $context) {global $DB;$userid = $context->instanceid;$path = [get_string('competencies', 'core_competency'), get_string('privacy:path:plans', 'core_competency')];$helper = new performance_helper();$pfields = plan::get_sql_fields('p', 'p_');$pcfields = plan_competency::get_sql_fields('pc', 'pc_');$cfields = competency::get_sql_fields('c', 'c_');$ucfields = user_competency::get_sql_fields('uc', 'uc_');$ucpfields = user_competency_plan::get_sql_fields('ucp', 'ucp_');// The user's learning plans.$sql = "SELECT $pfields, $pcfields, $cfields, $ucfields, $ucpfieldsFROM {" . plan::TABLE . "} pLEFT JOIN {" . plan_competency::TABLE . "} pcON p.id = pc.planidAND p.templateid IS NULLAND p.status != :complete1LEFT JOIN {" . template_competency::TABLE . "} tcON tc.templateid = p.templateidAND p.templateid IS NOT NULLAND p.status != :complete2LEFT JOIN {" . user_competency_plan::TABLE . "} ucpON ucp.planid = p.idAND p.status = :complete3LEFT JOIN {" . competency::TABLE . "} cON c.id = pc.competencyidOR c.id = tc.competencyidOR c.id = ucp.competencyidLEFT JOIN {" . user_competency::TABLE . "} ucON uc.userid = p.useridAND (uc.competencyid = pc.competencyid OR uc.competencyid = tc.competencyid)WHERE p.userid = :useridORDER BY p.id, c.id";$params = ['userid' => $userid,'complete1' => plan::STATUS_COMPLETE,'complete2' => plan::STATUS_COMPLETE,'complete3' => plan::STATUS_COMPLETE,];$recordset = $DB->get_recordset_sql($sql, $params);static::recordset_loop_and_export($recordset, 'p_id', null, function($carry, $record) use ($userid, $helper, $context) {$iscomplete = $record->p_status == plan::STATUS_COMPLETE;if ($carry === null) {$plan = new plan(null, plan::extract_record($record, 'p_'));$options = ['context' => $context];$carry = ['name' => format_string($plan->get('name'), true, $options),'description' => format_text($plan->get('description'), $plan->get('descriptionformat'), $options),'status' => $plan->get_statusname(),'duedate' => $plan->get('duedate') ? transform::datetime($plan->get('duedate')) : '-','reviewerid' => $plan->get('reviewerid') ? transform::user($plan->get('reviewerid')) : '-','timecreated' => transform::datetime($plan->get('timecreated')),'timemodified' => transform::datetime($plan->get('timemodified')),'competencies' => [],];}// The plan is empty.if (empty($record->c_id)) {return $carry;}$competency = new competency(null, competency::extract_record($record, 'c_'));$rating = null;if ($iscomplete) {// When the plan is complete, we should always found the user_competency_plan.$ucp = new user_competency_plan(null, user_competency_plan::extract_record($record, 'ucp_'));$rating = static::transform_user_competency($userid, $ucp, $competency, $helper);} else if (!empty($record->uc_id)) {// When the plan is complete, there are still records of user_competency but we do not// export them here, we export them as part of the competencies structure. The reason why// we try to get the user_competency when the plan is not complete is to give the most accurate// representation of the plan as possible.$uc = new user_competency(null, user_competency::extract_record($record, 'uc_'));$rating = static::transform_user_competency($userid, $uc, $competency, $helper);}$carry['competencies'][] = array_merge(static::transform_competency_brief($competency), ['rating' => $rating]);return $carry;}, function($planid, $data) use ($context, $path) {$planpath = array_merge($path, [$data['name'] . ' (' . $planid . ')']);\core_comment\privacy\provider::export_comments($context, 'competency', 'plan', $planid, $planpath, false);writer::with_context($context)->export_data($planpath, (object) $data);});}/*** Export a user's data related to learning plans.** @param int $userid The user ID we're exporting for.* @param context_user $context The context of the user in which we're gathering data.* @return void*/protected static function export_user_data_learning_plans_related_to_me($userid, context_user $context) {global $DB;$path = [get_string('competencies', 'core_competency'),get_string('privacy:path:relatedtome', 'core_competency'),get_string('privacy:path:plans', 'core_competency'),];$plans = [];$helper = new performance_helper();$pfields = plan::get_sql_fields('p', 'p_');$pcfields = plan_competency::get_sql_fields('pc', 'pc_');$cfields = competency::get_sql_fields('c', 'c_');$ucpfields = user_competency_plan::get_sql_fields('ucp', 'ucp_');// Function to initialise a plan record.$initplan = function($record) use ($context, $userid, &$plans) {$plan = new plan(null, plan::extract_record($record, 'p_'));$options = ['context' => $context];$plans[$plan->get('id')] = ['name' => format_string($plan->get('name'), true, $options),'reviewer_is_you' => transform::yesno($plan->get('reviewerid') == $userid),'timecreated' => transform::datetime($plan->get('timecreated')),'timemodified' => transform::datetime($plan->get('timemodified')),'created_or_modified_by_you' => transform::yesno($plan->get('usermodified') == $userid),'competencies' => [],];};$initcompetency = function($record, $planid) use (&$plans) {$competency = new competency(null, competency::extract_record($record, 'c_'));$plans[$planid]['competencies'][$competency->get('id')] = static::transform_competency_brief($competency);};// Look for associations that were created.$sql = "SELECT $pfields, $pcfields, $cfieldsFROM {" . plan_competency::TABLE . "} pcJOIN {" . plan::TABLE . "} pON p.id = pc.planidJOIN {" . competency::TABLE . "} cON c.id = pc.competencyidWHERE p.userid = :targetuseridAND pc.usermodified = :useridORDER BY p.id, c.id";$params = ['targetuserid' => $context->instanceid,'userid' => $userid,];$recordset = $DB->get_recordset_sql($sql, $params);foreach ($recordset as $record) {$planid = $record->p_id;if (!isset($plans[$planid])) {$initplan($record);}$initcompetency($record, $planid);$pc = new plan_competency(null, plan_competency::extract_record($record, 'pc_'));$plans[$planid]['competencies'][$pc->get('competencyid')] = array_merge($plans[$planid]['competencies'][$pc->get('competencyid')], ['timemodified' => $pc->get('timemodified') ? transform::datetime($pc->get('timemodified')) : '-','timecreated' => $pc->get('timecreated') ? transform::datetime($pc->get('timecreated')) : '-','created_or_modified_by_you' => transform::yesno($pc->get('usermodified') == $userid),]);}$recordset->close();// Look for final grades that were given.$sql = "SELECT $pfields, $ucpfields, $cfieldsFROM {" . user_competency_plan::TABLE . "} ucpJOIN {" . plan::TABLE . "} pON p.id = ucp.planidJOIN {" . competency::TABLE . "} cON c.id = ucp.competencyidWHERE p.userid = :targetuseridAND ucp.usermodified = :useridORDER BY p.id, c.id";$params = ['targetuserid' => $context->instanceid,'userid' => $userid,];$recordset = $DB->get_recordset_sql($sql, $params);foreach ($recordset as $record) {$planid = $record->p_id;$competencyid = $record->c_id;if (!isset($plans[$planid])) {$initplan($record);}if (!isset($plans[$planid]['competencies'][$competencyid])) {$initcompetency($record, $planid);}$competency = new competency(null, competency::extract_record($record, 'c_'));$ucp = new user_competency_plan(null, user_competency_plan::extract_record($record, 'ucp_'));$plans[$planid]['competencies'][$competencyid]['rating'] = static::transform_user_competency($userid, $ucp,$competency, $helper);}$recordset->close();// Find the plans that were modified or reviewed.$insql = " > 0";$inparams = [];if (!empty($plans)) {list($insql, $inparams) = $DB->get_in_or_equal(array_keys($plans), SQL_PARAMS_NAMED, 'param', false);}$sql = "SELECT $pfieldsFROM {" . plan::TABLE . "} pLEFT JOIN {comments} cON c.contextid = :contextidAND c.commentarea = :planareaAND c.component = :competencyAND c.itemid = p.idWHERE p.userid = :targetuseridAND (p.usermodified = :userid1OR p.reviewerid = :userid2OR c.userid = :userid3)AND p.id $insqlORDER BY p.id";$params = array_merge($inparams, ['targetuserid' => $context->instanceid,'userid1' => $userid,'userid2' => $userid,'userid3' => $userid,'contextid' => $context->id,'planarea' => 'plan','competency' => 'competency']);$recordset = $DB->get_recordset_sql($sql, $params);foreach ($recordset as $record) {$planid = $record->p_id;if (!isset($plans[$planid])) {$initplan($record);}}$recordset->close();// Export each plan on its own.foreach ($plans as $planid => $plan) {$planpath = array_merge($path, ["{$plan['name']} ({$planid})"]);$plan['competencies'] = array_values($plan['competencies']); // Drop the keys.writer::with_context($context)->export_data($planpath, (object) $plan);\core_comment\privacy\provider::export_comments($context, 'competency', 'plan', $planid, $planpath, true);}}/*** Export a user's data related to competencies.** @param int $userid The user ID we're exporting for.* @param context_user $context The context of the user in which we're gathering data.* @return void*/protected static function export_user_data_competencies_related_to_me($userid, context_user $context) {global $DB;$path = [get_string('competencies', 'core_competency'),get_string('privacy:path:relatedtome', 'core_competency'),get_string('competencies', 'core_competency'),];$competencies = [];$helper = new performance_helper();$cfields = competency::get_sql_fields('c', 'c_');$ucfields = user_competency::get_sql_fields('uc', 'uc_');$efields = evidence::get_sql_fields('e', 'e_');$initcompetency = function($record) use (&$competencies) {$competency = new competency(null, competency::extract_record($record, 'c_'));$competencies[$competency->get('id')] = array_merge(static::transform_competency_brief($competency), ['evidence' => []]);};$initusercomp = function($competency, $record) use (&$competencies, $userid, $helper) {$competencyid = $competency->get('id');$uc = new user_competency(null, user_competency::extract_record($record, 'uc_'));$competencies[$competencyid]['uc_id'] = $uc->get('id');$competencies[$competencyid]['rating'] = static::transform_user_competency($userid, $uc, $competency, $helper);};// Look for evidence.$sql = "SELECT $efields, $ucfields, $cfieldsFROM {" . evidence::TABLE . "} eJOIN {" . user_competency::TABLE . "} ucON uc.id = e.usercompetencyidJOIN {" . competency::TABLE . "} cON c.id = uc.competencyidWHERE uc.userid = :targetuseridAND (e.usermodified = :userid1OR e.actionuserid = :userid2)ORDER BY c.id, e.id";$params = ['targetuserid' => $context->instanceid,'userid1' => $userid,'userid2' => $userid,];$recordset = $DB->get_recordset_sql($sql, $params);foreach ($recordset as $record) {$competencyid = $record->c_id;$competency = new competency(null, competency::extract_record($record, 'c_'));if (!isset($competencies[$competencyid])) {$initcompetency($record);}if (!array_key_exists('rating', $competencies[$competencyid])) {$competencies[$competencyid]['rating'] = null;if ($record->uc_reviewerid == $userid || $record->uc_usermodified == $userid) {$initusercomp($competency, $record);}}$evidence = new evidence(null, evidence::extract_record($record, 'e_'));$competencies[$competencyid]['evidence'][] = static::transform_evidence($userid, $evidence, $competency, $helper);}$recordset->close();// Look for user competency we modified and didn't catch.$insql = ' > 0';$inparams = [];if (!empty($competencies)) {list($insql, $inparams) = $DB->get_in_or_equal(array_keys($competencies), SQL_PARAMS_NAMED, 'param', false);}$sql = "SELECT $ucfields, $cfieldsFROM {" . user_competency::TABLE . "} ucJOIN {" . competency::TABLE . "} cON c.id = uc.competencyidLEFT JOIN {comments} cmtON cmt.contextid = :contextidAND cmt.commentarea = :ucareaAND cmt.component = :competencyAND cmt.itemid = uc.idWHERE uc.userid = :targetuseridAND (uc.usermodified = :userid1OR uc.reviewerid = :userid2OR cmt.userid = :userid3)AND uc.competencyid $insqlORDER BY c.id, uc.id";$params = array_merge($inparams, ['targetuserid' => $context->instanceid,'userid1' => $userid,'userid2' => $userid,'userid3' => $userid,'contextid' => $context->id,'ucarea' => 'user_competency','competency' => 'competency',]);$recordset = $DB->get_recordset_sql($sql, $params);foreach ($recordset as $record) {$competency = new competency(null, competency::extract_record($record, 'c_'));if (!isset($competencies[$competency->get('id')])) {$initcompetency($record);$initusercomp($competency, $record);}}$recordset->close();// Export each competency on its own.foreach ($competencies as $competencyid => $competency) {$comppath = array_merge($path, ["{$competency['name']} ({$competencyid})"]);$ucid = isset($competency['uc_id']) ? $competency['uc_id'] : null;unset($competency['uc_id']);// Send to writer.writer::with_context($context)->export_data($comppath, (object) $competency);if ($ucid) {\core_comment\privacy\provider::export_comments($context, 'competency', 'user_competency', $ucid, $comppath, true);}}}/*** Export a user's data related to evidence of prior learning.** @param int $userid The user ID we're exporting for.* @param context_user $context The context of the user in which we're gathering data.* @return void*/protected static function export_user_data_user_evidence_related_to_me($userid, context_user $context) {global $DB;$path = [get_string('competencies', 'core_competency'),get_string('privacy:path:relatedtome', 'core_competency'),get_string('privacy:path:userevidence', 'core_competency'),];$evidence = [];$helper = new performance_helper();$cfields = competency::get_sql_fields('c', 'c_');$uecfields = user_evidence_competency::get_sql_fields('uec', 'uec_');$uefields = user_evidence::get_sql_fields('ue', 'ue_');$initevidence = function($record) use (&$evidence, $userid) {$ue = new user_evidence(null, user_evidence::extract_record($record, 'ue_'));$evidence[$ue->get('id')] = static::transform_user_evidence($userid, $ue);};// Look for evidence.$sql = "SELECT $uefields, $uecfields, $cfieldsFROM {" . user_evidence_competency::TABLE . "} uecJOIN {" . user_evidence::TABLE . "} ueON ue.id = uec.userevidenceidJOIN {" . competency::TABLE . "} cON c.id = uec.competencyidWHERE ue.userid = :targetuseridAND uec.usermodified = :useridORDER BY ue.id, c.id";$params = ['targetuserid' => $context->instanceid,'userid' => $userid,];$recordset = $DB->get_recordset_sql($sql, $params);foreach ($recordset as $record) {$ueid = $record->ue_id;if (!isset($evidence[$ueid])) {$initevidence($record);}$competency = new competency(null, competency::extract_record($record, 'c_'));$uec = new user_evidence_competency(null, user_evidence_competency::extract_record($record, 'uec_'));$evidence[$ueid]['competencies'][] = array_merge(static::transform_competency_brief($competency), ['timemodified' => $uec->get('timemodified') ? transform::datetime($uec->get('timemodified')) : '-','timecreated' => $uec->get('timecreated') ? transform::datetime($uec->get('timecreated')) : '-','created_or_modified_by_you' => transform::yesno($uec->get('usermodified'))]);}$recordset->close();// Look for user evidence we modified or reviewed and didn't catch.$insql = ' > 0';$inparams = [];if (!empty($evidence)) {list($insql, $inparams) = $DB->get_in_or_equal(array_keys($evidence), SQL_PARAMS_NAMED, 'param', false);}$sql = "SELECT $uefieldsFROM {" . user_evidence::TABLE . "} ueWHERE ue.userid = :targetuseridAND ue.usermodified = :useridAND ue.id $insqlORDER BY ue.id";$params = array_merge($inparams, ['targetuserid' => $context->instanceid,'userid' => $userid,]);$recordset = $DB->get_recordset_sql($sql, $params);foreach ($recordset as $record) {$initevidence($record);}$recordset->close();// Export files, then content.foreach ($evidence as $ueid => $data) {$uepath = array_merge($path, ["{$data['name']} ({$ueid})"]);writer::with_context($context)->export_area_files($uepath, 'core_competency', 'userevidence', $ueid);writer::with_context($context)->export_data($uepath, (object) $data);}}/*** Export the evidence of prior learning of a user.** @param context_user $context The context of the user we're exporting for.* @return void*/protected static function export_user_data_user_evidence(context_user $context) {global $DB;$userid = $context->instanceid;$path = [get_string('competencies', 'core_competency'), get_string('privacy:path:userevidence', 'core_competency')];$uefields = user_evidence::get_sql_fields('ue', 'ue_');$cfields = competency::get_sql_fields('c', 'c_');$sql = "SELECT $uefields, $cfieldsFROM {" . user_evidence::TABLE . "} ueLEFT JOIN {" . user_evidence_competency::TABLE . "} uecON uec.userevidenceid = ue.idLEFT JOIN {" . competency::TABLE . "} cON c.id = uec.competencyidWHERE ue.userid = :useridORDER BY ue.id";$params = ['userid' => $userid];$recordset = $DB->get_recordset_sql($sql, $params);static::recordset_loop_and_export($recordset, 'ue_id', null, function($carry, $record) use ($userid, $context){if ($carry === null) {$ue = new user_evidence(null, user_evidence::extract_record($record, 'ue_'));$carry = static::transform_user_evidence($userid, $ue);}if (!empty($record->c_id)) {$competency = new competency(null, competency::extract_record($record, 'c_'));$carry['competencies'][] = static::transform_competency_brief($competency);}return $carry;}, function($ueid, $data) use ($context, $path) {$finalpath = array_merge($path, [$data['name'] . ' (' . $ueid . ')']);writer::with_context($context)->export_area_files($finalpath, 'core_competency', 'userevidence', $ueid);writer::with_context($context)->export_data($finalpath, (object) $data);});}/*** Export the user data related to frameworks in context.** @param int $userid The user ID.* @param context $context The context.* @return void*/protected static function export_user_data_frameworks_in_context($userid, context $context) {global $DB;$ffields = competency_framework::get_sql_fields('f', 'f_');$cfields = competency::get_sql_fields('c', 'c_');$c2fields = competency::get_sql_fields('c2', 'c2_');$rcfields = related_competency::get_sql_fields('rc', 'rc_');$frameworks = [];$initframework = function($record) use (&$frameworks, $userid) {$framework = new competency_framework(null, competency_framework::extract_record($record, 'f_'));$frameworks[$framework->get('id')] = array_merge(static::transform_framework_brief($framework), ['timemodified' => transform::datetime($framework->get('timemodified')),'created_or_modified_by_you' => transform::yesno($framework->get('usermodified') == $userid),'competencies' => []]);};$initcompetency = function($record, $prefix) use (&$frameworks, $userid) {$competency = new competency(null, competency::extract_record($record, $prefix));$frameworks[$competency->get('competencyframeworkid')]['competencies'][$competency->get('id')] = array_merge(static::transform_competency_brief($competency),['timemodified' => transform::datetime($competency->get('timemodified')),'created_or_modified_by_you' => transform::yesno($competency->get('usermodified') == $userid),'related_competencies' => []]);};// Start by looking for related competencies.$sql = "SELECT $ffields, $cfields, $c2fields, $rcfieldsFROM {" . related_competency::TABLE . "} rcJOIN {" . competency::TABLE . "} cON c.id = rc.competencyidJOIN {" . competency::TABLE . "} c2ON c2.id = rc.relatedcompetencyidJOIN {" . competency_framework::TABLE . "} fON f.id = c.competencyframeworkidWHERE rc.usermodified = :useridAND f.contextid = :contextidORDER BY rc.id, c.id";$params = ['userid' => $userid, 'contextid' => $context->id];$recordset = $DB->get_recordset_sql($sql, $params);foreach ($recordset as $record) {$frameworkid = $record->f_id;$comp1id = $record->c_id;$comp2id = $record->c2_id;if (!isset($frameworks[$frameworkid])) {$initframework($record);}foreach (['c_', 'c2_'] as $key) {$competencyid = $record->{$key . 'id'};if (!isset($frameworks[$frameworkid]['competencies'][$competencyid])) {$initcompetency($record, $key);}}$relcomp = new related_competency(null, related_competency::extract_record($record, 'rc_'));foreach (['c_' => 'c2_', 'c2_' => 'c_'] as $key => $relatedkey) {$competencyid = $record->{$key . 'id'};$competency = new competency(null, competency::extract_record($record, $relatedkey));$frameworks[$frameworkid]['competencies'][$competencyid]['related_competencies'][] = ['name' => $competency->get('shortname'),'idnumber' => $competency->get('idnumber'),'timemodified' => transform::datetime($relcomp->get('timemodified')),'created_or_modified_by_you' => transform::yesno($relcomp->get('usermodified') == $userid),];}}$recordset->close();// Now look for competencies, but skip the ones we've already seen.$competencyids = array_reduce($frameworks, function($carry, $framework) {return array_merge($carry, array_keys($framework['competencies']));}, []);$insql = ' IS NOT NULL';$inparams = [];if (!empty($competencyids)) {list($insql, $inparams) = $DB->get_in_or_equal($competencyids, SQL_PARAMS_NAMED, 'param', false);}$sql = "SELECT $ffields, $cfieldsFROM {" . competency::TABLE . "} cJOIN {" . competency_framework::TABLE . "} fON f.id = c.competencyframeworkidWHERE c.usermodified = :useridAND f.contextid = :contextidAND c.id $insqlORDER BY c.id";$params = array_merge($inparams, ['userid' => $userid, 'contextid' => $context->id]);$recordset = $DB->get_recordset_sql($sql, $params);foreach ($recordset as $record) {$frameworkid = $record->f_id;if (!isset($frameworks[$frameworkid])) {$initframework($record);}$initcompetency($record, 'c_');}$recordset->close();// Now look for frameworks, but skip the ones we've already seen.$frameworkids = array_keys($frameworks);$insql = ' IS NOT NULL';$inparams = [];if (!empty($frameworkids)) {list($insql, $inparams) = $DB->get_in_or_equal($frameworkids, SQL_PARAMS_NAMED, 'param', false);}$sql = "SELECT $ffieldsFROM {" . competency_framework::TABLE . "} fWHERE f.usermodified = :useridAND f.contextid = :contextidAND f.id $insqlORDER BY f.id";$params = array_merge($inparams, ['userid' => $userid, 'contextid' => $context->id]);$recordset = $DB->get_recordset_sql($sql, $params);foreach ($recordset as $record) {context_helper::preload_from_record($record);$initframework($record);}$recordset->close();// Export all the things!writer::with_context($context)->export_related_data([get_string('competencies', 'core_competency')],'frameworks',(object) [// Drop the temporary IDs.'frameworks' => array_reduce($frameworks, function($carry, $item) {$item['competencies'] = array_values($item['competencies']);$carry[] = $item;return $carry;}, [])]);}/*** Export the user data related to templates in contexts.** @param int $userid The user ID.* @param context $context The context.* @return void*/protected static function export_user_data_templates_in_context($userid, context $context) {global $DB;$tfields = template::get_sql_fields('t', 't_');$cfields = competency::get_sql_fields('c', 'c_');$tcfields = template_competency::get_sql_fields('tc', 'tc_');$tchfields = template_cohort::get_sql_fields('tch', 'tch_');$templates = [];$inittemplate = function($record) use (&$templates, $userid) {$template = new template(null, template::extract_record($record, 't_'));$templates[$template->get('id')] = array_merge(static::transform_template_brief($template), ['timemodified' => transform::datetime($template->get('timemodified')),'created_or_modified_by_you' => transform::yesno($template->get('usermodified') == $userid),'competencies' => [],'cohorts' => []]);};// Find the template competencies.$sql = "SELECT $tfields, $cfields, $tcfieldsFROM {" . template_competency::TABLE . "} tcJOIN {" . template::TABLE . "} tON t.id = tc.templateidJOIN {" . competency::TABLE . "} cON c.id = tc.competencyidWHERE t.contextid = :contextidAND tc.usermodified = :useridORDER BY t.id, tc.id";$params = ['userid' => $userid, 'contextid' => $context->id];$recordset = $DB->get_recordset_sql($sql, $params);foreach ($recordset as $record) {$templateid = $record->t_id;if (!isset($templates[$templateid])) {$inittemplate($record);}$tplcomp = new template_competency(null, template_competency::extract_record($record, 'tc_'));$competency = new competency(null, competency::extract_record($record, 'c_'));$templates[$templateid]['competencies'][] = array_merge(static::transform_competency_brief($competency),['timemodified' => transform::datetime($tplcomp->get('timemodified')),'created_or_modified_by_you' => transform::yesno($tplcomp->get('usermodified') == $userid)]);}$recordset->close();// Find the template cohorts.$sql = "SELECT $tfields, $tchfields, c.name AS cohortnameFROM {" . template_cohort::TABLE . "} tchJOIN {" . template::TABLE . "} tON t.id = tch.templateidJOIN {cohort} cON c.id = tch.cohortidWHERE t.contextid = :contextidAND tch.usermodified = :useridORDER BY t.id, tch.id";$params = ['userid' => $userid, 'contextid' => $context->id];$recordset = $DB->get_recordset_sql($sql, $params);foreach ($recordset as $record) {$templateid = $record->t_id;if (!isset($templates[$templateid])) {$inittemplate($record);}$tplcohort = new template_cohort(null, template_cohort::extract_record($record, 'tch_'));$templates[$templateid]['cohorts'][] = ['name' => $record->cohortname,'timemodified' => transform::datetime($tplcohort->get('timemodified')),'created_or_modified_by_you' => transform::yesno($tplcohort->get('usermodified') == $userid)];}$recordset->close();// Find the modified templates which we haven't been found yet.$templateids = array_keys($templates);$insql = "IS NOT NULL";$inparams = [];if (!empty($templateids)) {list($insql, $inparams) = $DB->get_in_or_equal($templateids, SQL_PARAMS_NAMED, 'param', false);}$sql = "SELECT $tfieldsFROM {" . template::TABLE . "} tWHERE t.contextid = :contextidAND t.usermodified = :useridAND t.id $insqlORDER BY t.id";$params = array_merge($inparams, ['userid' => $userid, 'contextid' => $context->id]);$recordset = $DB->get_recordset_sql($sql, $params);foreach ($recordset as $record) {$inittemplate($record);}$recordset->close();// Export all the things!writer::with_context($context)->export_related_data([get_string('competencies', 'core_competency')],'templates', (object) ['templates' => array_values($templates)]);}/*** Transform a competency into a brief description.** @param competency $competency The competency.* @return array*/protected static function transform_competency_brief(competency $competency) {global $OUTPUT;$exporter = new \core_competency\external\competency_exporter($competency, ['context' => $competency->get_context()]);$data = $exporter->export($OUTPUT);return ['idnumber' => $data->idnumber,'name' => $data->shortname,'description' => $data->description];}/*** Transform a competency rating.** @param competency $competency The competency.* @param int $grade The grade.* @param performance_helper $helper The performance helper.* @return string*/protected static function transform_competency_grade(competency $competency, $grade, performance_helper $helper) {if ($grade === null) {return '-';}$scale = $helper->get_scale_from_competency($competency);return $scale->scale_items[$grade - 1];}/*** Transform an evidence.** @param int $userid The user ID we are exporting for.* @param evidence $evidence The evidence.* @param competency $competency The associated competency.* @param performance_helper $helper The performance helper.* @return array*/protected static function transform_evidence($userid, evidence $evidence, competency $competency, performance_helper $helper) {$action = $evidence->get('action');$actiontxt = '?';if ($action == evidence::ACTION_LOG) {$actiontxt = get_string('privacy:evidence:action:log', 'core_competency');} else if ($action == evidence::ACTION_COMPLETE) {$actiontxt = get_string('privacy:evidence:action:complete', 'core_competency');} else if ($action == evidence::ACTION_OVERRIDE) {$actiontxt = get_string('privacy:evidence:action:override', 'core_competency');}$actionuserid = $evidence->get('actionuserid');return ['action' => $actiontxt,'actionuserid' => $actionuserid ? transform::user($actionuserid) : '-','acting_user_is_you' => transform::yesno($userid == $actionuserid),'description' => (string) $evidence->get_description(),'url' => $evidence->get('url'),'grade' => static::transform_competency_grade($competency, $evidence->get('grade'), $helper),'note' => $evidence->get('note'),'timecreated' => transform::datetime($evidence->get('timecreated')),'timemodified' => transform::datetime($evidence->get('timemodified')),'created_or_modified_by_you' => transform::yesno($userid == $evidence->get('usermodified'))];}/*** Transform a framework into a brief description.** @param competency_framework $framework The framework.* @return array*/protected static function transform_framework_brief(competency_framework $framework) {global $OUTPUT;$exporter = new \core_competency\external\competency_framework_exporter($framework);$data = $exporter->export($OUTPUT);return ['name' => $data->shortname,'idnumber' => $data->idnumber,'description' => $data->description];}/*** Transform a template into a brief description.** @param template $template The Template.* @return array*/protected static function transform_template_brief(template $template) {global $OUTPUT;$exporter = new \core_competency\external\template_exporter($template);$data = $exporter->export($OUTPUT);return ['name' => $data->shortname,'description' => $data->description];}/*** Transform proficiency.** @param null|bool $proficiency The proficiency.* @return string*/protected static function transform_proficiency($proficiency) {return $proficiency !== null ? transform::yesno($proficiency) : '-';}/*** Transform user competency.** @param int $userid The user ID we are exporting for.* @param user_competency|user_competency_plan|user_competency_course $uc The user competency.* @param competency $competency The associated competency.* @param performance_helper $helper The performance helper.* @return array*/protected static function transform_user_competency($userid, $uc, competency $competency, performance_helper $helper) {$data = ['proficient' => static::transform_proficiency($uc->get('proficiency')),'rating' => static::transform_competency_grade($competency, $uc->get('grade'), $helper),'timemodified' => $uc->get('timemodified') ? transform::datetime($uc->get('timemodified')) : '-','timecreated' => $uc->get('timecreated') ? transform::datetime($uc->get('timecreated')) : '-','created_or_modified_by_you' => transform::yesno($uc->get('usermodified') == $userid),];if ($uc instanceof user_competency) {$reviewer = $uc->get('reviewerid');$data['status'] = (string) user_competency::get_status_name($uc->get('status'));$data['reviewerid'] = $reviewer ? transform::user($reviewer) : '-';$data['reviewer_is_you'] = transform::yesno($reviewer == $userid);}return $data;}/*** Transform a user evidence.** @param int $userid The user we are exporting for.* @param user_evidence $ue The evidence of prior learning.* @return array*/protected static function transform_user_evidence($userid, user_evidence $ue) {$options = ['context' => $ue->get_context()];return ['name' => format_string($ue->get('name'), true, $options),'description' => format_text($ue->get('description'), $ue->get('descriptionformat'), $options),'url' => $ue->get('url'),'timecreated' => $ue->get('timecreated') ? transform::datetime($ue->get('timecreated')) : '-','timemodified' => $ue->get('timemodified') ? transform::datetime($ue->get('timemodified')) : '-','created_or_modified_by_you' => transform::yesno($ue->get('usermodified') == $userid),'competencies' => []];}/*** Loop and export from a recordset.** @param moodle_recordset $recordset The recordset.* @param string $splitkey The record key to determine when to export.* @param mixed $initial The initial data to reduce from.* @param callable $reducer The function to return the dataset, receives current dataset, and the current record.* @param callable $export The function to export the dataset, receives the last value from $splitkey and the dataset.* @return void*/protected static function recordset_loop_and_export(moodle_recordset $recordset, $splitkey, $initial,callable $reducer, callable $export) {$data = $initial;$lastid = null;foreach ($recordset as $record) {if ($lastid && $record->{$splitkey} != $lastid) {$export($lastid, $data);$data = $initial;}$data = $reducer($data, $record);$lastid = $record->{$splitkey};}$recordset->close();if (!empty($lastid)) {$export($lastid, $data);}}}