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
/**
18
 * Privacy Subsystem implementation for H5P.
19
 */
20
 
21
namespace mod_hvp\privacy;
22
 
23
use \core_privacy\local\request\writer;
24
use \core_privacy\local\request\contextlist;
25
use \core_privacy\local\request\approved_contextlist;
26
use \core_privacy\local\request\deletion_criteria;
27
use \core_privacy\local\request\approved_userlist;
28
use \core_privacy\local\request\userlist;
29
use \core_privacy\local\metadata\collection;
30
 
31
defined('MOODLE_INTERNAL') || die();
32
 
33
/**
34
 * Privacy Subsystem implementation for H5P.
35
 */
36
class provider implements
37
    // This plugin has data.
38
    \core_privacy\local\metadata\provider,
39
    \core_privacy\local\request\core_userlist_provider,
40
    // This plugin currently implements the original plugin_provider interface.
41
    \core_privacy\local\request\plugin\provider {
42
 
43
    use \core_privacy\local\legacy_polyfill;
44
 
45
    /**
46
     * Get the list of contexts that contain user information for the specified user.
47
     *
48
     * @param collection $items The collection to add metadata to.
49
     *
50
     * @return collection The array of metadata
51
     */
52
    public static function _get_metadata(collection $items) {
53
        // Stores files using the Moodle file api.
54
        $items->add_subsystem_link(
55
            'core_files',
56
            [],
57
            'privacy:metadata:core_files'
58
        );
59
 
60
        // Stores grades using the Moodle gradebook api.
61
        $items->add_subsystem_link(
62
            'core_grades',
63
            [],
64
            'privacy:metadata:core_grades'
65
        );
66
 
67
        // Content user data table.
68
        $items->add_database_table('hvp_content_user_data', [
69
            'id'                       => 'privacy:metadata:hvp_content_user_data:id',
70
            'user_id'                  => 'privacy:metadata:hvp_content_user_data:user_id',
71
            'hvp_id'                   => 'privacy:metadata:hvp_content_user_data:hvp_id',
72
            'sub_content_id'           => 'privacy:metadata:hvp_content_user_data:sub_content_id',
73
            'data_id'                  => 'privacy:metadata:hvp_content_user_data:data_id',
74
            'data'                     => 'privacy:metadata:hvp_content_user_data:data',
75
            'preloaded'                => 'privacy:metadata:hvp_content_user_data:preloaded',
76
            'delete_on_content_change' => 'privacy:metadata:hvp_content_user_data:delete_on_content_change',
77
        ],
78
            'privacy:metadata:hvp_content_user_data'
79
        );
80
 
81
        // Events table.
82
        $items->add_database_table('hvp_events', [
83
            'id'              => 'privacy:metadata:hvp_events:id',
84
            'user_id'         => 'privacy:metadata:hvp_events:user_id',
85
            'created_at'      => 'privacy:metadata:hvp_events:created_at',
86
            'type'            => 'privacy:metadata:hvp_events:type',
87
            'sub_type'        => 'privacy:metadata:hvp_events:sub_type',
88
            'content_id'      => 'privacy:metadata:hvp_events:content_id',
89
            'content_title'   => 'privacy:metadata:hvp_events:content_title',
90
            'library_name'    => 'privacy:metadata:hvp_events:library_name',
91
            'library_version' => 'privacy:metadata:hvp_events:library_version',
92
        ], 'privacy:metadata:hvp_events');
93
 
94
        // Xapi results table.
95
        $items->add_database_table('hvp_xapi_results', [
96
            'id'                        => 'privacy:metadata:hvp_xapi_results:id',
97
            'content_id'                => 'privacy:metadata:hvp_xapi_results:content_id',
98
            'user_id'                   => 'privacy:metadata:hvp_xapi_results:user_id',
99
            'parent_id'                 => 'privacy:metadata:hvp_xapi_results:parent_id',
100
            'interaction_type'          => 'privacy:metadata:hvp_xapi_results:interaction_type',
101
            'description'               => 'privacy:metadata:hvp_xapi_results:description',
102
            'correct_responses_pattern' => 'privacy:metadata:hvp_xapi_results:correct_responses_pattern',
103
            'response'                  => 'privacy:metadata:hvp_xapi_results:response',
104
            'additionals'               => 'privacy:metadata:hvp_xapi_results:additionals',
105
            'raw_score'                 => 'privacy:metadata:hvp_xapi_results:raw_score',
106
            'max_score'                 => 'privacy:metadata:hvp_xapi_results:max_score',
107
        ], 'privacy:metadata:hvp_xapi_results');
108
 
109
        return $items;
110
    }
111
 
112
    /**
113
     * Get the list of contexts where the specified user has attempted a quiz, or been involved with manual marking
114
     * and/or grading of a quiz.
115
     *
116
     * @param int $userid The user to search.
117
     *
118
     * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
119
     */
120
    public static function _get_contexts_for_userid($userid) {
121
        $contextlist = new contextlist();
122
 
123
        // Context for content_user_data.
124
        $cudsql = "
125
          SELECT
126
            c.id
127
          FROM {course_modules} cm
128
            INNER JOIN {modules} m ON m.id = cm.module
129
            INNER JOIN {context} c ON c.instanceid = cm.id
130
            INNER JOIN {hvp} h ON h.id = cm.instance
131
            INNER JOIN {hvp_content_user_data} d ON h.id = d.hvp_id
132
          WHERE m.name = 'hvp'
133
                AND c.contextlevel = :contextlevel
134
                AND d.user_id = :userid
135
        ";
136
 
137
        $cudparams = array(
138
            'contextlevel' => CONTEXT_MODULE,
139
            'userid'       => $userid,
140
        );
141
 
142
        $contextlist->add_from_sql($cudsql, $cudparams);
143
 
144
        // Context for xapi results.
145
        $xapisql = "
146
          SELECT
147
            c.id
148
          FROM {course_modules} cm
149
            INNER JOIN {modules} m ON m.id = cm.module
150
            INNER JOIN {context} c ON c.instanceid = cm.id
151
            INNER JOIN {hvp} h ON h.id = cm.instance
152
            INNER JOIN {hvp_xapi_results} x ON x.content_id = h.id
153
          WHERE m.name = 'hvp'
154
                AND c.contextlevel = :contextlevel
155
                AND x.user_id = :userid
156
        ";
157
 
158
        $xapiparams = array(
159
            'contextlevel' => CONTEXT_MODULE,
160
            'userid'       => $userid,
161
        );
162
 
163
        $contextlist->add_from_sql($xapisql, $xapiparams);
164
 
165
        // Table hvp_events note:
166
        // H5P events are not tied to instance ids, thus we cannot determine
167
        // their context ids, they are considered to be user level context
168
        // actions.
169
 
170
        return $contextlist;
171
    }
172
 
173
    /**
174
     * Export all user data for the specified user, in the specified contexts, using the supplied
175
     * exporter instance.
176
     *
177
     * @param approved_contextlist $contextlist The approved contexts to export information for.
178
     */
179
    public static function _export_user_data(approved_contextlist $contextlist) {
180
        global $DB;
181
 
182
        if (!count($contextlist)) {
183
            return;
184
        }
185
 
186
        $user   = $contextlist->get_user();
187
        $userid = $user->id;
188
        list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
189
 
190
        $cud = self::get_exportable_content_user_data($contextsql, $contextparams, $userid);
191
        $xapi = self::get_exportable_xapi_results($contextsql, $contextparams, $userid);
192
 
193
        // Export data with context.
194
        foreach ($contextlist->get_contexts() as $context) {
195
            $h5pdata = \core_privacy\local\request\helper::get_context_data($context, $contextlist->get_user());
196
            \core_privacy\local\request\helper::export_context_files($context, $contextlist->get_user());
197
 
198
            // Add content user data.
199
            if (!empty($cud[$context->id]) && !empty($cud[$context->id]->data)) {
200
                $h5pdata->contentuserdata = $cud[$context->id]->data;
201
            }
202
 
203
            // Add xAPI data.
204
            if (!empty($xapi[$context->id])) {
205
                $h5pdata->xapiresults = $xapi[$context->id];
206
            }
207
 
208
            writer::with_context($context)->export_data([], $h5pdata);
209
        }
210
 
211
        // Write H5PEvents to subcontext of the user context.
212
        $usercontext = \context_user::instance($userid);
213
        $h5pevents = self::get_exportable_events($userid);
214
        writer::with_context($usercontext)->export_data(['H5PEvents'], $h5pevents);
215
    }
216
 
217
    /**
218
     * Get exportable content user data for a given context and user
219
     *
220
     * @param $contextsql
221
     * @param $contextparams
222
     * @param $userid
223
     *
224
     * @return array Exportable and writable content user data
225
     * @throws \dml_exception
226
     */
227
    protected static function get_exportable_content_user_data($contextsql, $contextparams, $userid) {
228
        global $DB;
229
 
230
        $cudsql = "
231
          SELECT
232
            c.id as contextid,
233
            cm.id as cmid,
234
            h.id,
235
            h.name,
236
            cud.data
237
          FROM {context} c
238
            INNER JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
239
            INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
240
            INNER JOIN {hvp} h ON h.id = cm.instance
241
            INNER JOIN {hvp_content_user_data} cud ON cud.hvp_id = h.id
242
          WHERE cud.user_id = :userid
243
          AND c.id {$contextsql}
244
        ";
245
 
246
        $cudparams = [
247
            'contextlevel' => CONTEXT_MODULE,
248
            'modname'      => 'hvp',
249
            'userid'       => $userid,
250
        ];
251
        $cudparams += $contextparams;
252
 
253
        $cudresult = $DB->get_recordset_sql($cudsql, $cudparams);
254
        $cud       = [];
255
        foreach ($cudresult as $record) {
256
            $cud[$record->contextid] = $record;
257
        }
258
        $cudresult->close();
259
 
260
        return $cud;
261
    }
262
 
263
    /**
264
     * Get exportable xapi results from context and user id
265
     *
266
     * @param $contextsql
267
     * @param $contextparams
268
     * @param $userid
269
     *
270
     * @return array Exportable and writable xapi results
271
     * @throws \dml_exception
272
     */
273
    protected static function get_exportable_xapi_results($contextsql, $contextparams, $userid) {
274
        global $DB;
275
 
276
        $xapisql = "
277
          SELECT
278
            c.id as contextid,
279
            cm.id as cmid,
280
            h.id,
281
            h.name,
282
            x.content_id,
283
            x.parent_id,
284
            x.description,
285
            x.response,
286
            x.additionals,
287
            x.raw_score,
288
            x.max_score
289
          FROM {context} c
290
            INNER JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
291
            INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
292
            INNER JOIN {hvp} h ON h.id = cm.instance
293
            INNER JOIN {hvp_xapi_results} x ON x.content_id = h.id
294
          WHERE x.user_id = :userid
295
            AND c.id {$contextsql}
296
        ";
297
 
298
        $xapiparams = [
299
            'contextlevel' => CONTEXT_MODULE,
300
            'modname'      => 'hvp',
301
            'userid'       => $userid,
302
        ];
303
        $xapiparams += $contextparams;
304
 
305
        $xapiresult = $DB->get_recordset_sql($xapisql, $xapiparams);
306
        $xapi       = [];
307
        foreach ($xapiresult as $record) {
308
            $h5pxapi = (object) array();
309
            if (!empty($record->content_id)) {
310
                $h5pxapi->content_id = $record->content_id;
311
            }
312
 
313
            if (!empty($record->parent_id)) {
314
                $h5pxapi->parent_id = $record->parent_id;
315
            }
316
 
317
            if (!empty($record->description)) {
318
                $h5pxapi->description = $record->description;
319
            }
320
 
321
            if (!empty($record->response)) {
322
                $h5pxapi->response = $record->response;
323
            }
324
 
325
            if (!empty($record->additionals)) {
326
                $h5pxapi->additionals = $record->additionals;
327
            }
328
 
329
            if (!empty($record->raw_score)) {
330
                $h5pxapi->raw_score = $record->raw_score;
331
            }
332
 
333
            if (!empty($record->max_score)) {
334
                $h5pxapi->max_score = $record->max_score;
335
            }
336
 
337
            $xapi[$record->contextid][] = $h5pxapi;
338
        }
339
        $xapiresult->close();
340
 
341
        return $xapi;
342
    }
343
 
344
    /**
345
     * Get exportable H5P events from user id
346
     *
347
     * @param $userid
348
     *
349
     * @return object Exportable and writable H5P events
350
     * @throws \dml_exception
351
     */
352
    protected static function get_exportable_events($userid) {
353
        global $DB;
354
 
355
        $eventssql = "
356
          SELECT *
357
          FROM {hvp_events}
358
          WHERE user_id = :userid
359
        ";
360
 
361
        $eventsparams = [
362
            'userid' => $userid,
363
        ];
364
 
365
        $h5peventsresults = $DB->get_recordset_sql($eventssql, $eventsparams);
366
        $h5pevents        = (object) [
367
            'events' => [],
368
        ];
369
        foreach ($h5peventsresults as $event) {
370
            $h5pevents->events[] = $event;
371
        }
372
        $h5peventsresults->close();
373
 
374
        return $h5pevents;
375
    }
376
 
377
    /**
378
     * Delete all data for all users in the specified context.
379
     *
380
     * @param \context $context The specific context to delete data for.
381
     */
382
    public static function _delete_data_for_all_users_in_context(\context $context) {
383
        global $DB;
384
 
385
        if ($context->contextlevel == CONTEXT_USER) {
386
            // Delete all H5P events.
387
            $DB->delete_records('hvp_events');
388
            return;
389
        } else if ($context->contextlevel != CONTEXT_MODULE) {
390
            return;
391
        }
392
 
393
        $cm = get_coursemodule_from_id('hvp', $context->instanceid);
394
        if (!$cm) {
395
            return;
396
        }
397
 
398
        // Delete content user data.
399
        $DB->delete_records('hvp_content_user_data', [
400
            'hvp_id' => $cm->instance,
401
        ]);
402
 
403
        // Delete xAPI results.
404
        $DB->delete_records('hvp_xapi_results', [
405
            'content_id' => $cm->instance,
406
        ]);
407
    }
408
 
409
    /**
410
     * Delete all user data for the specified user, in the specified contexts.
411
     *
412
     * @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
413
     */
414
    public static function _delete_data_for_user(approved_contextlist $contextlist) {
415
        global $DB;
416
 
417
        $count = $contextlist->count();
418
        if (empty($count)) {
419
            return;
420
        }
421
 
422
        $userid = $contextlist->get_user()->id;
423
        foreach ($contextlist->get_contexts() as $context) {
424
            $cm = get_coursemodule_from_id('hvp', $context->instanceid);
425
 
426
            if (!$cm) {
427
                continue;
428
            }
429
 
430
            // Delete content user data.
431
            $DB->delete_records('hvp_content_user_data', [
432
                'hvp_id'  => $cm->instance,
433
                'user_id' => $userid,
434
            ]);
435
 
436
            // Delete xAPI results.
437
            $DB->delete_records('hvp_xapi_results', [
438
                'content_id' => $cm->instance,
439
                'user_id'    => $userid,
440
            ]);
441
        }
442
 
443
        // Delete H5P events.
444
        $DB->delete_records('hvp_events', [
445
            'user_id' => $userid,
446
        ]);
447
    }
448
 
449
    /**
450
     * Get the list of users who have data within a context.
451
     *
452
     * @param   userlist    $userlist   The userlist containing the list of users who have data in this context/plugin combination.
453
     */
454
    public static function get_users_in_context(userlist $userlist) {
455
        $context = $userlist->get_context();
456
        if (!is_a($context, \context_module::class)) {
457
            return;
458
        }
459
 
460
        $sql = "
461
          SELECT d.user_id
462
          FROM {course_modules} cm
463
            INNER JOIN {modules} m ON m.id = cm.module AND m.name = 'hvp'
464
            INNER JOIN {context} c ON c.instanceid = cm.id
465
            INNER JOIN {hvp} h ON h.id = cm.instance
466
            INNER JOIN {hvp_content_user_data} d ON h.id = d.hvp_id
467
          WHERE c.contextlevel = :contextlevel AND c.id = :contextid";
468
 
469
        $params = [
470
            'contextlevel' => CONTEXT_MODULE,
471
            'contextid'    => $context->id,
472
        ];
473
        $userlist->add_from_sql('user_id', $sql, $params);
474
 
475
        $sql = "
476
          SELECT
477
            x.user_id
478
          FROM {course_modules} cm
479
            INNER JOIN {modules} m ON m.id = cm.module AND m.name = 'hvp'
480
            INNER JOIN {context} c ON c.instanceid = cm.id
481
            INNER JOIN {hvp} h ON h.id = cm.instance
482
            INNER JOIN {hvp_xapi_results} x ON x.content_id = h.id
483
          WHERE c.contextlevel = :contextlevel AND c.id = :contextid";
484
        $userlist->add_from_sql('user_id', $sql, $params);
485
    }
486
 
487
    /**
488
     * Delete multiple users within a single context.
489
     *
490
     * @param   approved_userlist       $userlist The approved context and user information to delete information for.
491
     */
492
    public static function delete_data_for_users(approved_userlist $userlist) {
493
        global $DB;
494
        $context = $userlist->get_context();
495
        if (!is_a($context, \context_module::class)) {
496
            return;
497
        }
498
        $cm = get_coursemodule_from_id('hvp', $context->instanceid);
499
 
500
        // Prepare SQL to gather all completed IDs.
501
        $userids = $userlist->get_userids();
502
        list($insql, $inparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
503
 
504
        $inparams['hvpid'] = $cm->instance;
505
 
506
        $DB->delete_records_select(
507
            'hvp_content_user_data',
508
            "hvp_id = :hvpid AND user_id $insql",
509
            $inparams
510
        );
511
 
512
        $DB->delete_records_select(
513
            'hvp_xapi_results',
514
            "hvp_id = :hvpid AND user_id $insql",
515
            $inparams
516
        );
517
 
518
    }
519
}