Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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