Files
moodle/competency/classes/privacy/provider.php
T
Neill Magill ff5f100601 MDL-77965 competency: Improve query performance and data
This change removes LEFT JOINS to improve the performance
when run against Moodle sites with very large data sets.

It will also cause some additional contexts to be returned:

1. When a user has modified a user_evidence_competency
   without modifying its associated user_evidence record
2024-02-23 09:32:45 +00:00

2310 lines
97 KiB
PHP

<?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.id
FROM {context} ctx
JOIN {" . competency_framework::TABLE . "} cf ON cf.contextid = ctx.id
WHERE cf.usermodified = :userid1";
$params = ['userid1' => $userid];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . competency_framework::TABLE . "} cf ON cf.contextid = ctx.id
JOIN {" . competency::TABLE . "} c ON c.competencyframeworkid = cf.id
WHERE c.usermodified = :userid2";
$params = ['userid2' => $userid];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . competency_framework::TABLE . "} cf ON cf.contextid = ctx.id
JOIN {" . competency::TABLE . "} c ON c.competencyframeworkid = cf.id
JOIN {" . related_competency::TABLE . "} cr ON cr.competencyid = c.id
WHERE 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.id
FROM {context} ctx
JOIN {" . template::TABLE . "} tpl
ON tpl.contextid = ctx.id
WHERE tpl.usermodified = :userid1";
$params = ['userid1' => $userid];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . template::TABLE . "} tpl
ON tpl.contextid = ctx.id
JOIN {" . template_cohort::TABLE . "} tch
ON tch.templateid = tpl.id
AND tch.usermodified = :userid2";
$params = ['userid2' => $userid];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . template::TABLE . "} tpl
ON tpl.contextid = ctx.id
JOIN {" . template_competency::TABLE . "} tc
ON tc.templateid = tpl.id
AND tc.usermodified = :userid3";
$params = ['userid3' => $userid];
$contextlist->add_from_sql($sql, $params);
// Find the possible course contexts.
$sql = "
SELECT DISTINCT ctx.id
FROM {" . course_competency::TABLE . "} cc
JOIN {context} ctx
ON ctx.instanceid = cc.courseid
AND ctx.contextlevel = :courselevel
WHERE cc.usermodified = :userid";
$params = ['courselevel' => CONTEXT_COURSE, 'userid' => $userid];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {" . course_competency_settings::TABLE . "} ccs
JOIN {context} ctx
ON ctx.instanceid = ccs.courseid
AND ctx.contextlevel = :courselevel
WHERE ccs.usermodified = :userid";
$params = ['courselevel' => CONTEXT_COURSE, 'userid' => $userid];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {" . user_competency_course::TABLE . "} ucc
JOIN {context} ctx
ON ctx.instanceid = ucc.courseid
AND ctx.contextlevel = :courselevel
WHERE ucc.usermodified = :userid";
$params = ['courselevel' => CONTEXT_COURSE, 'userid' => $userid];
$contextlist->add_from_sql($sql, $params);
// Find the possible module contexts.
$sql = "
SELECT DISTINCT ctx.id
FROM {" . course_module_competency::TABLE . "} cmc
JOIN {context} ctx
ON ctx.instanceid = cmc.cmid
AND ctx.contextlevel = :modulelevel
WHERE 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.id
FROM {" . plan::TABLE . "} p
JOIN {context} ctx
ON ctx.instanceid = p.userid
AND ctx.contextlevel = :userlevel
WHERE p.usermodified = :userid1";
$params = [
'userlevel' => CONTEXT_USER,
'userid1' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {" . plan::TABLE . "} p
JOIN {context} ctx
ON ctx.instanceid = p.userid
AND ctx.contextlevel = :userlevel
WHERE p.reviewerid = :userid2";
$params = [
'userlevel' => CONTEXT_USER,
'userid2' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {" . plan::TABLE . "} p
JOIN {context} ctx
ON ctx.instanceid = p.userid
AND ctx.contextlevel = :userlevel
JOIN {" . plan_competency::TABLE . "} pc
ON pc.planid = p.id
AND pc.usermodified = :userid3";
$params = [
'userlevel' => CONTEXT_USER,
'userid3' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {" . plan::TABLE . "} p
JOIN {context} ctx
ON ctx.instanceid = p.userid
AND ctx.contextlevel = :userlevel
JOIN {" . user_competency_plan::TABLE . "} upc
ON upc.planid = p.id
AND 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.id
FROM {context} ctx
JOIN {" . user_competency::TABLE . "} uc
ON uc.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel1
WHERE uc.usermodified = :userid1";
$params = [
'userlevel1' => CONTEXT_USER,
'userid1' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_competency::TABLE . "} uc
ON uc.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel1
WHERE uc.reviewerid = :userid2";
$params = [
'userlevel1' => CONTEXT_USER,
'userid2' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_competency::TABLE . "} uc
ON uc.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel1
JOIN {" . evidence::TABLE . "} e
ON e.usercompetencyid = uc.id
AND e.usermodified = :userid3";
$params = [
'userlevel1' => CONTEXT_USER,
'userid3' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_competency::TABLE . "} uc
ON uc.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel1
JOIN {" . evidence::TABLE . "} e
ON e.usercompetencyid = uc.id
AND e.actionuserid = :userid4";
$params = [
'userlevel1' => CONTEXT_USER,
'userid4' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_evidence::TABLE . "} ue
ON ue.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel2
AND ue.usermodified = :userid5";
$params = [
'userlevel2' => CONTEXT_USER,
'userid5' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_evidence::TABLE . "} ue
ON ue.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel2
JOIN {" . user_evidence_competency::TABLE . "} uec
ON uec.userevidenceid = ue.id
AND 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.id
FROM {context} ctx
JOIN {" . plan::TABLE . "} p
ON p.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel1
WHERE p.userid = :userid1";
$params = [
'userlevel1' => CONTEXT_USER,
'userid1' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_competency::TABLE . "} uc
ON uc.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel2
AND uc.userid = :userid2";
$params = [
'userlevel2' => CONTEXT_USER,
'userid2' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_evidence::TABLE . "} ue
ON ue.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel3
AND ue.userid = :userid3";
$params = [
'userlevel3' => CONTEXT_USER,
'userid3' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_competency_course::TABLE . "} ucc
ON ucc.courseid = ctx.instanceid
AND ctx.contextlevel = :courselevel
AND 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.id
FROM {context} ctx
JOIN {comments} c
ON c.contextid = ctx.id
WHERE c.component = :component
AND 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.usermodified
FROM {" . competency_framework::TABLE . "} cf
WHERE cf.contextid = :contextid";
$userlist->add_from_sql('usermodified', $sql, $params);
$sql = "
SELECT c.usermodified
FROM {" . competency_framework::TABLE . "} cf
JOIN {" . competency::TABLE . "} c
ON c.competencyframeworkid = cf.id
WHERE cf.contextid = :contextid";
$userlist->add_from_sql('usermodified', $sql, $params);
$sql = "
SELECT cr.usermodified
FROM {" . competency_framework::TABLE . "} cf
JOIN {" . competency::TABLE . "} c
ON c.competencyframeworkid = cf.id
JOIN {" . related_competency::TABLE . "} cr
ON cr.competencyid = c.id
WHERE 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.usermodified
FROM {" . template::TABLE . "} tpl
WHERE tpl.contextid = :contextid";
$userlist->add_from_sql('usermodified', $sql, $params);
$sql = "
SELECT tch.usermodified
FROM {" . template::TABLE . "} tpl
JOIN {" . template_cohort::TABLE . "} tch
ON tch.templateid = tpl.id
WHERE tpl.contextid = :contextid";
$userlist->add_from_sql('usermodified', $sql, $params);
$sql = "
SELECT tc.usermodified
FROM {" . template::TABLE . "} tpl
JOIN {" . template_competency::TABLE . "} tc
ON tc.templateid = tpl.id
WHERE 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.usermodified
FROM {" . course_competency::TABLE . "} cc
WHERE cc.courseid = :courseid";
$userlist->add_from_sql('usermodified', $sql, $params);
$sql = "
SELECT ccs.usermodified
FROM {" . course_competency_settings::TABLE . "} ccs
WHERE ccs.courseid = :courseid";
$userlist->add_from_sql('usermodified', $sql, $params);
$sql = "
SELECT ucc.userid, ucc.usermodified
FROM {" . user_competency_course::TABLE . "} ucc
WHERE 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.usermodified
FROM {" . course_module_competency::TABLE . "} cmc
WHERE 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.reviewerid
FROM {" . plan::TABLE . "} p
WHERE 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.usermodified
FROM {" . plan::TABLE . "} p
JOIN {" . plan_competency::TABLE . "} pc
ON pc.planid = p.id
WHERE p.userid = :userid";
$userlist->add_from_sql('usermodified', $sql, $params);
$sql = "
SELECT upc.usermodified
FROM {" . user_competency_plan::TABLE . "} upc
WHERE upc.userid = :userid";
$userlist->add_from_sql('usermodified', $sql, $params);
// Add users through competency data.
$sql = "
SELECT uc.userid, uc.usermodified, uc.reviewerid
FROM {" . user_competency::TABLE . "} uc
WHERE 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.actionuserid
FROM {" . user_competency::TABLE . "} uc
JOIN {" . evidence::TABLE . "} e
ON e.usercompetencyid = uc.id
WHERE 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.usermodified
FROM {" . user_evidence::TABLE . "} ue
WHERE ue.userid = :userid";
$userlist->add_from_sql('userid', $sql, $params);
$userlist->add_from_sql('usermodified', $sql, $params);
$sql = "
SELECT ue.usermodified
FROM {" . user_evidence::TABLE . "} ue
JOIN {" . user_evidence_competency::TABLE . "} uec
ON uec.userevidenceid = ue.id
WHERE 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, $ctxfields
FROM {" . user_competency_course::TABLE . "} ucc
JOIN {" . competency::TABLE . "} c
ON c.id = ucc.competencyid
JOIN {" . competency_framework::TABLE . "} f
ON f.id = c.competencyframeworkid
JOIN {context} ctx
ON ctx.id = f.contextid
WHERE ucc.userid = :userid
AND ucc.courseid $insql
ORDER 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, $ctxfields
FROM {" . user_competency_course::TABLE . "} ucc
JOIN {" . competency::TABLE . "} c
ON c.id = ucc.competencyid
JOIN {" . competency_framework::TABLE . "} f
ON f.id = c.competencyframeworkid
JOIN {context} ctx
ON ctx.id = f.contextid
WHERE ucc.usermodified = :userid
AND ucc.courseid $insql
ORDER 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, $ctxfields
FROM {" . course_competency::TABLE . "} cc
JOIN {" . competency::TABLE . "} c
ON c.id = cc.competencyid
JOIN {" . competency_framework::TABLE . "} f
ON f.id = c.competencyframeworkid
JOIN {context} ctx
ON ctx.id = f.contextid
WHERE cc.usermodified = :userid
AND cc.courseid $insql
ORDER 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 $ccsfields
FROM {" . course_competency_settings::TABLE . "} ccs
WHERE ccs.usermodified = :userid
AND ccs.courseid $insql
ORDER 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, $ctxfields
FROM {" . course_module_competency::TABLE . "} cmc
JOIN {" . competency::TABLE . "} c
ON c.id = cmc.competencyid
JOIN {" . competency_framework::TABLE . "} f
ON f.id = c.competencyframeworkid
JOIN {context} ctx
ON ctx.id = f.contextid
WHERE cmc.usermodified = :userid
AND cmc.cmid $insql
ORDER 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, $efields
FROM {" . user_competency::TABLE . "} uc
JOIN {" . competency::TABLE . "} c
ON c.id = uc.competencyid
LEFT JOIN {" . evidence::TABLE . "} e
ON uc.id = e.usercompetencyid
WHERE uc.userid = :userid
ORDER 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, $ucpfields
FROM {" . plan::TABLE . "} p
LEFT JOIN {" . plan_competency::TABLE . "} pc
ON p.id = pc.planid
AND p.templateid IS NULL
AND p.status != :complete1
LEFT JOIN {" . template_competency::TABLE . "} tc
ON tc.templateid = p.templateid
AND p.templateid IS NOT NULL
AND p.status != :complete2
LEFT JOIN {" . user_competency_plan::TABLE . "} ucp
ON ucp.planid = p.id
AND p.status = :complete3
LEFT JOIN {" . competency::TABLE . "} c
ON c.id = pc.competencyid
OR c.id = tc.competencyid
OR c.id = ucp.competencyid
LEFT JOIN {" . user_competency::TABLE . "} uc
ON uc.userid = p.userid
AND (uc.competencyid = pc.competencyid OR uc.competencyid = tc.competencyid)
WHERE p.userid = :userid
ORDER 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, $cfields
FROM {" . plan_competency::TABLE . "} pc
JOIN {" . plan::TABLE . "} p
ON p.id = pc.planid
JOIN {" . competency::TABLE . "} c
ON c.id = pc.competencyid
WHERE p.userid = :targetuserid
AND pc.usermodified = :userid
ORDER 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, $cfields
FROM {" . user_competency_plan::TABLE . "} ucp
JOIN {" . plan::TABLE . "} p
ON p.id = ucp.planid
JOIN {" . competency::TABLE . "} c
ON c.id = ucp.competencyid
WHERE p.userid = :targetuserid
AND ucp.usermodified = :userid
ORDER 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 $pfields
FROM {" . plan::TABLE . "} p
LEFT JOIN {comments} c
ON c.contextid = :contextid
AND c.commentarea = :planarea
AND c.component = :competency
AND c.itemid = p.id
WHERE p.userid = :targetuserid
AND (p.usermodified = :userid1
OR p.reviewerid = :userid2
OR c.userid = :userid3)
AND p.id $insql
ORDER 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, $cfields
FROM {" . evidence::TABLE . "} e
JOIN {" . user_competency::TABLE . "} uc
ON uc.id = e.usercompetencyid
JOIN {" . competency::TABLE . "} c
ON c.id = uc.competencyid
WHERE uc.userid = :targetuserid
AND (e.usermodified = :userid1
OR 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, $cfields
FROM {" . user_competency::TABLE . "} uc
JOIN {" . competency::TABLE . "} c
ON c.id = uc.competencyid
LEFT JOIN {comments} cmt
ON cmt.contextid = :contextid
AND cmt.commentarea = :ucarea
AND cmt.component = :competency
AND cmt.itemid = uc.id
WHERE uc.userid = :targetuserid
AND (uc.usermodified = :userid1
OR uc.reviewerid = :userid2
OR cmt.userid = :userid3)
AND uc.competencyid $insql
ORDER 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, $cfields
FROM {" . user_evidence_competency::TABLE . "} uec
JOIN {" . user_evidence::TABLE . "} ue
ON ue.id = uec.userevidenceid
JOIN {" . competency::TABLE . "} c
ON c.id = uec.competencyid
WHERE ue.userid = :targetuserid
AND uec.usermodified = :userid
ORDER 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 $uefields
FROM {" . user_evidence::TABLE . "} ue
WHERE ue.userid = :targetuserid
AND ue.usermodified = :userid
AND ue.id $insql
ORDER 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, $cfields
FROM {" . user_evidence::TABLE . "} ue
LEFT JOIN {" . user_evidence_competency::TABLE . "} uec
ON uec.userevidenceid = ue.id
LEFT JOIN {" . competency::TABLE . "} c
ON c.id = uec.competencyid
WHERE ue.userid = :userid
ORDER 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, $rcfields
FROM {" . related_competency::TABLE . "} rc
JOIN {" . competency::TABLE . "} c
ON c.id = rc.competencyid
JOIN {" . competency::TABLE . "} c2
ON c2.id = rc.relatedcompetencyid
JOIN {" . competency_framework::TABLE . "} f
ON f.id = c.competencyframeworkid
WHERE rc.usermodified = :userid
AND f.contextid = :contextid
ORDER 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, $cfields
FROM {" . competency::TABLE . "} c
JOIN {" . competency_framework::TABLE . "} f
ON f.id = c.competencyframeworkid
WHERE c.usermodified = :userid
AND f.contextid = :contextid
AND c.id $insql
ORDER 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 $ffields
FROM {" . competency_framework::TABLE . "} f
WHERE f.usermodified = :userid
AND f.contextid = :contextid
AND f.id $insql
ORDER 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, $tcfields
FROM {" . template_competency::TABLE . "} tc
JOIN {" . template::TABLE . "} t
ON t.id = tc.templateid
JOIN {" . competency::TABLE . "} c
ON c.id = tc.competencyid
WHERE t.contextid = :contextid
AND tc.usermodified = :userid
ORDER 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 cohortname
FROM {" . template_cohort::TABLE . "} tch
JOIN {" . template::TABLE . "} t
ON t.id = tch.templateid
JOIN {cohort} c
ON c.id = tch.cohortid
WHERE t.contextid = :contextid
AND tch.usermodified = :userid
ORDER 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 $tfields
FROM {" . template::TABLE . "} t
WHERE t.contextid = :contextid
AND t.usermodified = :userid
AND t.id $insql
ORDER 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);
}
}
}