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 core_backup.
19
 *
20
 * @package    core_backup
21
 * @copyright  2018 Mark Nelson <markn@moodle.com>
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace core_backup\privacy;
26
 
27
use core_privacy\local\metadata\collection;
28
use core_privacy\local\request\approved_contextlist;
29
use core_privacy\local\request\contextlist;
30
use core_privacy\local\request\transform;
31
use core_privacy\local\request\writer;
32
use core_privacy\local\request\userlist;
33
use core_privacy\local\request\approved_userlist;
34
 
35
defined('MOODLE_INTERNAL') || die();
36
 
37
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
38
 
39
/**
40
 * Privacy Subsystem implementation for core_backup.
41
 *
42
 * @copyright  2018 Mark Nelson <markn@moodle.com>
43
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44
 */
45
class provider implements
46
    \core_privacy\local\metadata\provider,
47
    \core_privacy\local\request\core_userlist_provider,
48
    \core_privacy\local\request\subsystem\provider {
49
 
50
    /**
51
     * Return the fields which contain personal data.
52
     *
53
     * @param collection $items a reference to the collection to use to store the metadata.
54
     * @return collection the updated collection of metadata items.
55
     */
56
    public static function get_metadata(collection $items): collection {
57
        $items->link_external_location(
58
            'Backup',
59
            [
60
                'detailsofarchive' => 'privacy:metadata:backup:detailsofarchive'
61
            ],
62
            'privacy:metadata:backup:externalpurpose'
63
        );
64
 
65
        $items->add_database_table(
66
            'backup_controllers',
67
            [
68
                'operation' => 'privacy:metadata:backup_controllers:operation',
69
                'type' => 'privacy:metadata:backup_controllers:type',
70
                'itemid' => 'privacy:metadata:backup_controllers:itemid',
71
                'timecreated' => 'privacy:metadata:backup_controllers:timecreated',
72
                'timemodified' => 'privacy:metadata:backup_controllers:timemodified'
73
            ],
74
            'privacy:metadata:backup_controllers'
75
        );
76
 
77
        return $items;
78
    }
79
 
80
    /**
81
     * Get the list of contexts that contain user information for the specified user.
82
     *
83
     * @param int $userid The user to search.
84
     * @return contextlist The contextlist containing the list of contexts used in this plugin.
85
     */
86
    public static function get_contexts_for_userid(int $userid): contextlist {
87
        $contextlist = new contextlist();
88
 
89
        $sql = "SELECT ctx.id
90
                  FROM {backup_controllers} bc
91
                  JOIN {context} ctx
92
                        ON ctx.instanceid = bc.itemid
93
                       AND ctx.contextlevel = :contextlevel
94
                       AND bc.type = :type
95
                 WHERE bc.userid = :userid";
96
        $params = [
97
            'contextlevel' => CONTEXT_COURSE,
98
            'userid' => $userid,
99
            'type' => 'course',
100
        ];
101
        $contextlist->add_from_sql($sql, $params);
102
 
103
        $sql = "SELECT ctx.id
104
                  FROM {backup_controllers} bc
105
                  JOIN {course_sections} c
106
                        ON bc.itemid = c.id
107
                       AND bc.type = :type
108
                  JOIN {context} ctx
109
                        ON ctx.instanceid = c.course
110
                       AND ctx.contextlevel = :contextlevel
111
                 WHERE bc.userid = :userid";
112
        $params = [
113
            'contextlevel' => CONTEXT_COURSE,
114
            'userid' => $userid,
115
            'type' => 'section',
116
        ];
117
        $contextlist->add_from_sql($sql, $params);
118
 
119
        $sql = "SELECT ctx.id
120
                  FROM {backup_controllers} bc
121
                  JOIN {context} ctx
122
                        ON ctx.instanceid = bc.itemid
123
                       AND ctx.contextlevel = :contextlevel
124
                       AND bc.type = :type
125
                 WHERE bc.userid = :userid";
126
        $params = [
127
            'contextlevel' => CONTEXT_MODULE,
128
            'userid' => $userid,
129
            'type' => 'activity',
130
        ];
131
        $contextlist->add_from_sql($sql, $params);
132
 
133
        return $contextlist;
134
    }
135
 
136
    /**
137
     * Get the list of users within a specific context.
138
     *
139
     * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
140
     */
141
    public static function get_users_in_context(userlist $userlist) {
142
        $context = $userlist->get_context();
143
 
144
        if ($context instanceof \context_course) {
145
            $params = ['courseid' => $context->instanceid];
146
 
147
            $sql = "SELECT bc.userid
148
                      FROM {backup_controllers} bc
149
                     WHERE bc.itemid = :courseid
150
                           AND bc.type = :typecourse";
151
 
152
            $courseparams = ['typecourse' => 'course'] + $params;
153
 
154
            $userlist->add_from_sql('userid', $sql, $courseparams);
155
 
156
            $sql = "SELECT bc.userid
157
                      FROM {backup_controllers} bc
158
                      JOIN {course_sections} c
159
                           ON bc.itemid = c.id
160
                     WHERE c.course = :courseid
161
                           AND bc.type = :typesection";
162
 
163
            $sectionparams = ['typesection' => 'section'] + $params;
164
 
165
            $userlist->add_from_sql('userid', $sql, $sectionparams);
166
        }
167
 
168
        if ($context instanceof \context_module) {
169
            $params = [
170
                'cmid' => $context->instanceid,
171
                'typeactivity' => 'activity'
172
            ];
173
 
174
            $sql = "SELECT bc.userid
175
                      FROM {backup_controllers} bc
176
                     WHERE bc.itemid = :cmid
177
                           AND bc.type = :typeactivity";
178
 
179
            $userlist->add_from_sql('userid', $sql, $params);
180
        }
181
    }
182
 
183
    /**
184
     * Export all user data for the specified user, in the specified contexts.
185
     *
186
     * @param approved_contextlist $contextlist The approved contexts to export information for.
187
     */
188
    public static function export_user_data(approved_contextlist $contextlist) {
189
        global $DB;
190
 
191
        if (empty($contextlist->count())) {
192
            return;
193
        }
194
 
195
        $user = $contextlist->get_user();
196
 
197
        list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
198
 
199
        $sql = "SELECT bc.*
200
                  FROM {backup_controllers} bc
201
                  JOIN {context} ctx
202
                    ON ctx.instanceid = bc.itemid AND ctx.contextlevel = :contextlevel
203
                 WHERE ctx.id {$contextsql}
204
                   AND bc.userid = :userid
205
              ORDER BY bc.timecreated ASC";
206
        $params = ['contextlevel' => CONTEXT_COURSE, 'userid' => $user->id] + $contextparams;
207
        $backupcontrollers = $DB->get_recordset_sql($sql, $params);
208
        self::recordset_loop_and_export($backupcontrollers, 'itemid', [], function($carry, $record) {
209
            $carry[] = [
210
                'operation' => $record->operation,
211
                'type' => $record->type,
212
                'itemid' => $record->itemid,
213
                'timecreated' => transform::datetime($record->timecreated),
214
                'timemodified' => transform::datetime($record->timemodified),
215
            ];
216
            return $carry;
217
        }, function($courseid, $data) {
218
            $context = \context_course::instance($courseid);
219
            $finaldata = (object) $data;
220
            writer::with_context($context)->export_data([get_string('backup'), $courseid], $finaldata);
221
        });
222
    }
223
 
224
    /**
225
     * Delete all user data which matches the specified context.
226
     * Only dealing with the specific context - not it's child contexts.
227
     *
228
     * @param \context $context A user context.
229
     */
230
    public static function delete_data_for_all_users_in_context(\context $context) {
231
        global $DB;
232
 
233
        if ($context instanceof \context_course) {
234
            $sectionsql = "itemid IN (SELECT id FROM {course_sections} WHERE course = ?) AND type = ?";
235
            $DB->delete_records_select('backup_controllers', $sectionsql, [$context->instanceid, \backup::TYPE_1SECTION]);
236
            $DB->delete_records('backup_controllers', ['itemid' => $context->instanceid, 'type' => \backup::TYPE_1COURSE]);
237
        }
238
        if ($context instanceof \context_module) {
239
            $DB->delete_records('backup_controllers', ['itemid' => $context->instanceid, 'type' => \backup::TYPE_1ACTIVITY]);
240
        }
241
        return;
242
    }
243
 
244
    /**
245
     * Delete multiple users within a single context.
246
     * Only dealing with the specific context - not it's child contexts.
247
     *
248
     * @param approved_userlist $userlist The approved context and user information to delete information for.
249
     */
250
    public static function delete_data_for_users(approved_userlist $userlist) {
251
        global $DB;
252
 
253
        if (empty($userlist->get_userids())) {
254
            return;
255
        }
256
 
257
        $context = $userlist->get_context();
258
        if ($context instanceof \context_course) {
259
            list($usersql, $userparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
260
            $select = "itemid = :itemid AND userid {$usersql} AND type = :type";
261
            $params = $userparams;
262
            $params['itemid'] = $context->instanceid;
263
            $params['type'] = \backup::TYPE_1COURSE;
264
 
265
            $DB->delete_records_select('backup_controllers', $select, $params);
266
 
267
            $params = $userparams;
268
            $params['course'] = $context->instanceid;
269
            $params['type'] = \backup::TYPE_1SECTION;
270
            $sectionsql = "itemid IN (SELECT id FROM {course_sections} WHERE course = :course)";
271
            $select = $sectionsql . " AND userid {$usersql} AND type = :type";
272
            $DB->delete_records_select('backup_controllers', $select, $params);
273
        }
274
        if ($context instanceof \context_module) {
275
            list($usersql, $userparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
276
            $select = "itemid = :itemid AND userid {$usersql} AND type = :type";
277
            $params = $userparams;
278
            $params['itemid'] = $context->instanceid;
279
            $params['type'] = \backup::TYPE_1ACTIVITY;
280
 
281
            // Delete activity backup data.
282
            $select = "itemid = :itemid AND type = :type AND userid {$usersql}";
283
            $params = ['itemid' => $context->instanceid, 'type' => 'activity'] + $userparams;
284
            $DB->delete_records_select('backup_controllers', $select, $params);
285
        }
286
    }
287
 
288
    /**
289
     * Delete all user data for the specified user, in the specified contexts.
290
     * Only dealing with the specific context - not it's child contexts.
291
     *
292
     * @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
293
     */
294
    public static function delete_data_for_user(approved_contextlist $contextlist) {
295
        global $DB;
296
 
297
        if (empty($contextlist->count())) {
298
            return;
299
        }
300
 
301
        $userid = $contextlist->get_user()->id;
302
        foreach ($contextlist->get_contexts() as $context) {
303
            if ($context instanceof \context_course) {
304
                $select = "itemid = :itemid AND userid = :userid AND type = :type";
305
                $params = [
306
                    'userid' => $userid,
307
                    'itemid' => $context->instanceid,
308
                    'type' => \backup::TYPE_1COURSE
309
                ];
310
 
311
                $DB->delete_records_select('backup_controllers', $select, $params);
312
 
313
                $params = [
314
                    'userid' => $userid,
315
                    'course' => $context->instanceid,
316
                    'type' => \backup::TYPE_1SECTION
317
                ];
318
                $sectionsql = "itemid IN (SELECT id FROM {course_sections} WHERE course = :course)";
319
                $select = $sectionsql . " AND userid = :userid AND type = :type";
320
                $DB->delete_records_select('backup_controllers', $select, $params);
321
            }
322
            if ($context instanceof \context_module) {
323
                list($usersql, $userparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
324
                $select = "itemid = :itemid AND userid = :userid AND type = :type";
325
                $params = [
326
                    'itemid' => $context->instanceid,
327
                    'userid' => $userid,
328
                    'type' => \backup::TYPE_1ACTIVITY
329
                ];
330
 
331
                $DB->delete_records_select('backup_controllers', $select, $params);
332
            }
333
 
334
        }
335
    }
336
 
337
    /**
338
     * Loop and export from a recordset.
339
     *
340
     * @param \moodle_recordset $recordset The recordset.
341
     * @param string $splitkey The record key to determine when to export.
342
     * @param mixed $initial The initial data to reduce from.
343
     * @param callable $reducer The function to return the dataset, receives current dataset, and the current record.
344
     * @param callable $export The function to export the dataset, receives the last value from $splitkey and the dataset.
345
     * @return void
346
     */
347
    protected static function recordset_loop_and_export(\moodle_recordset $recordset, $splitkey, $initial,
348
            callable $reducer, callable $export) {
349
        $data = $initial;
350
        $lastid = null;
351
 
352
        foreach ($recordset as $record) {
353
            if ($lastid && $record->{$splitkey} != $lastid) {
354
                $export($lastid, $data);
355
                $data = $initial;
356
            }
357
            $data = $reducer($data, $record);
358
            $lastid = $record->{$splitkey};
359
        }
360
        $recordset->close();
361
 
362
        if (!empty($lastid)) {
363
            $export($lastid, $data);
364
        }
365
    }
366
}