Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace core_competency\privacy; 18 19 use context; 20 use context_course; 21 use context_helper; 22 use context_module; 23 use context_system; 24 use context_user; 25 use moodle_recordset; 26 use core_competency\api; 27 use core_competency\competency; 28 use core_competency\competency_framework; 29 use core_competency\course_competency; 30 use core_competency\course_competency_settings; 31 use core_competency\course_module_competency; 32 use core_competency\evidence; 33 use core_competency\plan; 34 use core_competency\plan_competency; 35 use core_competency\related_competency; 36 use core_competency\template; 37 use core_competency\template_cohort; 38 use core_competency\template_competency; 39 use core_competency\user_competency; 40 use core_competency\user_competency_course; 41 use core_competency\user_competency_plan; 42 use core_competency\user_evidence; 43 use core_competency\user_evidence_competency; 44 use core_competency\external\performance_helper; 45 use core_privacy\local\metadata\collection; 46 use core_privacy\local\request\approved_userlist; 47 use core_privacy\local\request\contextlist; 48 use core_privacy\local\request\approved_contextlist; 49 use core_privacy\local\request\transform; 50 use core_privacy\local\request\userlist; 51 use core_privacy\local\request\writer; 52 53 /** 54 * Data provider class. 55 * 56 * @package core_competency 57 * @copyright 2018 Frédéric Massart 58 * @author Frédéric Massart <fred@branchup.tech> 59 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 60 */ 61 class provider implements 62 \core_privacy\local\metadata\provider, 63 \core_privacy\local\request\core_userlist_provider, 64 \core_privacy\local\request\subsystem\provider { 65 66 /** 67 * Returns metadata. 68 * 69 * @param collection $collection The initialised collection to add items to. 70 * @return collection A listing of user data stored through this system. 71 */ 72 public static function get_metadata(collection $collection) : collection { 73 74 // Tables not related to users aside from the editing information. 75 $collection->add_database_table('competency', [ 76 'timecreated' => 'privacy:metadata:timecreated', 77 'timemodified' => 'privacy:metadata:timemodified', 78 'usermodified' => 'privacy:metadata:usermodified', 79 ], 'privacy:metadata:competency'); 80 81 $collection->add_database_table('competency_coursecompsetting', [ 82 'timecreated' => 'privacy:metadata:timecreated', 83 'timemodified' => 'privacy:metadata:timemodified', 84 'usermodified' => 'privacy:metadata:usermodified', 85 ], 'privacy:metadata:competency_coursecompsetting'); 86 87 $collection->add_database_table('competency_framework', [ 88 'timecreated' => 'privacy:metadata:timecreated', 89 'timemodified' => 'privacy:metadata:timemodified', 90 'usermodified' => 'privacy:metadata:usermodified', 91 ], 'privacy:metadata:competency_framework'); 92 93 $collection->add_database_table('competency_coursecomp', [ 94 'timecreated' => 'privacy:metadata:timecreated', 95 'timemodified' => 'privacy:metadata:timemodified', 96 'usermodified' => 'privacy:metadata:usermodified', 97 ], 'privacy:metadata:competency_coursecomp'); 98 99 $collection->add_database_table('competency_template', [ 100 'timecreated' => 'privacy:metadata:timecreated', 101 'timemodified' => 'privacy:metadata:timemodified', 102 'usermodified' => 'privacy:metadata:usermodified', 103 ], 'privacy:metadata:competency_template'); 104 105 $collection->add_database_table('competency_templatecomp', [ 106 'timecreated' => 'privacy:metadata:timecreated', 107 'timemodified' => 'privacy:metadata:timemodified', 108 'usermodified' => 'privacy:metadata:usermodified', 109 ], 'privacy:metadata:competency_templatecomp'); 110 111 $collection->add_database_table('competency_templatecohort', [ 112 'timecreated' => 'privacy:metadata:timecreated', 113 'timemodified' => 'privacy:metadata:timemodified', 114 'usermodified' => 'privacy:metadata:usermodified', 115 ], 'privacy:metadata:competency_templatecohort'); 116 117 $collection->add_database_table('competency_relatedcomp', [ 118 'timecreated' => 'privacy:metadata:timecreated', 119 'timemodified' => 'privacy:metadata:timemodified', 120 'usermodified' => 'privacy:metadata:usermodified', 121 ], 'privacy:metadata:competency_relatedcomp'); 122 123 $collection->add_database_table('competency_modulecomp', [ 124 'timecreated' => 'privacy:metadata:timecreated', 125 'timemodified' => 'privacy:metadata:timemodified', 126 'usermodified' => 'privacy:metadata:usermodified', 127 ], 'privacy:metadata:competency_modulecomp'); 128 129 // Tables containing user data. 130 $collection->add_database_table('competency_plan', [ 131 'name' => 'privacy:metadata:plan:name', 132 'description' => 'privacy:metadata:plan:description', 133 'userid' => 'privacy:metadata:plan:userid', 134 'status' => 'privacy:metadata:plan:status', 135 'duedate' => 'privacy:metadata:plan:duedate', 136 'reviewerid' => 'privacy:metadata:plan:reviewerid', 137 'timecreated' => 'privacy:metadata:timecreated', 138 'timemodified' => 'privacy:metadata:timemodified', 139 'usermodified' => 'privacy:metadata:usermodified', 140 ], 'privacy:metadata:competency_plan'); 141 142 $collection->add_database_table('competency_usercomp', [ 143 'userid' => 'privacy:metadata:usercomp:userid', 144 'status' => 'privacy:metadata:usercomp:status', 145 'reviewerid' => 'privacy:metadata:usercomp:reviewerid', 146 'proficiency' => 'privacy:metadata:usercomp:proficiency', 147 'grade' => 'privacy:metadata:usercomp:grade', 148 'timecreated' => 'privacy:metadata:timecreated', 149 'timemodified' => 'privacy:metadata:timemodified', 150 'usermodified' => 'privacy:metadata:usermodified', 151 ], 'privacy:metadata:competency_usercomp'); 152 153 $collection->add_database_table('competency_usercompcourse', [ 154 'userid' => 'privacy:metadata:usercomp:userid', 155 'proficiency' => 'privacy:metadata:usercomp:proficiency', 156 'grade' => 'privacy:metadata:usercomp:grade', 157 'timecreated' => 'privacy:metadata:timecreated', 158 'timemodified' => 'privacy:metadata:timemodified', 159 'usermodified' => 'privacy:metadata:usermodified', 160 ], 'privacy:metadata:competency_usercompcourse'); 161 162 $collection->add_database_table('competency_usercompplan', [ 163 'userid' => 'privacy:metadata:usercomp:userid', 164 'proficiency' => 'privacy:metadata:usercomp:proficiency', 165 'grade' => 'privacy:metadata:usercomp:grade', 166 'timecreated' => 'privacy:metadata:timecreated', 167 'timemodified' => 'privacy:metadata:timemodified', 168 'usermodified' => 'privacy:metadata:usermodified', 169 ], 'privacy:metadata:competency_usercompplan'); 170 171 $collection->add_database_table('competency_plancomp', [ 172 'timecreated' => 'privacy:metadata:timecreated', 173 'timemodified' => 'privacy:metadata:timemodified', 174 'usermodified' => 'privacy:metadata:usermodified', 175 ], 'privacy:metadata:competency_plancomp'); 176 177 $collection->add_database_table('competency_evidence', [ 178 'action' => 'privacy:metadata:evidence:action', 179 'actionuserid' => 'privacy:metadata:evidence:actionuserid', 180 'descidentifier' => 'privacy:metadata:evidence:descidentifier', 181 'desccomponent' => 'privacy:metadata:evidence:desccomponent', 182 'desca' => 'privacy:metadata:evidence:desca', 183 'url' => 'privacy:metadata:evidence:url', 184 'grade' => 'privacy:metadata:evidence:grade', 185 'note' => 'privacy:metadata:evidence:note', 186 'timecreated' => 'privacy:metadata:timecreated', 187 'timemodified' => 'privacy:metadata:timemodified', 188 'usermodified' => 'privacy:metadata:usermodified', 189 ], 'privacy:metadata:competency_evidence'); 190 191 $collection->add_database_table('competency_userevidence', [ 192 'name' => 'privacy:metadata:userevidence:name', 193 'description' => 'privacy:metadata:userevidence:description', 194 'url' => 'privacy:metadata:userevidence:url', 195 'timecreated' => 'privacy:metadata:timecreated', 196 'timemodified' => 'privacy:metadata:timemodified', 197 'usermodified' => 'privacy:metadata:usermodified', 198 ], 'privacy:metadata:competency_userevidence'); 199 200 $collection->add_database_table('competency_userevidencecomp', [ 201 'timecreated' => 'privacy:metadata:timecreated', 202 'timemodified' => 'privacy:metadata:timemodified', 203 'usermodified' => 'privacy:metadata:usermodified', 204 ], 'privacy:metadata:competency_userevidencecomp'); 205 206 // Comments can be left on learning plans and competencies. 207 $collection->link_subsystem('core_comment', 'privacy:metadata:core_comments'); 208 209 return $collection; 210 } 211 212 /** 213 * Get the list of contexts that contain user information for the specified user. 214 * 215 * @param int $userid The user to search. 216 * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. 217 */ 218 public static function get_contexts_for_userid(int $userid) : contextlist { 219 global $DB; 220 $contextlist = new \core_privacy\local\request\contextlist(); 221 222 // Find the contexts of the frameworks, and related data, modified by the user. 223 $sql = " 224 SELECT DISTINCT ctx.id 225 FROM {context} ctx 226 JOIN {" . competency_framework::TABLE . "} cf 227 ON cf.contextid = ctx.id 228 LEFT JOIN {" . competency::TABLE . "} c 229 ON c.competencyframeworkid = cf.id 230 LEFT JOIN {" . related_competency::TABLE . "} cr 231 ON cr.competencyid = c.id 232 WHERE cf.usermodified = :userid1 233 OR c.usermodified = :userid2 234 OR cr.usermodified = :userid3"; 235 $params = ['userid1' => $userid, 'userid2' => $userid, 'userid3' => $userid]; 236 $contextlist->add_from_sql($sql, $params); 237 238 // Find the contexts of the templates, and related data, modified by the user. 239 $sql = " 240 SELECT DISTINCT ctx.id 241 FROM {context} ctx 242 JOIN {" . template::TABLE . "} tpl 243 ON tpl.contextid = ctx.id 244 LEFT JOIN {" . template_cohort::TABLE . "} tch 245 ON tch.templateid = tpl.id 246 AND tch.usermodified = :userid2 247 LEFT JOIN {" . template_competency::TABLE . "} tc 248 ON tc.templateid = tpl.id 249 AND tc.usermodified = :userid3 250 WHERE tpl.usermodified = :userid1 251 OR tch.id IS NOT NULL 252 OR tc.id IS NOT NULL"; 253 $params = ['userid1' => $userid, 'userid2' => $userid, 'userid3' => $userid]; 254 $contextlist->add_from_sql($sql, $params); 255 256 // Find the possible course contexts. 257 $sql = " 258 SELECT DISTINCT ctx.id 259 FROM {" . course_competency::TABLE . "} cc 260 JOIN {context} ctx 261 ON ctx.instanceid = cc.courseid 262 AND ctx.contextlevel = :courselevel 263 WHERE cc.usermodified = :userid"; 264 $params = ['courselevel' => CONTEXT_COURSE, 'userid' => $userid]; 265 $contextlist->add_from_sql($sql, $params); 266 267 $sql = " 268 SELECT DISTINCT ctx.id 269 FROM {" . course_competency_settings::TABLE . "} ccs 270 JOIN {context} ctx 271 ON ctx.instanceid = ccs.courseid 272 AND ctx.contextlevel = :courselevel 273 WHERE ccs.usermodified = :userid"; 274 $params = ['courselevel' => CONTEXT_COURSE, 'userid' => $userid]; 275 $contextlist->add_from_sql($sql, $params); 276 277 $sql = " 278 SELECT DISTINCT ctx.id 279 FROM {" . user_competency_course::TABLE . "} ucc 280 JOIN {context} ctx 281 ON ctx.instanceid = ucc.courseid 282 AND ctx.contextlevel = :courselevel 283 WHERE ucc.usermodified = :userid"; 284 $params = ['courselevel' => CONTEXT_COURSE, 'userid' => $userid]; 285 $contextlist->add_from_sql($sql, $params); 286 287 // Find the possible module contexts. 288 $sql = " 289 SELECT DISTINCT ctx.id 290 FROM {" . course_module_competency::TABLE . "} cmc 291 JOIN {context} ctx 292 ON ctx.instanceid = cmc.cmid 293 AND ctx.contextlevel = :modulelevel 294 WHERE cmc.usermodified = :userid"; 295 $params = ['modulelevel' => CONTEXT_MODULE, 'userid' => $userid]; 296 $contextlist->add_from_sql($sql, $params); 297 298 // Add user contexts through usermodified/reviewing of plan related data. 299 $sql = " 300 SELECT DISTINCT ctx.id 301 FROM {" . plan::TABLE . "} p 302 JOIN {context} ctx 303 ON ctx.instanceid = p.userid 304 AND ctx.contextlevel = :userlevel 305 LEFT JOIN {" . plan_competency::TABLE . "} pc 306 ON pc.planid = p.id 307 AND pc.usermodified = :userid3 308 LEFT JOIN {" . user_competency_plan::TABLE . "} upc 309 ON upc.planid = p.id 310 AND upc.usermodified = :userid4 311 WHERE p.usermodified = :userid1 312 OR p.reviewerid = :userid2 313 OR pc.id IS NOT NULL 314 OR upc.id IS NOT NULL"; 315 $params = [ 316 'userlevel' => CONTEXT_USER, 317 'userid1' => $userid, 318 'userid2' => $userid, 319 'userid3' => $userid, 320 'userid4' => $userid, 321 ]; 322 $contextlist->add_from_sql($sql, $params); 323 324 // Add user contexts through usermodified/reviewing of competency data. 325 $sql = " 326 SELECT DISTINCT ctx.id 327 FROM {context} ctx 328 LEFT JOIN {" . user_competency::TABLE . "} uc 329 ON uc.userid = ctx.instanceid 330 AND ctx.contextlevel = :userlevel1 331 LEFT JOIN {" . evidence::TABLE . "} e 332 ON e.usercompetencyid = uc.id 333 AND (e.usermodified = :userid3 OR e.actionuserid = :userid4) 334 LEFT JOIN {" . user_evidence::TABLE . "} ue 335 ON ue.userid = ctx.instanceid 336 AND ctx.contextlevel = :userlevel2 337 AND ue.usermodified = :userid5 338 LEFT JOIN {" . user_evidence_competency::TABLE . "} uec 339 ON uec.userevidenceid = ue.id 340 AND uec.usermodified = :userid6 341 WHERE uc.usermodified = :userid1 342 OR uc.reviewerid = :userid2 343 OR e.id IS NOT NULL 344 OR ue.id IS NOT NULL 345 OR uec.id IS NOT NULL"; 346 $params = [ 347 'userlevel1' => CONTEXT_USER, 348 'userlevel2' => CONTEXT_USER, 349 'userid1' => $userid, 350 'userid2' => $userid, 351 'userid3' => $userid, 352 'userid4' => $userid, 353 'userid5' => $userid, 354 'userid6' => $userid, 355 ]; 356 $contextlist->add_from_sql($sql, $params); 357 358 // Now, the easy part, we fetch the user context for user plans and competencies. 359 // We also fetch the course context for the state of competencies for the user in courses. 360 $sql = " 361 SELECT DISTINCT ctx.id 362 FROM {context} ctx 363 LEFT JOIN {" . plan::TABLE . "} p 364 ON p.userid = ctx.instanceid 365 AND ctx.contextlevel = :userlevel1 366 LEFT JOIN {" . user_competency::TABLE . "} uc 367 ON uc.userid = ctx.instanceid 368 AND ctx.contextlevel = :userlevel2 369 AND uc.userid = :userid2 370 LEFT JOIN {" . user_evidence::TABLE . "} ue 371 ON ue.userid = ctx.instanceid 372 AND ctx.contextlevel = :userlevel3 373 AND ue.userid = :userid3 374 LEFT JOIN {" . user_competency_course::TABLE . "} ucc 375 ON ucc.courseid = ctx.instanceid 376 AND ctx.contextlevel = :courselevel 377 AND ucc.userid = :userid4 378 WHERE p.userid = :userid1 379 OR uc.id IS NOT NULL 380 OR ue.id IS NOT NULL 381 OR ucc.id IS NOT NULL"; 382 $params = [ 383 'userlevel1' => CONTEXT_USER, 384 'userlevel2' => CONTEXT_USER, 385 'userlevel3' => CONTEXT_USER, 386 'courselevel' => CONTEXT_COURSE, 387 'userid1' => $userid, 388 'userid2' => $userid, 389 'userid3' => $userid, 390 'userid4' => $userid, 391 ]; 392 $contextlist->add_from_sql($sql, $params); 393 394 // Include the user contexts in which the user commented. 395 $sql = " 396 SELECT ctx.id 397 FROM {context} ctx 398 JOIN {comments} c 399 ON c.contextid = ctx.id 400 WHERE c.component = :component 401 AND c.commentarea IN (:planarea, :usercomparea) 402 AND c.userid = :userid"; 403 $params = [ 404 'component' => 'competency', // Must not be core_competency. 405 'planarea' => 'plan', 406 'usercomparea' => 'user_competency', 407 'userid' => $userid 408 ]; 409 $contextlist->add_from_sql($sql, $params); 410 411 return $contextlist; 412 } 413 414 /** 415 * Get the list of users who have data within a context. 416 * 417 * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. 418 */ 419 public static function get_users_in_context(userlist $userlist) { 420 $context = $userlist->get_context(); 421 $params = ['contextid' => $context->id]; 422 423 // Add users who have modified the frameworks and related data in the context. 424 $sql = " 425 SELECT cf.usermodified 426 FROM {" . competency_framework::TABLE . "} cf 427 WHERE cf.contextid = :contextid"; 428 $userlist->add_from_sql('usermodified', $sql, $params); 429 430 $sql = " 431 SELECT c.usermodified 432 FROM {" . competency_framework::TABLE . "} cf 433 JOIN {" . competency::TABLE . "} c 434 ON c.competencyframeworkid = cf.id 435 WHERE cf.contextid = :contextid"; 436 $userlist->add_from_sql('usermodified', $sql, $params); 437 438 $sql = " 439 SELECT cr.usermodified 440 FROM {" . competency_framework::TABLE . "} cf 441 JOIN {" . competency::TABLE . "} c 442 ON c.competencyframeworkid = cf.id 443 JOIN {" . related_competency::TABLE . "} cr 444 ON cr.competencyid = c.id 445 WHERE cf.contextid = :contextid"; 446 $userlist->add_from_sql('usermodified', $sql, $params); 447 448 // Add users who have modified the templates and related data in the context. 449 $sql = " 450 SELECT tpl.usermodified 451 FROM {" . template::TABLE . "} tpl 452 WHERE tpl.contextid = :contextid"; 453 $userlist->add_from_sql('usermodified', $sql, $params); 454 455 $sql = " 456 SELECT tch.usermodified 457 FROM {" . template::TABLE . "} tpl 458 JOIN {" . template_cohort::TABLE . "} tch 459 ON tch.templateid = tpl.id 460 WHERE tpl.contextid = :contextid"; 461 $userlist->add_from_sql('usermodified', $sql, $params); 462 463 $sql = " 464 SELECT tc.usermodified 465 FROM {" . template::TABLE . "} tpl 466 JOIN {" . template_competency::TABLE . "} tc 467 ON tc.templateid = tpl.id 468 WHERE tpl.contextid = :contextid"; 469 $userlist->add_from_sql('usermodified', $sql, $params); 470 471 // Add users if userlist is in course context. 472 if (is_a($context, \context_course::class)) { 473 $params = ['courseid' => $context->instanceid]; 474 475 $sql = " 476 SELECT cc.usermodified 477 FROM {" . course_competency::TABLE . "} cc 478 WHERE cc.courseid = :courseid"; 479 $userlist->add_from_sql('usermodified', $sql, $params); 480 481 $sql = " 482 SELECT ccs.usermodified 483 FROM {" . course_competency_settings::TABLE . "} ccs 484 WHERE ccs.courseid = :courseid"; 485 $userlist->add_from_sql('usermodified', $sql, $params); 486 487 $sql = " 488 SELECT ucc.userid, ucc.usermodified 489 FROM {" . user_competency_course::TABLE . "} ucc 490 WHERE ucc.courseid = :courseid"; 491 $userlist->add_from_sql('userid', $sql, $params); 492 $userlist->add_from_sql('usermodified', $sql, $params); 493 494 } else if (is_a($context, \context_module::class)) { 495 // Add users if userlist is in module context. 496 $params = ['moduleid' => $context->instanceid]; 497 498 $sql = " 499 SELECT cmc.usermodified 500 FROM {" . course_module_competency::TABLE . "} cmc 501 WHERE cmc.cmid = :moduleid"; 502 $userlist->add_from_sql('usermodified', $sql, $params); 503 504 } else if (is_a($context, \context_user::class)) { 505 $params = ['userid' => $context->instanceid]; 506 507 // Add users through plan related data. 508 $sql = " 509 SELECT p.userid, p.usermodified, p.reviewerid 510 FROM {" . plan::TABLE . "} p 511 WHERE p.userid = :userid"; 512 $userlist->add_from_sql('userid', $sql, $params); 513 $userlist->add_from_sql('usermodified', $sql, $params); 514 $userlist->add_from_sql('reviewerid', $sql, $params); 515 516 $sql = " 517 SELECT pc.usermodified 518 FROM {" . plan::TABLE . "} p 519 JOIN {" . plan_competency::TABLE . "} pc 520 ON pc.planid = p.id 521 WHERE p.userid = :userid"; 522 $userlist->add_from_sql('usermodified', $sql, $params); 523 524 $sql = " 525 SELECT upc.usermodified 526 FROM {" . user_competency_plan::TABLE . "} upc 527 WHERE upc.userid = :userid"; 528 $userlist->add_from_sql('usermodified', $sql, $params); 529 530 // Add users through competency data. 531 $sql = " 532 SELECT uc.userid, uc.usermodified, uc.reviewerid 533 FROM {" . user_competency::TABLE . "} uc 534 WHERE uc.userid = :userid"; 535 $userlist->add_from_sql('userid', $sql, $params); 536 $userlist->add_from_sql('usermodified', $sql, $params); 537 $userlist->add_from_sql('reviewerid', $sql, $params); 538 539 $sql = " 540 SELECT e.usermodified, e.actionuserid 541 FROM {" . user_competency::TABLE . "} uc 542 JOIN {" . evidence::TABLE . "} e 543 ON e.usercompetencyid = uc.id 544 WHERE uc.userid = :userid"; 545 $userlist->add_from_sql('usermodified', $sql, $params); 546 $userlist->add_from_sql('actionuserid', $sql, $params); 547 548 // Add users through evidence data. 549 $sql = " 550 SELECT ue.userid, ue.usermodified 551 FROM {" . user_evidence::TABLE . "} ue 552 WHERE ue.userid = :userid"; 553 $userlist->add_from_sql('userid', $sql, $params); 554 $userlist->add_from_sql('usermodified', $sql, $params); 555 556 $sql = " 557 SELECT ue.usermodified 558 FROM {" . user_evidence::TABLE . "} ue 559 JOIN {" . user_evidence_competency::TABLE . "} uec 560 ON uec.userevidenceid = ue.id 561 WHERE ue.userid = :userid"; 562 $userlist->add_from_sql('usermodified', $sql, $params); 563 } 564 565 // Add users who commented in the context. 566 // Note: Comment component must be competency and not core_competency. 567 \core_comment\privacy\provider::get_users_in_context_from_sql( 568 $userlist, 'com', 'competency', 'plan', $context->id); 569 570 \core_comment\privacy\provider::get_users_in_context_from_sql( 571 $userlist, 'com', 'competency', 'user_competency', $context->id); 572 } 573 574 /** 575 * Export all user data for the specified user, in the specified contexts. 576 * 577 * We skip the enabled check for competencies, as there could be historical data. This avoids exceptions thrown from 578 * the {@see api::require_enabled} method, which is called at various points during export via the competency API 579 * 580 * @param approved_contextlist $contextlist The approved contexts to export information for. 581 */ 582 public static function export_user_data(approved_contextlist $contextlist) { 583 584 // Export even if competencies are not currently enabled. 585 api::skip_enabled(); 586 587 $user = $contextlist->get_user(); 588 $userid = $user->id; 589 590 // Re-arrange the contexts by context level. 591 $groupedcontexts = array_reduce($contextlist->get_contexts(), function($carry, $context) { 592 $contextlevel = $context->contextlevel; 593 if (!in_array($contextlevel, [CONTEXT_USER, CONTEXT_COURSE, CONTEXT_MODULE, CONTEXT_SYSTEM, CONTEXT_COURSECAT])) { 594 return $carry; 595 } 596 $carry[$contextlevel][] = $context; 597 return $carry; 598 }, [ 599 CONTEXT_COURSE => [], 600 CONTEXT_COURSECAT => [], 601 CONTEXT_MODULE => [], 602 CONTEXT_SYSTEM => [], 603 CONTEXT_USER => [], 604 ]); 605 606 // Process module contexts. 607 static::export_user_data_in_module_contexts($userid, $groupedcontexts[CONTEXT_MODULE]); 608 609 // Process course contexts. 610 static::export_user_data_in_course_contexts($userid, $groupedcontexts[CONTEXT_COURSE]); 611 612 // Process course categories context. 613 static::export_user_data_in_category_contexts($userid, $groupedcontexts[CONTEXT_COURSECAT]); 614 615 // Process system context. 616 if (!empty($groupedcontexts[CONTEXT_SYSTEM])) { 617 static::export_user_data_in_system_context($userid); 618 } 619 620 // Process user contexts. 621 static::export_user_data_in_user_contexts($userid, $groupedcontexts[CONTEXT_USER]); 622 } 623 624 /** 625 * Delete all data for all users in the specified context. 626 * 627 * @param context $context The specific context to delete data for. 628 */ 629 public static function delete_data_for_all_users_in_context(context $context) { 630 global $DB; 631 632 switch ($context->contextlevel) { 633 case CONTEXT_USER: 634 $userid = $context->instanceid; 635 static::delete_user_evidence_of_prior_learning($userid); 636 static::delete_user_plans($userid); 637 static::delete_user_competencies($userid); 638 break; 639 640 case CONTEXT_COURSE: 641 static::delete_user_competencies_in_course($context->instanceid); 642 break; 643 } 644 } 645 646 /** 647 * Delete all user data for the specified user, in the specified contexts. 648 * 649 * Here we only delete the private data of user, not whether they modified, are reviewing, 650 * or are associated with the record on at a second level. Only data directly linked to the 651 * user will be affected. 652 * 653 * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. 654 */ 655 public static function delete_data_for_user(approved_contextlist $contextlist) { 656 $user = $contextlist->get_user(); 657 $userid = $user->id; 658 659 foreach ($contextlist as $context) { 660 switch ($context->contextlevel) { 661 case CONTEXT_USER: 662 if ($context->instanceid == $userid) { 663 // Only delete the user's information when they requested their context to be deleted. We 664 // do not take any action on other user's contexts because we don't own the data there. 665 static::delete_user_evidence_of_prior_learning($userid); 666 static::delete_user_plans($userid); 667 static::delete_user_competencies($userid); 668 } 669 break; 670 671 case CONTEXT_COURSE: 672 static::delete_user_competencies_in_course($context->instanceid, [$userid]); 673 break; 674 } 675 } 676 } 677 678 /** 679 * Delete multiple users within a single context. 680 * 681 * Here we only delete the private data of users, not whether they modified, are reviewing, 682 * or are associated with the record on at a second level. Only data directly linked to the 683 * user will be affected. 684 * 685 * @param approved_userlist $userlist The approved context and user information to delete information for. 686 */ 687 public static function delete_data_for_users(approved_userlist $userlist) { 688 $context = $userlist->get_context(); 689 $userids = $userlist->get_userids(); 690 691 switch ($context->contextlevel) { 692 case CONTEXT_USER: 693 // Only delete the user's information when their context is being deleted. 694 // We do not take any action on other user's contexts because we don't own the data there. 695 if (in_array($context->instanceid, $userids)) { 696 static::delete_user_evidence_of_prior_learning($context->instanceid); 697 static::delete_user_plans($context->instanceid); 698 static::delete_user_competencies($context->instanceid); 699 } 700 701 break; 702 703 case CONTEXT_COURSE: 704 static::delete_user_competencies_in_course($context->instanceid, $userids); 705 break; 706 } 707 } 708 709 /** 710 * Delete evidence of prior learning. 711 * 712 * @param int $userid The user ID. 713 * @return void 714 */ 715 protected static function delete_user_evidence_of_prior_learning($userid) { 716 global $DB; 717 718 $usercontext = context_user::instance($userid); 719 $ueids = $DB->get_fieldset_select(user_evidence::TABLE, 'id', 'userid = :userid', ['userid' => $userid]); 720 if (empty($ueids)) { 721 return; 722 } 723 list($insql, $inparams) = $DB->get_in_or_equal($ueids, SQL_PARAMS_NAMED); 724 725 // Delete competencies associated with user evidence. 726 $DB->delete_records_select(user_evidence_competency::TABLE, "userevidenceid $insql", $inparams); 727 728 // Delete the user evidence. 729 $DB->delete_records_select(user_evidence::TABLE, "id $insql", $inparams); 730 731 // Delete the user evidence files. 732 $fs = get_file_storage(); 733 $fs->delete_area_files($usercontext->id, 'core_competency', 'userevidence'); 734 } 735 736 /** 737 * User plans. 738 * 739 * @param int $userid The user ID. 740 * @return void 741 */ 742 protected static function delete_user_plans($userid) { 743 global $DB; 744 $usercontext = context_user::instance($userid); 745 746 // Remove all the comments made on plans. 747 \core_comment\privacy\provider::delete_comments_for_all_users($usercontext, 'competency', 'plan'); 748 749 // Find the user plan IDs. 750 $planids = $DB->get_fieldset_select(plan::TABLE, 'id', 'userid = :userid', ['userid' => $userid]); 751 if (empty($planids)) { 752 return; 753 } 754 list($insql, $inparams) = $DB->get_in_or_equal($planids, SQL_PARAMS_NAMED); 755 756 // Delete all the competencies proficiency in the plans. 757 $DB->delete_records_select(user_competency_plan::TABLE, "planid $insql", $inparams); 758 759 // Delete all the competencies in the plans. 760 $DB->delete_records_select(plan_competency::TABLE, "planid $insql", $inparams); 761 762 // Delete all the plans. 763 $DB->delete_records_select(plan::TABLE, "id $insql", $inparams); 764 } 765 766 /** 767 * Delete user competency data. 768 * 769 * @param int $userid The user ID. 770 * @return void 771 */ 772 protected static function delete_user_competencies($userid) { 773 global $DB; 774 $usercontext = context_user::instance($userid); 775 776 // Remove all the comments made on user competencies. 777 \core_comment\privacy\provider::delete_comments_for_all_users($usercontext, 'competency', 'user_competency'); 778 779 // Find the user competency IDs. 780 $ucids = $DB->get_fieldset_select(user_competency::TABLE, 'id', 'userid = :userid', ['userid' => $userid]); 781 if (empty($ucids)) { 782 return; 783 } 784 list($insql, $inparams) = $DB->get_in_or_equal($ucids, SQL_PARAMS_NAMED); 785 786 // Delete all the evidence associated with competencies. 787 $DB->delete_records_select(evidence::TABLE, "usercompetencyid $insql", $inparams); 788 789 // Delete all the record of competency. 790 $DB->delete_records_select(user_competency::TABLE, "id $insql", $inparams); 791 } 792 793 /** 794 * Delete the record of competencies for user(s) in a course. 795 * 796 * @param int $courseid The course ID. 797 * @param int[] $userids The user IDs, if deleting for specific user(s). 798 * @return void 799 */ 800 protected static function delete_user_competencies_in_course($courseid, $userids = []) { 801 global $DB; 802 803 $params = ['courseid' => $courseid]; 804 $where = "courseid = :courseid"; 805 806 if (!empty($userids)) { 807 list($insql, $inparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); 808 $params = $params + $inparams; 809 810 $where .= " AND userid {$insql}"; 811 } 812 813 $DB->delete_records_select(user_competency_course::TABLE, $where, $params); 814 } 815 816 /** 817 * Export the user data in user context. 818 * 819 * @param int $userid The user ID. 820 * @param array $contexts The contexts. 821 * @return void 822 */ 823 protected static function export_user_data_in_user_contexts($userid, array $contexts) { 824 global $DB; 825 826 $mycontext = context_user::instance($userid); 827 $contextids = array_map(function($context) { 828 return $context->id; 829 }, $contexts); 830 $exportowncontext = in_array($mycontext->id, $contextids); 831 $othercontexts = array_filter($contextids, function($contextid) use ($mycontext) { 832 return $contextid != $mycontext->id; 833 }); 834 835 if ($exportowncontext) { 836 static::export_user_data_learning_plans($mycontext); 837 static::export_user_data_competencies($mycontext); 838 static::export_user_data_user_evidence($mycontext); 839 } 840 841 foreach ($othercontexts as $contextid) { 842 static::export_user_data_learning_plans_related_to_me($userid, context::instance_by_id($contextid)); 843 static::export_user_data_competencies_related_to_me($userid, context::instance_by_id($contextid)); 844 static::export_user_data_user_evidence_related_to_me($userid, context::instance_by_id($contextid)); 845 } 846 } 847 848 /** 849 * Export the user data in systen context. 850 * 851 * @param int $userid The user ID. 852 * @return void 853 */ 854 protected static function export_user_data_in_system_context($userid) { 855 static::export_user_data_frameworks_in_context($userid, context_system::instance()); 856 static::export_user_data_templates_in_context($userid, context_system::instance()); 857 } 858 859 /** 860 * Export the user data in category contexts. 861 * 862 * @param int $userid The user ID. 863 * @param array $contexts The contexts. 864 * @return void 865 */ 866 protected static function export_user_data_in_category_contexts($userid, array $contexts) { 867 $contexts = array_filter($contexts, function($context) { 868 return $context->contextlevel == CONTEXT_COURSECAT; 869 }); 870 if (empty($contexts)) { 871 return; 872 } 873 874 foreach ($contexts as $context) { 875 static::export_user_data_frameworks_in_context($userid, $context); 876 static::export_user_data_templates_in_context($userid, $context); 877 } 878 } 879 880 /** 881 * Export the user data in course contexts. 882 * 883 * @param int $userid The user whose data we're exporting. 884 * @param array $contexts A list of contexts. 885 * @return void 886 */ 887 protected static function export_user_data_in_course_contexts($userid, array $contexts) { 888 global $DB; 889 890 $contexts = array_filter($contexts, function($context) { 891 return $context->contextlevel == CONTEXT_COURSE; 892 }); 893 if (empty($contexts)) { 894 return; 895 } 896 897 $helper = new performance_helper(); 898 $path = [get_string('competencies', 'core_competency')]; 899 $courseids = array_map(function($context) { 900 return $context->instanceid; 901 }, $contexts); 902 903 // Fetch all the records of competency proficiency in the course. 904 $ffields = competency_framework::get_sql_fields('f', 'f_'); 905 $compfields = competency::get_sql_fields('c', 'c_'); 906 $uccfields = user_competency_course::get_sql_fields('ucc', 'ucc_'); 907 $ctxfields = context_helper::get_preload_record_columns_sql('ctx'); 908 list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); 909 $sql = " 910 SELECT $ffields, $compfields, $uccfields, $ctxfields 911 FROM {" . user_competency_course::TABLE . "} ucc 912 JOIN {" . competency::TABLE . "} c 913 ON c.id = ucc.competencyid 914 JOIN {" . competency_framework::TABLE . "} f 915 ON f.id = c.competencyframeworkid 916 JOIN {context} ctx 917 ON ctx.id = f.contextid 918 WHERE ucc.userid = :userid 919 AND ucc.courseid $insql 920 ORDER BY ucc.courseid, c.id"; 921 $params = array_merge($inparams, ['userid' => $userid]); 922 923 // Export data. 924 $recordset = $DB->get_recordset_sql($sql, $params); 925 static::recordset_loop_and_export($recordset, 'ucc_courseid', [], function($carry, $record) use ($helper) { 926 context_helper::preload_from_record($record); 927 $framework = new competency_framework(null, competency_framework::extract_record($record, 'f_')); 928 $competency = new competency(null, competency::extract_record($record, 'c_')); 929 $ucc = new user_competency_course(null, user_competency_course::extract_record($record, 'ucc_')); 930 $helper->ingest_framework($framework); 931 932 $carry[] = array_merge(static::transform_competency_brief($competency), [ 933 'rating' => [ 934 'rating' => static::transform_competency_grade($competency, $ucc->get('grade'), $helper), 935 'proficient' => static::transform_proficiency($ucc->get('proficiency')), 936 'timecreated' => transform::datetime($ucc->get('timecreated')), 937 'timemodified' => transform::datetime($ucc->get('timemodified')), 938 ] 939 ]); 940 return $carry; 941 942 }, function($courseid, $data) use ($path) { 943 $context = context_course::instance($courseid); 944 writer::with_context($context)->export_data($path, (object) ['ratings' => $data]); 945 }); 946 947 // Export usermodified data. 948 static::export_user_data_in_course_contexts_associations($userid, $courseids, $path); 949 static::export_user_data_in_course_contexts_settings($userid, $courseids, $path); 950 static::export_user_data_in_course_contexts_rated_by_me($userid, $courseids, $path, $helper); 951 } 952 953 /** 954 * Export the ratings given in a course. 955 * 956 * @param int $userid The user ID. 957 * @param array $courseids The course IDs. 958 * @param array $path The root path. 959 * @param performance_helper $helper The performance helper. 960 * @return void 961 */ 962 protected static function export_user_data_in_course_contexts_rated_by_me($userid, $courseids, $path, 963 performance_helper $helper) { 964 global $DB; 965 966 // Fetch all the records of competency proficiency in the course. 967 $ffields = competency_framework::get_sql_fields('f', 'f_'); 968 $compfields = competency::get_sql_fields('c', 'c_'); 969 $uccfields = user_competency_course::get_sql_fields('ucc', 'ucc_'); 970 $ctxfields = context_helper::get_preload_record_columns_sql('ctx'); 971 list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); 972 $sql = " 973 SELECT $ffields, $compfields, $uccfields, $ctxfields 974 FROM {" . user_competency_course::TABLE . "} ucc 975 JOIN {" . competency::TABLE . "} c 976 ON c.id = ucc.competencyid 977 JOIN {" . competency_framework::TABLE . "} f 978 ON f.id = c.competencyframeworkid 979 JOIN {context} ctx 980 ON ctx.id = f.contextid 981 WHERE ucc.usermodified = :userid 982 AND ucc.courseid $insql 983 ORDER BY ucc.courseid, ucc.id"; 984 $params = array_merge($inparams, ['userid' => $userid]); 985 986 // Export the data. 987 static::recordset_loop_and_export($DB->get_recordset_sql($sql, $params), 'ucc_courseid', [], 988 function($carry, $record) use ($helper) { 989 context_helper::preload_from_record($record); 990 991 $framework = new competency_framework(null, competency_framework::extract_record($record, 'f_')); 992 $competency = new competency(null, competency::extract_record($record, 'c_')); 993 $ucc = new user_competency_course(null, user_competency_course::extract_record($record, 'ucc_')); 994 $helper->ingest_framework($framework); 995 996 $carry[] = array_merge(static::transform_competency_brief($competency), [ 997 'rating' => [ 998 'userid' => transform::user($ucc->get('userid')), 999 'rating' => static::transform_competency_grade($competency, $ucc->get('grade'), $helper), 1000 'proficient' => static::transform_proficiency($ucc->get('proficiency')), 1001 'timemodified' => transform::datetime($ucc->get('timemodified')), 1002 ] 1003 ]); 1004 return $carry; 1005 1006 }, function($courseid, $data) use ($path) { 1007 $context = context_course::instance($courseid); 1008 writer::with_context($context)->export_related_data($path, 'rated_by_me', (object) [ 1009 'ratings' => $data 1010 ]); 1011 } 1012 ); 1013 } 1014 1015 /** 1016 * Export user data in course contexts related to linked competencies. 1017 * 1018 * @param int $userid The user ID. 1019 * @param array $courseids The course IDs. 1020 * @param array $path The root path to export at. 1021 * @return void 1022 */ 1023 protected static function export_user_data_in_course_contexts_associations($userid, $courseids, $path) { 1024 global $DB; 1025 1026 // Fetch all the courses with associations we created or modified. 1027 $compfields = competency::get_sql_fields('c', 'c_'); 1028 $ccfields = course_competency::get_sql_fields('cc', 'cc_'); 1029 $ctxfields = context_helper::get_preload_record_columns_sql('ctx'); 1030 list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); 1031 $sql = " 1032 SELECT $compfields, $ccfields, $ctxfields 1033 FROM {" . course_competency::TABLE . "} cc 1034 JOIN {" . competency::TABLE . "} c 1035 ON c.id = cc.competencyid 1036 JOIN {" . competency_framework::TABLE . "} f 1037 ON f.id = c.competencyframeworkid 1038 JOIN {context} ctx 1039 ON ctx.id = f.contextid 1040 WHERE cc.usermodified = :userid 1041 AND cc.courseid $insql 1042 ORDER BY cc.courseid, c.id"; 1043 $params = array_merge($inparams, ['userid' => $userid]); 1044 $recordset = $DB->get_recordset_sql($sql, $params); 1045 1046 // Export the data. 1047 static::recordset_loop_and_export($recordset, 'cc_courseid', [], function($carry, $record) { 1048 context_helper::preload_from_record($record); 1049 $competency = new competency(null, competency::extract_record($record, 'c_')); 1050 $cc = new course_competency(null, course_competency::extract_record($record, 'cc_')); 1051 $carry[] = array_merge(static::transform_competency_brief($competency), [ 1052 'timemodified' => transform::datetime($cc->get('timemodified')), 1053 'created_or_modified_by_you' => transform::yesno(true) 1054 ]); 1055 return $carry; 1056 1057 }, function($courseid, $data) use ($path, $userid, $DB) { 1058 $context = context_course::instance($courseid); 1059 writer::with_context($context)->export_related_data($path, 'associations', (object) ['competencies' => $data]); 1060 }); 1061 } 1062 1063 /** 1064 * Export user data in course contexts related to course settings. 1065 * 1066 * @param int $userid The user ID. 1067 * @param array $courseids The course IDs. 1068 * @param array $path The root path to export at. 1069 * @return void 1070 */ 1071 protected static function export_user_data_in_course_contexts_settings($userid, $courseids, $path) { 1072 global $DB; 1073 1074 // Fetch all the courses with associations we created or modified. 1075 $ccsfields = course_competency_settings::get_sql_fields('ccs', 'ccs_'); 1076 list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); 1077 $sql = " 1078 SELECT $ccsfields 1079 FROM {" . course_competency_settings::TABLE . "} ccs 1080 WHERE ccs.usermodified = :userid 1081 AND ccs.courseid $insql 1082 ORDER BY ccs.courseid"; 1083 $params = array_merge($inparams, ['userid' => $userid]); 1084 $recordset = $DB->get_recordset_sql($sql, $params); 1085 1086 // Export the data. 1087 static::recordset_loop_and_export($recordset, 'ccs_courseid', [], function($carry, $record) { 1088 $ccs = new course_competency_settings(null, course_competency_settings::extract_record($record, 'ccs_')); 1089 return [ 1090 'timemodified' => transform::datetime($ccs->get('timemodified')), 1091 'created_or_modified_by_you' => transform::yesno(true) 1092 ]; 1093 }, function($courseid, $data) use ($path, $userid, $DB) { 1094 $context = context_course::instance($courseid); 1095 writer::with_context($context)->export_related_data($path, 'settings', (object) $data); 1096 }); 1097 } 1098 1099 /** 1100 * Export the user data in module contexts. 1101 * 1102 * @param int $userid The user whose data we're exporting. 1103 * @param array $contexts A list of contexts. 1104 * @return void 1105 */ 1106 protected static function export_user_data_in_module_contexts($userid, array $contexts) { 1107 global $DB; 1108 1109 $contexts = array_filter($contexts, function($context) { 1110 return $context->contextlevel == CONTEXT_MODULE; 1111 }); 1112 if (empty($contexts)) { 1113 return; 1114 } 1115 1116 $path = [get_string('competencies', 'core_competency')]; 1117 $cmids = array_map(function($context) { 1118 return $context->instanceid; 1119 }, $contexts); 1120 1121 // Fetch all the modules with associations we created or modified. 1122 $compfields = competency::get_sql_fields('c', 'c_'); 1123 $cmcfields = course_module_competency::get_sql_fields('cmc', 'cmc_'); 1124 $ctxfields = context_helper::get_preload_record_columns_sql('ctx'); 1125 list($insql, $inparams) = $DB->get_in_or_equal($cmids, SQL_PARAMS_NAMED); 1126 $sql = " 1127 SELECT $compfields, $cmcfields, $ctxfields 1128 FROM {" . course_module_competency::TABLE . "} cmc 1129 JOIN {" . competency::TABLE . "} c 1130 ON c.id = cmc.competencyid 1131 JOIN {" . competency_framework::TABLE . "} f 1132 ON f.id = c.competencyframeworkid 1133 JOIN {context} ctx 1134 ON ctx.id = f.contextid 1135 WHERE cmc.usermodified = :userid 1136 AND cmc.cmid $insql 1137 ORDER BY cmc.cmid"; 1138 $params = array_merge($inparams, ['userid' => $userid]); 1139 1140 // Export the data. 1141 $recordset = $DB->get_recordset_sql($sql, $params); 1142 static::recordset_loop_and_export($recordset, 'cmc_cmid', [], function($carry, $record) { 1143 context_helper::preload_from_record($record); 1144 $competency = new competency(null, competency::extract_record($record, 'c_')); 1145 $cmc = new course_module_competency(null, course_module_competency::extract_record($record, 'cmc_')); 1146 $carry[] = array_merge(static::transform_competency_brief($competency), [ 1147 'timecreated' => transform::datetime($cmc->get('timecreated')), 1148 'timemodified' => transform::datetime($cmc->get('timemodified')), 1149 'created_or_modified_by_you' => transform::yesno(true) 1150 ]); 1151 return $carry; 1152 1153 }, function($cmid, $data) use ($path) { 1154 $context = context_module::instance($cmid); 1155 writer::with_context($context)->export_data($path, (object) ['associations' => $data]); 1156 }); 1157 } 1158 1159 /** 1160 * Export a user's competencies. 1161 * 1162 * @param context_user $context The context of the user requesting the export. 1163 * @return void 1164 */ 1165 protected static function export_user_data_competencies(context_user $context) { 1166 global $DB; 1167 1168 $userid = $context->instanceid; 1169 $path = [get_string('competencies', 'core_competency'), get_string('competencies', 'core_competency')]; 1170 $helper = new performance_helper(); 1171 $cfields = competency::get_sql_fields('c', 'c_'); 1172 $ucfields = user_competency::get_sql_fields('uc', 'uc_'); 1173 $efields = evidence::get_sql_fields('e', 'e_'); 1174 1175 $makecomppath = function($competencyid, $data) use ($path) { 1176 return array_merge($path, [$data['name'] . ' (' . $competencyid . ')']); 1177 }; 1178 1179 $sql = " 1180 SELECT $cfields, $ucfields, $efields 1181 FROM {" . user_competency::TABLE . "} uc 1182 JOIN {" . competency::TABLE . "} c 1183 ON c.id = uc.competencyid 1184 LEFT JOIN {" . evidence::TABLE . "} e 1185 ON uc.id = e.usercompetencyid 1186 WHERE uc.userid = :userid 1187 ORDER BY c.id, e.timecreated DESC, e.id DESC"; 1188 $params = ['userid' => $userid]; 1189 1190 $recordset = $DB->get_recordset_sql($sql, $params); 1191 static::recordset_loop_and_export($recordset, 'c_id', null, function($carry, $record) 1192 use ($context, $userid, $helper, $makecomppath) { 1193 1194 $competency = new competency(null, competency::extract_record($record, 'c_')); 1195 1196 if ($carry === null) { 1197 $uc = new user_competency(null, user_competency::extract_record($record, 'uc_')); 1198 $carry = array_merge(static::transform_competency_brief($competency), [ 1199 'rating' => static::transform_user_competency($userid, $uc, $competency, $helper), 1200 'evidence' => [] 1201 ]); 1202 \core_comment\privacy\provider::export_comments($context, 'competency', 'user_competency', 1203 $uc->get('id'), $makecomppath($competency->get('id'), $carry), false); 1204 } 1205 1206 // There is an evidence in this record. 1207 if (!empty($record->e_id)) { 1208 $evidence = new evidence(null, evidence::extract_record($record, 'e_')); 1209 $carry['evidence'][] = static::transform_evidence($userid, $evidence, $competency, $helper); 1210 } 1211 1212 return $carry; 1213 1214 }, function($competencyid, $data) use ($makecomppath, $context) { 1215 writer::with_context($context)->export_data($makecomppath($competencyid, $data), (object) $data); 1216 }); 1217 } 1218 1219 /** 1220 * Export a user's learning plans. 1221 * 1222 * @param context_user $context The context of the user requesting the export. 1223 * @return void 1224 */ 1225 protected static function export_user_data_learning_plans(context_user $context) { 1226 global $DB; 1227 1228 $userid = $context->instanceid; 1229 $path = [get_string('competencies', 'core_competency'), get_string('privacy:path:plans', 'core_competency')]; 1230 $helper = new performance_helper(); 1231 $pfields = plan::get_sql_fields('p', 'p_'); 1232 $pcfields = plan_competency::get_sql_fields('pc', 'pc_'); 1233 $cfields = competency::get_sql_fields('c', 'c_'); 1234 $ucfields = user_competency::get_sql_fields('uc', 'uc_'); 1235 $ucpfields = user_competency_plan::get_sql_fields('ucp', 'ucp_'); 1236 1237 // The user's learning plans. 1238 $sql = " 1239 SELECT $pfields, $pcfields, $cfields, $ucfields, $ucpfields 1240 FROM {" . plan::TABLE . "} p 1241 LEFT JOIN {" . plan_competency::TABLE . "} pc 1242 ON p.id = pc.planid 1243 AND p.templateid IS NULL 1244 AND p.status != :complete1 1245 LEFT JOIN {" . template_competency::TABLE . "} tc 1246 ON tc.templateid = p.templateid 1247 AND p.templateid IS NOT NULL 1248 AND p.status != :complete2 1249 LEFT JOIN {" . user_competency_plan::TABLE . "} ucp 1250 ON ucp.planid = p.id 1251 AND p.status = :complete3 1252 LEFT JOIN {" . competency::TABLE . "} c 1253 ON c.id = pc.competencyid 1254 OR c.id = tc.competencyid 1255 OR c.id = ucp.competencyid 1256 LEFT JOIN {" . user_competency::TABLE . "} uc 1257 ON uc.userid = p.userid 1258 AND (uc.competencyid = pc.competencyid OR uc.competencyid = tc.competencyid) 1259 WHERE p.userid = :userid 1260 ORDER BY p.id, c.id"; 1261 $params = [ 1262 'userid' => $userid, 1263 'complete1' => plan::STATUS_COMPLETE, 1264 'complete2' => plan::STATUS_COMPLETE, 1265 'complete3' => plan::STATUS_COMPLETE, 1266 ]; 1267 1268 $recordset = $DB->get_recordset_sql($sql, $params); 1269 static::recordset_loop_and_export($recordset, 'p_id', null, function($carry, $record) use ($userid, $helper, $context) { 1270 $iscomplete = $record->p_status == plan::STATUS_COMPLETE; 1271 1272 if ($carry === null) { 1273 $plan = new plan(null, plan::extract_record($record, 'p_')); 1274 $options = ['context' => $context]; 1275 $carry = [ 1276 'name' => format_string($plan->get('name'), true, $options), 1277 'description' => format_text($plan->get('description'), $plan->get('descriptionformat'), $options), 1278 'status' => $plan->get_statusname(), 1279 'duedate' => $plan->get('duedate') ? transform::datetime($plan->get('duedate')) : '-', 1280 'reviewerid' => $plan->get('reviewerid') ? transform::user($plan->get('reviewerid')) : '-', 1281 'timecreated' => transform::datetime($plan->get('timecreated')), 1282 'timemodified' => transform::datetime($plan->get('timemodified')), 1283 'competencies' => [], 1284 ]; 1285 } 1286 1287 // The plan is empty. 1288 if (empty($record->c_id)) { 1289 return $carry; 1290 } 1291 1292 $competency = new competency(null, competency::extract_record($record, 'c_')); 1293 $rating = null; 1294 1295 if ($iscomplete) { 1296 // When the plan is complete, we should always found the user_competency_plan. 1297 $ucp = new user_competency_plan(null, user_competency_plan::extract_record($record, 'ucp_')); 1298 $rating = static::transform_user_competency($userid, $ucp, $competency, $helper); 1299 1300 } else if (!empty($record->uc_id)) { 1301 // When the plan is complete, there are still records of user_competency but we do not 1302 // export them here, we export them as part of the competencies structure. The reason why 1303 // we try to get the user_competency when the plan is not complete is to give the most accurate 1304 // representation of the plan as possible. 1305 $uc = new user_competency(null, user_competency::extract_record($record, 'uc_')); 1306 $rating = static::transform_user_competency($userid, $uc, $competency, $helper); 1307 } 1308 1309 $carry['competencies'][] = array_merge(static::transform_competency_brief($competency), ['rating' => $rating]); 1310 return $carry; 1311 1312 }, function($planid, $data) use ($context, $path) { 1313 $planpath = array_merge($path, [$data['name'] . ' (' . $planid . ')']); 1314 \core_comment\privacy\provider::export_comments($context, 'competency', 'plan', $planid, $planpath, false); 1315 writer::with_context($context)->export_data($planpath, (object) $data); 1316 }); 1317 } 1318 1319 /** 1320 * Export a user's data related to learning plans. 1321 * 1322 * @param int $userid The user ID we're exporting for. 1323 * @param context_user $context The context of the user in which we're gathering data. 1324 * @return void 1325 */ 1326 protected static function export_user_data_learning_plans_related_to_me($userid, context_user $context) { 1327 global $DB; 1328 1329 $path = [ 1330 get_string('competencies', 'core_competency'), 1331 get_string('privacy:path:relatedtome', 'core_competency'), 1332 get_string('privacy:path:plans', 'core_competency'), 1333 ]; 1334 $plans = []; 1335 $helper = new performance_helper(); 1336 $pfields = plan::get_sql_fields('p', 'p_'); 1337 $pcfields = plan_competency::get_sql_fields('pc', 'pc_'); 1338 $cfields = competency::get_sql_fields('c', 'c_'); 1339 $ucpfields = user_competency_plan::get_sql_fields('ucp', 'ucp_'); 1340 1341 // Function to initialise a plan record. 1342 $initplan = function($record) use ($context, $userid, &$plans) { 1343 $plan = new plan(null, plan::extract_record($record, 'p_')); 1344 $options = ['context' => $context]; 1345 $plans[$plan->get('id')] = [ 1346 'name' => format_string($plan->get('name'), true, $options), 1347 'reviewer_is_you' => transform::yesno($plan->get('reviewerid') == $userid), 1348 'timecreated' => transform::datetime($plan->get('timecreated')), 1349 'timemodified' => transform::datetime($plan->get('timemodified')), 1350 'created_or_modified_by_you' => transform::yesno($plan->get('usermodified') == $userid), 1351 'competencies' => [], 1352 ]; 1353 }; 1354 1355 $initcompetency = function($record, $planid) use (&$plans) { 1356 $competency = new competency(null, competency::extract_record($record, 'c_')); 1357 $plans[$planid]['competencies'][$competency->get('id')] = static::transform_competency_brief($competency); 1358 }; 1359 1360 // Look for associations that were created. 1361 $sql = " 1362 SELECT $pfields, $pcfields, $cfields 1363 FROM {" . plan_competency::TABLE . "} pc 1364 JOIN {" . plan::TABLE . "} p 1365 ON p.id = pc.planid 1366 JOIN {" . competency::TABLE . "} c 1367 ON c.id = pc.competencyid 1368 WHERE p.userid = :targetuserid 1369 AND pc.usermodified = :userid 1370 ORDER BY p.id, c.id"; 1371 $params = [ 1372 'targetuserid' => $context->instanceid, 1373 'userid' => $userid, 1374 ]; 1375 1376 $recordset = $DB->get_recordset_sql($sql, $params); 1377 foreach ($recordset as $record) { 1378 $planid = $record->p_id; 1379 if (!isset($plans[$planid])) { 1380 $initplan($record); 1381 } 1382 1383 $initcompetency($record, $planid); 1384 $pc = new plan_competency(null, plan_competency::extract_record($record, 'pc_')); 1385 $plans[$planid]['competencies'][$pc->get('competencyid')] = array_merge( 1386 $plans[$planid]['competencies'][$pc->get('competencyid')], [ 1387 'timemodified' => $pc->get('timemodified') ? transform::datetime($pc->get('timemodified')) : '-', 1388 'timecreated' => $pc->get('timecreated') ? transform::datetime($pc->get('timecreated')) : '-', 1389 'created_or_modified_by_you' => transform::yesno($pc->get('usermodified') == $userid), 1390 ] 1391 ); 1392 } 1393 $recordset->close(); 1394 1395 // Look for final grades that were given. 1396 $sql = " 1397 SELECT $pfields, $ucpfields, $cfields 1398 FROM {" . user_competency_plan::TABLE . "} ucp 1399 JOIN {" . plan::TABLE . "} p 1400 ON p.id = ucp.planid 1401 JOIN {" . competency::TABLE . "} c 1402 ON c.id = ucp.competencyid 1403 WHERE p.userid = :targetuserid 1404 AND ucp.usermodified = :userid 1405 ORDER BY p.id, c.id"; 1406 $params = [ 1407 'targetuserid' => $context->instanceid, 1408 'userid' => $userid, 1409 ]; 1410 1411 $recordset = $DB->get_recordset_sql($sql, $params); 1412 foreach ($recordset as $record) { 1413 $planid = $record->p_id; 1414 $competencyid = $record->c_id; 1415 1416 if (!isset($plans[$planid])) { 1417 $initplan($record); 1418 } 1419 1420 if (!isset($plans[$planid]['competencies'][$competencyid])) { 1421 $initcompetency($record, $planid); 1422 } 1423 1424 $competency = new competency(null, competency::extract_record($record, 'c_')); 1425 $ucp = new user_competency_plan(null, user_competency_plan::extract_record($record, 'ucp_')); 1426 $plans[$planid]['competencies'][$competencyid]['rating'] = static::transform_user_competency($userid, $ucp, 1427 $competency, $helper); 1428 } 1429 $recordset->close(); 1430 1431 // Find the plans that were modified or reviewed. 1432 $insql = " > 0"; 1433 $inparams = []; 1434 if (!empty($plans)) { 1435 list($insql, $inparams) = $DB->get_in_or_equal(array_keys($plans), SQL_PARAMS_NAMED, 'param', false); 1436 } 1437 $sql = " 1438 SELECT $pfields 1439 FROM {" . plan::TABLE . "} p 1440 LEFT JOIN {comments} c 1441 ON c.contextid = :contextid 1442 AND c.commentarea = :planarea 1443 AND c.component = :competency 1444 AND c.itemid = p.id 1445 WHERE p.userid = :targetuserid 1446 AND (p.usermodified = :userid1 1447 OR p.reviewerid = :userid2 1448 OR c.userid = :userid3) 1449 AND p.id $insql 1450 ORDER BY p.id"; 1451 $params = array_merge($inparams, [ 1452 'targetuserid' => $context->instanceid, 1453 'userid1' => $userid, 1454 'userid2' => $userid, 1455 'userid3' => $userid, 1456 'contextid' => $context->id, 1457 'planarea' => 'plan', 1458 'competency' => 'competency' 1459 ]); 1460 1461 $recordset = $DB->get_recordset_sql($sql, $params); 1462 foreach ($recordset as $record) { 1463 $planid = $record->p_id; 1464 if (!isset($plans[$planid])) { 1465 $initplan($record); 1466 } 1467 } 1468 $recordset->close(); 1469 1470 // Export each plan on its own. 1471 foreach ($plans as $planid => $plan) { 1472 $planpath = array_merge($path, ["{$plan['name']} ({$planid})"]); 1473 $plan['competencies'] = array_values($plan['competencies']); // Drop the keys. 1474 1475 writer::with_context($context)->export_data($planpath, (object) $plan); 1476 \core_comment\privacy\provider::export_comments($context, 'competency', 'plan', $planid, $planpath, true); 1477 } 1478 } 1479 1480 /** 1481 * Export a user's data related to competencies. 1482 * 1483 * @param int $userid The user ID we're exporting for. 1484 * @param context_user $context The context of the user in which we're gathering data. 1485 * @return void 1486 */ 1487 protected static function export_user_data_competencies_related_to_me($userid, context_user $context) { 1488 global $DB; 1489 1490 $path = [ 1491 get_string('competencies', 'core_competency'), 1492 get_string('privacy:path:relatedtome', 'core_competency'), 1493 get_string('competencies', 'core_competency'), 1494 ]; 1495 $competencies = []; 1496 $helper = new performance_helper(); 1497 $cfields = competency::get_sql_fields('c', 'c_'); 1498 $ucfields = user_competency::get_sql_fields('uc', 'uc_'); 1499 $efields = evidence::get_sql_fields('e', 'e_'); 1500 1501 $initcompetency = function($record) use (&$competencies) { 1502 $competency = new competency(null, competency::extract_record($record, 'c_')); 1503 $competencies[$competency->get('id')] = array_merge(static::transform_competency_brief($competency), [ 1504 'evidence' => [] 1505 ]); 1506 }; 1507 1508 $initusercomp = function($competency, $record) use (&$competencies, $userid, $helper) { 1509 $competencyid = $competency->get('id'); 1510 $uc = new user_competency(null, user_competency::extract_record($record, 'uc_')); 1511 $competencies[$competencyid]['uc_id'] = $uc->get('id'); 1512 $competencies[$competencyid]['rating'] = static::transform_user_competency($userid, $uc, $competency, $helper); 1513 }; 1514 1515 // Look for evidence. 1516 $sql = " 1517 SELECT $efields, $ucfields, $cfields 1518 FROM {" . evidence::TABLE . "} e 1519 JOIN {" . user_competency::TABLE . "} uc 1520 ON uc.id = e.usercompetencyid 1521 JOIN {" . competency::TABLE . "} c 1522 ON c.id = uc.competencyid 1523 WHERE uc.userid = :targetuserid 1524 AND (e.usermodified = :userid1 1525 OR e.actionuserid = :userid2) 1526 ORDER BY c.id, e.id"; 1527 $params = [ 1528 'targetuserid' => $context->instanceid, 1529 'userid1' => $userid, 1530 'userid2' => $userid, 1531 ]; 1532 $recordset = $DB->get_recordset_sql($sql, $params); 1533 foreach ($recordset as $record) { 1534 $competencyid = $record->c_id; 1535 $competency = new competency(null, competency::extract_record($record, 'c_')); 1536 1537 if (!isset($competencies[$competencyid])) { 1538 $initcompetency($record); 1539 } 1540 1541 if (!array_key_exists('rating', $competencies[$competencyid])) { 1542 $competencies[$competencyid]['rating'] = null; 1543 if ($record->uc_reviewerid == $userid || $record->uc_usermodified == $userid) { 1544 $initusercomp($competency, $record); 1545 } 1546 } 1547 1548 $evidence = new evidence(null, evidence::extract_record($record, 'e_')); 1549 $competencies[$competencyid]['evidence'][] = static::transform_evidence($userid, $evidence, $competency, $helper); 1550 } 1551 $recordset->close(); 1552 1553 // Look for user competency we modified and didn't catch. 1554 $insql = ' > 0'; 1555 $inparams = []; 1556 if (!empty($competencies)) { 1557 list($insql, $inparams) = $DB->get_in_or_equal(array_keys($competencies), SQL_PARAMS_NAMED, 'param', false); 1558 } 1559 $sql = " 1560 SELECT $ucfields, $cfields 1561 FROM {" . user_competency::TABLE . "} uc 1562 JOIN {" . competency::TABLE . "} c 1563 ON c.id = uc.competencyid 1564 LEFT JOIN {comments} cmt 1565 ON cmt.contextid = :contextid 1566 AND cmt.commentarea = :ucarea 1567 AND cmt.component = :competency 1568 AND cmt.itemid = uc.id 1569 WHERE uc.userid = :targetuserid 1570 AND (uc.usermodified = :userid1 1571 OR uc.reviewerid = :userid2 1572 OR cmt.userid = :userid3) 1573 AND uc.competencyid $insql 1574 ORDER BY c.id, uc.id"; 1575 $params = array_merge($inparams, [ 1576 'targetuserid' => $context->instanceid, 1577 'userid1' => $userid, 1578 'userid2' => $userid, 1579 'userid3' => $userid, 1580 'contextid' => $context->id, 1581 'ucarea' => 'user_competency', 1582 'competency' => 'competency', 1583 ]); 1584 1585 $recordset = $DB->get_recordset_sql($sql, $params); 1586 foreach ($recordset as $record) { 1587 $competency = new competency(null, competency::extract_record($record, 'c_')); 1588 if (!isset($competencies[$competency->get('id')])) { 1589 $initcompetency($record); 1590 $initusercomp($competency, $record); 1591 } 1592 } 1593 $recordset->close(); 1594 1595 // Export each competency on its own. 1596 foreach ($competencies as $competencyid => $competency) { 1597 $comppath = array_merge($path, ["{$competency['name']} ({$competencyid})"]); 1598 $ucid = isset($competency['uc_id']) ? $competency['uc_id'] : null; 1599 unset($competency['uc_id']); 1600 1601 // Send to writer. 1602 writer::with_context($context)->export_data($comppath, (object) $competency); 1603 if ($ucid) { 1604 \core_comment\privacy\provider::export_comments($context, 'competency', 'user_competency', $ucid, $comppath, true); 1605 } 1606 } 1607 } 1608 1609 /** 1610 * Export a user's data related to evidence of prior learning. 1611 * 1612 * @param int $userid The user ID we're exporting for. 1613 * @param context_user $context The context of the user in which we're gathering data. 1614 * @return void 1615 */ 1616 protected static function export_user_data_user_evidence_related_to_me($userid, context_user $context) { 1617 global $DB; 1618 1619 $path = [ 1620 get_string('competencies', 'core_competency'), 1621 get_string('privacy:path:relatedtome', 'core_competency'), 1622 get_string('privacy:path:userevidence', 'core_competency'), 1623 ]; 1624 $evidence = []; 1625 $helper = new performance_helper(); 1626 $cfields = competency::get_sql_fields('c', 'c_'); 1627 $uecfields = user_evidence_competency::get_sql_fields('uec', 'uec_'); 1628 $uefields = user_evidence::get_sql_fields('ue', 'ue_'); 1629 1630 $initevidence = function($record) use (&$evidence, $userid) { 1631 $ue = new user_evidence(null, user_evidence::extract_record($record, 'ue_')); 1632 $evidence[$ue->get('id')] = static::transform_user_evidence($userid, $ue); 1633 }; 1634 1635 // Look for evidence. 1636 $sql = " 1637 SELECT $uefields, $uecfields, $cfields 1638 FROM {" . user_evidence_competency::TABLE . "} uec 1639 JOIN {" . user_evidence::TABLE . "} ue 1640 ON ue.id = uec.userevidenceid 1641 JOIN {" . competency::TABLE . "} c 1642 ON c.id = uec.competencyid 1643 WHERE ue.userid = :targetuserid 1644 AND uec.usermodified = :userid 1645 ORDER BY ue.id, c.id"; 1646 $params = [ 1647 'targetuserid' => $context->instanceid, 1648 'userid' => $userid, 1649 ]; 1650 $recordset = $DB->get_recordset_sql($sql, $params); 1651 foreach ($recordset as $record) { 1652 $ueid = $record->ue_id; 1653 if (!isset($evidence[$ueid])) { 1654 $initevidence($record); 1655 } 1656 1657 $competency = new competency(null, competency::extract_record($record, 'c_')); 1658 $uec = new user_evidence_competency(null, user_evidence_competency::extract_record($record, 'uec_')); 1659 $evidence[$ueid]['competencies'][] = array_merge(static::transform_competency_brief($competency), [ 1660 'timemodified' => $uec->get('timemodified') ? transform::datetime($uec->get('timemodified')) : '-', 1661 'timecreated' => $uec->get('timecreated') ? transform::datetime($uec->get('timecreated')) : '-', 1662 'created_or_modified_by_you' => transform::yesno($uec->get('usermodified')) 1663 ]); 1664 } 1665 $recordset->close(); 1666 1667 // Look for user evidence we modified or reviewed and didn't catch. 1668 $insql = ' > 0'; 1669 $inparams = []; 1670 if (!empty($evidence)) { 1671 list($insql, $inparams) = $DB->get_in_or_equal(array_keys($evidence), SQL_PARAMS_NAMED, 'param', false); 1672 } 1673 $sql = " 1674 SELECT $uefields 1675 FROM {" . user_evidence::TABLE . "} ue 1676 WHERE ue.userid = :targetuserid 1677 AND ue.usermodified = :userid 1678 AND ue.id $insql 1679 ORDER BY ue.id"; 1680 $params = array_merge($inparams, [ 1681 'targetuserid' => $context->instanceid, 1682 'userid' => $userid, 1683 ]); 1684 1685 $recordset = $DB->get_recordset_sql($sql, $params); 1686 foreach ($recordset as $record) { 1687 $initevidence($record); 1688 } 1689 $recordset->close(); 1690 1691 // Export files, then content. 1692 foreach ($evidence as $ueid => $data) { 1693 $uepath = array_merge($path, ["{$data['name']} ({$ueid})"]); 1694 writer::with_context($context)->export_area_files($uepath, 'core_competency', 'userevidence', $ueid); 1695 writer::with_context($context)->export_data($uepath, (object) $data); 1696 } 1697 } 1698 1699 /** 1700 * Export the evidence of prior learning of a user. 1701 * 1702 * @param context_user $context The context of the user we're exporting for. 1703 * @return void 1704 */ 1705 protected static function export_user_data_user_evidence(context_user $context) { 1706 global $DB; 1707 1708 $userid = $context->instanceid; 1709 $path = [get_string('competencies', 'core_competency'), get_string('privacy:path:userevidence', 'core_competency')]; 1710 $uefields = user_evidence::get_sql_fields('ue', 'ue_'); 1711 $cfields = competency::get_sql_fields('c', 'c_'); 1712 1713 $sql = " 1714 SELECT $uefields, $cfields 1715 FROM {" . user_evidence::TABLE . "} ue 1716 LEFT JOIN {" . user_evidence_competency::TABLE . "} uec 1717 ON uec.userevidenceid = ue.id 1718 LEFT JOIN {" . competency::TABLE . "} c 1719 ON c.id = uec.competencyid 1720 WHERE ue.userid = :userid 1721 ORDER BY ue.id"; 1722 $params = ['userid' => $userid]; 1723 1724 $recordset = $DB->get_recordset_sql($sql, $params); 1725 static::recordset_loop_and_export($recordset, 'ue_id', null, function($carry, $record) use ($userid, $context){ 1726 if ($carry === null) { 1727 $ue = new user_evidence(null, user_evidence::extract_record($record, 'ue_')); 1728 $carry = static::transform_user_evidence($userid, $ue); 1729 } 1730 1731 if (!empty($record->c_id)) { 1732 $competency = new competency(null, competency::extract_record($record, 'c_')); 1733 $carry['competencies'][] = static::transform_competency_brief($competency); 1734 } 1735 1736 return $carry; 1737 }, function($ueid, $data) use ($context, $path) { 1738 $finalpath = array_merge($path, [$data['name'] . ' (' . $ueid . ')']); 1739 writer::with_context($context)->export_area_files($finalpath, 'core_competency', 'userevidence', $ueid); 1740 writer::with_context($context)->export_data($finalpath, (object) $data); 1741 }); 1742 } 1743 1744 /** 1745 * Export the user data related to frameworks in context. 1746 * 1747 * @param int $userid The user ID. 1748 * @param context $context The context. 1749 * @return void 1750 */ 1751 protected static function export_user_data_frameworks_in_context($userid, context $context) { 1752 global $DB; 1753 1754 $ffields = competency_framework::get_sql_fields('f', 'f_'); 1755 $cfields = competency::get_sql_fields('c', 'c_'); 1756 $c2fields = competency::get_sql_fields('c2', 'c2_'); 1757 $rcfields = related_competency::get_sql_fields('rc', 'rc_'); 1758 1759 $frameworks = []; 1760 $initframework = function($record) use (&$frameworks, $userid) { 1761 $framework = new competency_framework(null, competency_framework::extract_record($record, 'f_')); 1762 $frameworks[$framework->get('id')] = array_merge(static::transform_framework_brief($framework), [ 1763 'timemodified' => transform::datetime($framework->get('timemodified')), 1764 'created_or_modified_by_you' => transform::yesno($framework->get('usermodified') == $userid), 1765 'competencies' => [] 1766 ]); 1767 }; 1768 $initcompetency = function($record, $prefix) use (&$frameworks, $userid) { 1769 $competency = new competency(null, competency::extract_record($record, $prefix)); 1770 $frameworks[$competency->get('competencyframeworkid')]['competencies'][$competency->get('id')] = array_merge( 1771 static::transform_competency_brief($competency), 1772 [ 1773 'timemodified' => transform::datetime($competency->get('timemodified')), 1774 'created_or_modified_by_you' => transform::yesno($competency->get('usermodified') == $userid), 1775 'related_competencies' => [] 1776 ] 1777 ); 1778 }; 1779 1780 // Start by looking for related competencies. 1781 $sql = " 1782 SELECT $ffields, $cfields, $c2fields, $rcfields 1783 FROM {" . related_competency::TABLE . "} rc 1784 JOIN {" . competency::TABLE . "} c 1785 ON c.id = rc.competencyid 1786 JOIN {" . competency::TABLE . "} c2 1787 ON c2.id = rc.relatedcompetencyid 1788 JOIN {" . competency_framework::TABLE . "} f 1789 ON f.id = c.competencyframeworkid 1790 WHERE rc.usermodified = :userid 1791 AND f.contextid = :contextid 1792 ORDER BY rc.id, c.id"; 1793 $params = ['userid' => $userid, 'contextid' => $context->id]; 1794 1795 $recordset = $DB->get_recordset_sql($sql, $params); 1796 foreach ($recordset as $record) { 1797 $frameworkid = $record->f_id; 1798 $comp1id = $record->c_id; 1799 $comp2id = $record->c2_id; 1800 1801 if (!isset($frameworks[$frameworkid])) { 1802 $initframework($record); 1803 } 1804 1805 foreach (['c_', 'c2_'] as $key) { 1806 $competencyid = $record->{$key . 'id'}; 1807 if (!isset($frameworks[$frameworkid]['competencies'][$competencyid])) { 1808 $initcompetency($record, $key); 1809 } 1810 } 1811 1812 $relcomp = new related_competency(null, related_competency::extract_record($record, 'rc_')); 1813 foreach (['c_' => 'c2_', 'c2_' => 'c_'] as $key => $relatedkey) { 1814 $competencyid = $record->{$key . 'id'}; 1815 $competency = new competency(null, competency::extract_record($record, $relatedkey)); 1816 $frameworks[$frameworkid]['competencies'][$competencyid]['related_competencies'][] = [ 1817 'name' => $competency->get('shortname'), 1818 'idnumber' => $competency->get('idnumber'), 1819 'timemodified' => transform::datetime($relcomp->get('timemodified')), 1820 'created_or_modified_by_you' => transform::yesno($relcomp->get('usermodified') == $userid), 1821 ]; 1822 } 1823 } 1824 $recordset->close(); 1825 1826 // Now look for competencies, but skip the ones we've already seen. 1827 $competencyids = array_reduce($frameworks, function($carry, $framework) { 1828 return array_merge($carry, array_keys($framework['competencies'])); 1829 }, []); 1830 $insql = ' IS NOT NULL'; 1831 $inparams = []; 1832 if (!empty($competencyids)) { 1833 list($insql, $inparams) = $DB->get_in_or_equal($competencyids, SQL_PARAMS_NAMED, 'param', false); 1834 } 1835 $sql = " 1836 SELECT $ffields, $cfields 1837 FROM {" . competency::TABLE . "} c 1838 JOIN {" . competency_framework::TABLE . "} f 1839 ON f.id = c.competencyframeworkid 1840 WHERE c.usermodified = :userid 1841 AND f.contextid = :contextid 1842 AND c.id $insql 1843 ORDER BY c.id"; 1844 $params = array_merge($inparams, ['userid' => $userid, 'contextid' => $context->id]); 1845 $recordset = $DB->get_recordset_sql($sql, $params); 1846 foreach ($recordset as $record) { 1847 $frameworkid = $record->f_id; 1848 if (!isset($frameworks[$frameworkid])) { 1849 $initframework($record); 1850 } 1851 $initcompetency($record, 'c_'); 1852 } 1853 $recordset->close(); 1854 1855 // Now look for frameworks, but skip the ones we've already seen. 1856 $frameworkids = array_keys($frameworks); 1857 $insql = ' IS NOT NULL'; 1858 $inparams = []; 1859 if (!empty($frameworkids)) { 1860 list($insql, $inparams) = $DB->get_in_or_equal($frameworkids, SQL_PARAMS_NAMED, 'param', false); 1861 } 1862 $sql = " 1863 SELECT $ffields 1864 FROM {" . competency_framework::TABLE . "} f 1865 WHERE f.usermodified = :userid 1866 AND f.contextid = :contextid 1867 AND f.id $insql 1868 ORDER BY f.id"; 1869 $params = array_merge($inparams, ['userid' => $userid, 'contextid' => $context->id]); 1870 $recordset = $DB->get_recordset_sql($sql, $params); 1871 foreach ($recordset as $record) { 1872 context_helper::preload_from_record($record); 1873 $initframework($record); 1874 } 1875 $recordset->close(); 1876 1877 // Export all the things! 1878 writer::with_context($context)->export_related_data( 1879 [get_string('competencies', 'core_competency')], 1880 'frameworks', 1881 (object) [ 1882 // Drop the temporary IDs. 1883 'frameworks' => array_reduce($frameworks, function($carry, $item) { 1884 $item['competencies'] = array_values($item['competencies']); 1885 $carry[] = $item; 1886 return $carry; 1887 }, []) 1888 ] 1889 ); 1890 } 1891 1892 /** 1893 * Export the user data related to templates in contexts. 1894 * 1895 * @param int $userid The user ID. 1896 * @param context $context The context. 1897 * @return void 1898 */ 1899 protected static function export_user_data_templates_in_context($userid, context $context) { 1900 global $DB; 1901 1902 $tfields = template::get_sql_fields('t', 't_'); 1903 $cfields = competency::get_sql_fields('c', 'c_'); 1904 $tcfields = template_competency::get_sql_fields('tc', 'tc_'); 1905 $tchfields = template_cohort::get_sql_fields('tch', 'tch_'); 1906 1907 $templates = []; 1908 $inittemplate = function($record) use (&$templates, $userid) { 1909 $template = new template(null, template::extract_record($record, 't_')); 1910 $templates[$template->get('id')] = array_merge(static::transform_template_brief($template), [ 1911 'timemodified' => transform::datetime($template->get('timemodified')), 1912 'created_or_modified_by_you' => transform::yesno($template->get('usermodified') == $userid), 1913 'competencies' => [], 1914 'cohorts' => [] 1915 ]); 1916 }; 1917 1918 // Find the template competencies. 1919 $sql = " 1920 SELECT $tfields, $cfields, $tcfields 1921 FROM {" . template_competency::TABLE . "} tc 1922 JOIN {" . template::TABLE . "} t 1923 ON t.id = tc.templateid 1924 JOIN {" . competency::TABLE . "} c 1925 ON c.id = tc.competencyid 1926 WHERE t.contextid = :contextid 1927 AND tc.usermodified = :userid 1928 ORDER BY t.id, tc.id"; 1929 $params = ['userid' => $userid, 'contextid' => $context->id]; 1930 $recordset = $DB->get_recordset_sql($sql, $params); 1931 foreach ($recordset as $record) { 1932 $templateid = $record->t_id; 1933 if (!isset($templates[$templateid])) { 1934 $inittemplate($record); 1935 } 1936 $tplcomp = new template_competency(null, template_competency::extract_record($record, 'tc_')); 1937 $competency = new competency(null, competency::extract_record($record, 'c_')); 1938 $templates[$templateid]['competencies'][] = array_merge( 1939 static::transform_competency_brief($competency), 1940 [ 1941 'timemodified' => transform::datetime($tplcomp->get('timemodified')), 1942 'created_or_modified_by_you' => transform::yesno($tplcomp->get('usermodified') == $userid) 1943 ] 1944 ); 1945 } 1946 $recordset->close(); 1947 1948 // Find the template cohorts. 1949 $sql = " 1950 SELECT $tfields, $tchfields, c.name AS cohortname 1951 FROM {" . template_cohort::TABLE . "} tch 1952 JOIN {" . template::TABLE . "} t 1953 ON t.id = tch.templateid 1954 JOIN {cohort} c 1955 ON c.id = tch.cohortid 1956 WHERE t.contextid = :contextid 1957 AND tch.usermodified = :userid 1958 ORDER BY t.id, tch.id"; 1959 $params = ['userid' => $userid, 'contextid' => $context->id]; 1960 $recordset = $DB->get_recordset_sql($sql, $params); 1961 foreach ($recordset as $record) { 1962 $templateid = $record->t_id; 1963 if (!isset($templates[$templateid])) { 1964 $inittemplate($record); 1965 } 1966 $tplcohort = new template_cohort(null, template_cohort::extract_record($record, 'tch_')); 1967 $templates[$templateid]['cohorts'][] = [ 1968 'name' => $record->cohortname, 1969 'timemodified' => transform::datetime($tplcohort->get('timemodified')), 1970 'created_or_modified_by_you' => transform::yesno($tplcohort->get('usermodified') == $userid) 1971 ]; 1972 } 1973 $recordset->close(); 1974 1975 // Find the modified templates which we haven't been found yet. 1976 $templateids = array_keys($templates); 1977 $insql = "IS NOT NULL"; 1978 $inparams = []; 1979 if (!empty($templateids)) { 1980 list($insql, $inparams) = $DB->get_in_or_equal($templateids, SQL_PARAMS_NAMED, 'param', false); 1981 } 1982 $sql = " 1983 SELECT $tfields 1984 FROM {" . template::TABLE . "} t 1985 WHERE t.contextid = :contextid 1986 AND t.usermodified = :userid 1987 AND t.id $insql 1988 ORDER BY t.id"; 1989 $params = array_merge($inparams, ['userid' => $userid, 'contextid' => $context->id]); 1990 $recordset = $DB->get_recordset_sql($sql, $params); 1991 foreach ($recordset as $record) { 1992 $inittemplate($record); 1993 } 1994 $recordset->close(); 1995 1996 // Export all the things! 1997 writer::with_context($context)->export_related_data([get_string('competencies', 'core_competency')], 1998 'templates', (object) ['templates' => array_values($templates)]); 1999 } 2000 2001 /** 2002 * Transform a competency into a brief description. 2003 * 2004 * @param competency $competency The competency. 2005 * @return array 2006 */ 2007 protected static function transform_competency_brief(competency $competency) { 2008 global $OUTPUT; 2009 $exporter = new \core_competency\external\competency_exporter($competency, ['context' => $competency->get_context()]); 2010 $data = $exporter->export($OUTPUT); 2011 return [ 2012 'idnumber' => $data->idnumber, 2013 'name' => $data->shortname, 2014 'description' => $data->description 2015 ]; 2016 } 2017 2018 /** 2019 * Transform a competency rating. 2020 * 2021 * @param competency $competency The competency. 2022 * @param int $grade The grade. 2023 * @param performance_helper $helper The performance helper. 2024 * @return string 2025 */ 2026 protected static function transform_competency_grade(competency $competency, $grade, performance_helper $helper) { 2027 if ($grade === null) { 2028 return '-'; 2029 } 2030 $scale = $helper->get_scale_from_competency($competency); 2031 return $scale->scale_items[$grade - 1]; 2032 } 2033 2034 /** 2035 * Transform an evidence. 2036 * 2037 * @param int $userid The user ID we are exporting for. 2038 * @param evidence $evidence The evidence. 2039 * @param competency $competency The associated competency. 2040 * @param performance_helper $helper The performance helper. 2041 * @return array 2042 */ 2043 protected static function transform_evidence($userid, evidence $evidence, competency $competency, performance_helper $helper) { 2044 $action = $evidence->get('action'); 2045 $actiontxt = '?'; 2046 if ($action == evidence::ACTION_LOG) { 2047 $actiontxt = get_string('privacy:evidence:action:log', 'core_competency'); 2048 } else if ($action == evidence::ACTION_COMPLETE) { 2049 $actiontxt = get_string('privacy:evidence:action:complete', 'core_competency'); 2050 } else if ($action == evidence::ACTION_OVERRIDE) { 2051 $actiontxt = get_string('privacy:evidence:action:override', 'core_competency'); 2052 } 2053 2054 $actionuserid = $evidence->get('actionuserid'); 2055 2056 return [ 2057 'action' => $actiontxt, 2058 'actionuserid' => $actionuserid ? transform::user($actionuserid) : '-', 2059 'acting_user_is_you' => transform::yesno($userid == $actionuserid), 2060 'description' => (string) $evidence->get_description(), 2061 'url' => $evidence->get('url'), 2062 'grade' => static::transform_competency_grade($competency, $evidence->get('grade'), $helper), 2063 'note' => $evidence->get('note'), 2064 'timecreated' => transform::datetime($evidence->get('timecreated')), 2065 'timemodified' => transform::datetime($evidence->get('timemodified')), 2066 'created_or_modified_by_you' => transform::yesno($userid == $evidence->get('usermodified')) 2067 ]; 2068 } 2069 2070 /** 2071 * Transform a framework into a brief description. 2072 * 2073 * @param competency_framework $framework The framework. 2074 * @return array 2075 */ 2076 protected static function transform_framework_brief(competency_framework $framework) { 2077 global $OUTPUT; 2078 $exporter = new \core_competency\external\competency_framework_exporter($framework); 2079 $data = $exporter->export($OUTPUT); 2080 return [ 2081 'name' => $data->shortname, 2082 'idnumber' => $data->idnumber, 2083 'description' => $data->description 2084 ]; 2085 } 2086 2087 /** 2088 * Transform a template into a brief description. 2089 * 2090 * @param template $template The Template. 2091 * @return array 2092 */ 2093 protected static function transform_template_brief(template $template) { 2094 global $OUTPUT; 2095 $exporter = new \core_competency\external\template_exporter($template); 2096 $data = $exporter->export($OUTPUT); 2097 return [ 2098 'name' => $data->shortname, 2099 'description' => $data->description 2100 ]; 2101 } 2102 2103 /** 2104 * Transform proficiency. 2105 * 2106 * @param null|bool $proficiency The proficiency. 2107 * @return string 2108 */ 2109 protected static function transform_proficiency($proficiency) { 2110 return $proficiency !== null ? transform::yesno($proficiency) : '-'; 2111 } 2112 2113 /** 2114 * Transform user competency. 2115 * 2116 * @param int $userid The user ID we are exporting for. 2117 * @param user_competency|user_competency_plan|user_competency_course $uc The user competency. 2118 * @param competency $competency The associated competency. 2119 * @param performance_helper $helper The performance helper. 2120 * @return array 2121 */ 2122 protected static function transform_user_competency($userid, $uc, competency $competency, performance_helper $helper) { 2123 $data = [ 2124 'proficient' => static::transform_proficiency($uc->get('proficiency')), 2125 'rating' => static::transform_competency_grade($competency, $uc->get('grade'), $helper), 2126 'timemodified' => $uc->get('timemodified') ? transform::datetime($uc->get('timemodified')) : '-', 2127 'timecreated' => $uc->get('timecreated') ? transform::datetime($uc->get('timecreated')) : '-', 2128 'created_or_modified_by_you' => transform::yesno($uc->get('usermodified') == $userid), 2129 ]; 2130 2131 if ($uc instanceof user_competency) { 2132 $reviewer = $uc->get('reviewerid'); 2133 $data['status'] = (string) user_competency::get_status_name($uc->get('status')); 2134 $data['reviewerid'] = $reviewer ? transform::user($reviewer) : '-'; 2135 $data['reviewer_is_you'] = transform::yesno($reviewer == $userid); 2136 } 2137 2138 return $data; 2139 } 2140 2141 /** 2142 * Transform a user evidence. 2143 * 2144 * @param int $userid The user we are exporting for. 2145 * @param user_evidence $ue The evidence of prior learning. 2146 * @return array 2147 */ 2148 protected static function transform_user_evidence($userid, user_evidence $ue) { 2149 $options = ['context' => $ue->get_context()]; 2150 return [ 2151 'name' => format_string($ue->get('name'), true, $options), 2152 'description' => format_text($ue->get('description'), $ue->get('descriptionformat'), $options), 2153 'url' => $ue->get('url'), 2154 'timecreated' => $ue->get('timecreated') ? transform::datetime($ue->get('timecreated')) : '-', 2155 'timemodified' => $ue->get('timemodified') ? transform::datetime($ue->get('timemodified')) : '-', 2156 'created_or_modified_by_you' => transform::yesno($ue->get('usermodified') == $userid), 2157 'competencies' => [] 2158 ]; 2159 } 2160 2161 /** 2162 * Loop and export from a recordset. 2163 * 2164 * @param moodle_recordset $recordset The recordset. 2165 * @param string $splitkey The record key to determine when to export. 2166 * @param mixed $initial The initial data to reduce from. 2167 * @param callable $reducer The function to return the dataset, receives current dataset, and the current record. 2168 * @param callable $export The function to export the dataset, receives the last value from $splitkey and the dataset. 2169 * @return void 2170 */ 2171 protected static function recordset_loop_and_export(moodle_recordset $recordset, $splitkey, $initial, 2172 callable $reducer, callable $export) { 2173 2174 $data = $initial; 2175 $lastid = null; 2176 2177 foreach ($recordset as $record) { 2178 if ($lastid && $record->{$splitkey} != $lastid) { 2179 $export($lastid, $data); 2180 $data = $initial; 2181 } 2182 $data = $reducer($data, $record); 2183 $lastid = $record->{$splitkey}; 2184 } 2185 $recordset->close(); 2186 2187 if (!empty($lastid)) { 2188 $export($lastid, $data); 2189 } 2190 } 2191 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body