MDL-62010 core_enrol: Add privacy implementation for core_enrol
[moodle.git] / enrol / classes / privacy / provider.php
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  * Privacy Subsystem implementation for core_enrol.
18  *
19  * @package    core_enrol
20  * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
21  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22  */
23 namespace core_enrol\privacy;
24 defined('MOODLE_INTERNAL') || die();
26 use core_privacy\local\metadata\collection;
27 use core_privacy\local\request\approved_contextlist;
28 use core_privacy\local\request\context;
29 use core_privacy\local\request\contextlist;
30 use core_privacy\local\request\transform;
31 use core_privacy\local\request\writer;
33 /**
34  * Privacy Subsystem for core_enrol implementing metadata and plugin providers.
35  *
36  * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
37  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38  */
39 class provider implements
40         \core_privacy\local\metadata\provider,
41         \core_privacy\local\request\subsystem\provider {
42     /**
43      * Returns meta data about this system.
44      *
45      * @param   collection $collection The initialised collection to add items to.
46      * @return  collection     A listing of user data stored through this system.
47      */
48     public static function get_metadata(collection $collection) : collection {
49         $collection->add_database_table(
50             'user_enrolments',
51             [
52                 'status' => 'privacy:metadata:user_enrolments:status',
53                 'enrolid' => 'privacy:metadata:user_enrolments:enrolid',
54                 'userid' => 'privacy:metadata:user_enrolments:userid',
55                 'timestart' => 'privacy:metadata:user_enrolments:timestart',
56                 'timeend' => 'privacy:metadata:user_enrolments:timeend',
57                 'modifierid' => 'privacy:metadata:user_enrolments:modifierid',
58                 'timecreated' => 'privacy:metadata:user_enrolments:timecreated',
59                 'timemodified' => 'privacy:metadata:user_enrolments:timemodified'
60             ],
61             'privacy:metadata:user_enrolments:tableexplanation'
62         );
64         return $collection;
65     }
66     /**
67      * Get the list of contexts that contain user information for the specified user.
68      *
69      * @param   int $userid The user to search.
70      * @return  contextlist   $contextlist  The contextlist containing the list of contexts used in this plugin.
71      */
72     public static function get_contexts_for_userid(int $userid) : contextlist {
73         $sql = "SELECT ctx.id
74                   FROM {user_enrolments} ue
75                   JOIN {enrol} e
76                     ON e.id = ue.enrolid
77                    AND ue.userid = :userid
78                   JOIN {context} ctx
79                     ON ctx.instanceid = e.courseid
80                    AND ctx.contextlevel = :contextlevel";
81         $params = [
82             'contextlevel' => CONTEXT_COURSE,
83             'userid'       => $userid
84         ];
85         $contextlist = new contextlist();
86         $contextlist->add_from_sql($sql, $params);
88         return $contextlist;
89     }
90     /**
91      * Export all user data for the specified user, in the specified contexts.
92      *
93      * @param   approved_contextlist $contextlist The approved contexts to export information for.
94      */
95     public static function export_user_data(approved_contextlist $contextlist) {
96         global $DB;
98         if (empty($contextlist->count())) {
99             return;
100         }
101         $userid = $contextlist->get_user()->id;
102         list($insql, $inparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
103         $params = [
104             'contextlevel' => CONTEXT_COURSE,
105             'userid' => $userid
106          ];
107         $params += $inparams;
108         $sql = "SELECT ue.id,
109                        ue.status,
110                        ue.timestart,
111                        ue.timeend,
112                        ue.timecreated,
113                        ue.timemodified,
114                        e.enrol,
115                        ctx.id as contextid
116                   FROM {user_enrolments} ue
117                   JOIN {enrol} e
118                     ON e.id = ue.enrolid
119                    AND ue.userid = :userid
120                   JOIN {context} ctx
121                     ON ctx.instanceid = e.courseid
122                    AND ctx.contextlevel = :contextlevel
123                  WHERE ctx.id $insql
124                  ORDER BY ctx.id, e.enrol";
125         $data = [];
126         $lastcontextid = null;
127         $lastenrol = null;
128         $path = [get_string('privacy:metadata:user_enrolments', 'core_enrol')];
129         $flush = function($lastcontextid, $lastenrol, $data) use ($path) {
130             $context = \context::instance_by_id($lastcontextid);
131             writer::with_context($context)->export_related_data(
132                 $path,
133                 $lastenrol,
134                 (object)$data
135             );
136         };
137         $userenrolments = $DB->get_recordset_sql($sql, $params);
138         foreach ($userenrolments as $userenrolment) {
139             if (($lastcontextid && $lastcontextid != $userenrolment->contextid) ||
140                     ($lastenrol && $lastenrol != $userenrolment->enrol)) {
141                 $flush($lastcontextid, $lastenrol, $data);
142                 $data = [];
143             }
144             $data[] = (object) [
145                 'status' => $userenrolment->status,
146                 'timecreated' => transform::datetime($userenrolment->timecreated),
147                 'timemodified' => transform::datetime($userenrolment->timemodified),
148                 'timestart' => transform::datetime($userenrolment->timestart),
149                 'timeend' => transform::datetime($userenrolment->timeend)
150             ];
151             $lastcontextid = $userenrolment->contextid;
152             $lastenrol = $userenrolment->enrol;
153         }
154         if (!empty($data)) {
155             $flush($lastcontextid, $lastenrol, $data);
156         }
157         $userenrolments->close();
158     }
159     /**
160      * Delete all data for all users in the specified context.
161      *
162      * @param   context $context The specific context to delete data for.
163      */
164     public static function delete_data_for_all_users_in_context(\context $context) {
165         global $DB;
167         if (empty($context)) {
168             return;
169         }
170         // Sanity check that context is at the User context level.
171         if ($context->contextlevel == CONTEXT_COURSE) {
172             $sql = "SELECT ue.id
173                       FROM {user_enrolments} ue
174                       JOIN {enrol} e
175                         ON e.id = ue.enrolid
176                       JOIN {context} ctx
177                         ON ctx.instanceid = e.courseid
178                      WHERE ctx.id = :contextid";
179             $params = ['contextid' => $context->id];
180             $enrolsids = $DB->get_fieldset_sql($sql, $params);
181             if (!empty($enrolsids)) {
182                 list($insql, $inparams) = $DB->get_in_or_equal($enrolsids, SQL_PARAMS_NAMED);
183                 static::delete_user_data($insql, $inparams);
184             }
185         }
186     }
187     /**
188      * Delete all user data for the specified user, in the specified contexts.
189      *
190      * @param   approved_contextlist $contextlist The approved contexts and user information to delete information for.
191      */
192     public static function delete_data_for_user(approved_contextlist $contextlist) {
193         global $DB;
195         if (empty($contextlist->count())) {
196             return;
197         }
198         $userid = $contextlist->get_user()->id;
199         list($insql, $inparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
200         $params = [
201             'contextlevel' => CONTEXT_COURSE,
202             'userid' => $userid
203          ];
204         $params += $inparams;
205         $sql = "SELECT ue.id
206                   FROM {user_enrolments} ue
207                   JOIN {enrol} e
208                     ON e.id = ue.enrolid
209                    AND ue.userid = :userid
210                   JOIN {context} ctx
211                     ON ctx.instanceid = e.courseid
212                    AND ctx.contextlevel = :contextlevel
213                  WHERE ctx.id $insql";
214         $enrolsids = $DB->get_fieldset_sql($sql, $params);
215         if (!empty($enrolsids)) {
216             list($insql, $inparams) = $DB->get_in_or_equal($enrolsids, SQL_PARAMS_NAMED);
217             static::delete_user_data($insql, $inparams);
218         }
219     }
221     /**
222      * Delete data from $tablename with the IDs returned by $sql query.
223      *
224      * @param  string $sql    SQL query for getting the IDs of the uer enrolments entries to delete.
225      * @param  array  $params SQL params for the query.
226      */
227     protected static function delete_user_data(string $sql, array $params) {
228         global $DB;
230         $DB->delete_records_select('user_enrolments', "id $sql", $params);
231     }