You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2194 lines
93 KiB
2194 lines
93 KiB
<?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/>.
|
|
|
|
/**
|
|
* Data provider.
|
|
*
|
|
* @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
|
|
*/
|
|
|
|
namespace core_competency\privacy;
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
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
|
|
LEFT JOIN {" . competency::TABLE . "} c
|
|
ON c.competencyframeworkid = cf.id
|
|
LEFT JOIN {" . related_competency::TABLE . "} cr
|
|
ON cr.competencyid = c.id
|
|
WHERE cf.usermodified = :userid1
|
|
OR c.usermodified = :userid2
|
|
OR cr.usermodified = :userid3";
|
|
$params = ['userid1' => $userid, 'userid2' => $userid, '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
|
|
LEFT JOIN {" . template_cohort::TABLE . "} tch
|
|
ON tch.templateid = tpl.id
|
|
AND tch.usermodified = :userid2
|
|
LEFT JOIN {" . template_competency::TABLE . "} tc
|
|
ON tc.templateid = tpl.id
|
|
AND tc.usermodified = :userid3
|
|
WHERE tpl.usermodified = :userid1
|
|
OR tch.id IS NOT NULL
|
|
OR tc.id IS NOT NULL";
|
|
$params = ['userid1' => $userid, 'userid2' => $userid, '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
|
|
LEFT JOIN {" . plan_competency::TABLE . "} pc
|
|
ON pc.planid = p.id
|
|
AND pc.usermodified = :userid3
|
|
LEFT JOIN {" . user_competency_plan::TABLE . "} upc
|
|
ON upc.planid = p.id
|
|
AND upc.usermodified = :userid4
|
|
WHERE p.usermodified = :userid1
|
|
OR p.reviewerid = :userid2
|
|
OR pc.id IS NOT NULL
|
|
OR upc.id IS NOT NULL";
|
|
$params = [
|
|
'userlevel' => CONTEXT_USER,
|
|
'userid1' => $userid,
|
|
'userid2' => $userid,
|
|
'userid3' => $userid,
|
|
'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
|
|
LEFT JOIN {" . user_competency::TABLE . "} uc
|
|
ON uc.userid = ctx.instanceid
|
|
AND ctx.contextlevel = :userlevel1
|
|
LEFT JOIN {" . evidence::TABLE . "} e
|
|
ON e.usercompetencyid = uc.id
|
|
AND (e.usermodified = :userid3 OR e.actionuserid = :userid4)
|
|
LEFT JOIN {" . user_evidence::TABLE . "} ue
|
|
ON ue.userid = ctx.instanceid
|
|
AND ctx.contextlevel = :userlevel2
|
|
AND ue.usermodified = :userid5
|
|
LEFT JOIN {" . user_evidence_competency::TABLE . "} uec
|
|
ON uec.userevidenceid = ue.id
|
|
AND uec.usermodified = :userid6
|
|
WHERE uc.usermodified = :userid1
|
|
OR uc.reviewerid = :userid2
|
|
OR e.id IS NOT NULL
|
|
OR ue.id IS NOT NULL
|
|
OR uec.id IS NOT NULL";
|
|
$params = [
|
|
'userlevel1' => CONTEXT_USER,
|
|
'userlevel2' => CONTEXT_USER,
|
|
'userid1' => $userid,
|
|
'userid2' => $userid,
|
|
'userid3' => $userid,
|
|
'userid4' => $userid,
|
|
'userid5' => $userid,
|
|
'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
|
|
LEFT JOIN {" . plan::TABLE . "} p
|
|
ON p.userid = ctx.instanceid
|
|
AND ctx.contextlevel = :userlevel1
|
|
LEFT JOIN {" . user_competency::TABLE . "} uc
|
|
ON uc.userid = ctx.instanceid
|
|
AND ctx.contextlevel = :userlevel2
|
|
AND uc.userid = :userid2
|
|
LEFT JOIN {" . user_evidence::TABLE . "} ue
|
|
ON ue.userid = ctx.instanceid
|
|
AND ctx.contextlevel = :userlevel3
|
|
AND ue.userid = :userid3
|
|
LEFT JOIN {" . user_competency_course::TABLE . "} ucc
|
|
ON ucc.courseid = ctx.instanceid
|
|
AND ctx.contextlevel = :courselevel
|
|
AND ucc.userid = :userid4
|
|
WHERE p.userid = :userid1
|
|
OR uc.id IS NOT NULL
|
|
OR ue.id IS NOT NULL
|
|
OR ucc.id IS NOT NULL";
|
|
$params = [
|
|
'userlevel1' => CONTEXT_USER,
|
|
'userlevel2' => CONTEXT_USER,
|
|
'userlevel3' => CONTEXT_USER,
|
|
'courselevel' => CONTEXT_COURSE,
|
|
'userid1' => $userid,
|
|
'userid2' => $userid,
|
|
'userid3' => $userid,
|
|
'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.
|
|
*
|
|
* @param approved_contextlist $contextlist The approved contexts to export information for.
|
|
*/
|
|
public static function export_user_data(approved_contextlist $contextlist) {
|
|
$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);
|
|
}
|
|
}
|
|
}
|
|
|