MDL-63664 tool_policy: Add support for removal of context users
[moodle.git] / admin / tool / policy / classes / privacy / provider.php
CommitLineData
24966a26
SA
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 tool_policy.
19 *
20 * @package tool_policy
21 * @copyright 2018 Sara Arjona <sara@moodle.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25namespace tool_policy\privacy;
26
27use core_privacy\local\metadata\collection;
28use core_privacy\local\request\approved_contextlist;
f6843ac9 29use core_privacy\local\request\approved_userlist;
24966a26 30use core_privacy\local\request\contextlist;
432a90f1 31use core_privacy\local\request\moodle_content_writer;
f6843ac9 32use core_privacy\local\request\userlist;
432a90f1
MG
33use core_privacy\local\request\transform;
34use core_privacy\local\request\writer;
24966a26
SA
35
36defined('MOODLE_INTERNAL') || die();
37
38/**
39 * Implementation of the privacy subsystem plugin provider for the policy tool.
40 *
41 * @copyright 2018 Sara Arjona <sara@moodle.com>
42 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
43 */
44class provider implements
45 // This tool stores user data.
46 \core_privacy\local\metadata\provider,
47
f6843ac9
MH
48 // This plugin is capable of determining which users have data within it.
49 \core_privacy\local\request\core_userlist_provider,
50
24966a26
SA
51 // This tool may provide access to and deletion of user data.
52 \core_privacy\local\request\plugin\provider {
53
54 /**
55 * Return the fields which contain personal data.
56 *
827c8e98
SA
57 * @param collection $collection The initialised collection to add items to.
58 * @return collection A listing of user data stored through this system.
24966a26 59 */
827c8e98
SA
60 public static function get_metadata(collection $collection) : collection {
61 $collection->add_database_table(
24966a26
SA
62 'tool_policy_acceptances',
63 [
64 'policyversionid' => 'privacy:metadata:acceptances:policyversionid',
65 'userid' => 'privacy:metadata:acceptances:userid',
66 'status' => 'privacy:metadata:acceptances:status',
67 'lang' => 'privacy:metadata:acceptances:lang',
68 'usermodified' => 'privacy:metadata:acceptances:usermodified',
69 'timecreated' => 'privacy:metadata:acceptances:timecreated',
70 'timemodified' => 'privacy:metadata:acceptances:timemodified',
71 'note' => 'privacy:metadata:acceptances:note',
72 ],
73 'privacy:metadata:acceptances'
74 );
75
827c8e98
SA
76 $collection->add_database_table(
77 'tool_policy_versions',
78 [
79 'name' => 'privacy:metadata:versions:name',
80 'type' => 'privacy:metadata:versions:type',
81 'audience' => 'privacy:metadata:versions:audience',
82 'archived' => 'privacy:metadata:versions:archived',
83 'usermodified' => 'privacy:metadata:versions:usermodified',
84 'timecreated' => 'privacy:metadata:versions:timecreated',
85 'timemodified' => 'privacy:metadata:versions:timemodified',
86 'policyid' => 'privacy:metadata:versions:policyid',
87 'revision' => 'privacy:metadata:versions:revision',
88 'summary' => 'privacy:metadata:versions:summary',
89 'summaryformat' => 'privacy:metadata:versions:summaryformat',
90 'content' => 'privacy:metadata:versions:content',
91 'contentformat' => 'privacy:metadata:versions:contentformat',
92 ],
93 'privacy:metadata:versions'
94 );
95
96 $collection->add_subsystem_link('core_files', [], 'privacy:metadata:subsystem:corefiles');
97
98 return $collection;
24966a26
SA
99 }
100
101 /**
102 * Get the list of contexts that contain user information for the specified user.
103 *
104 * @param int $userid The userid.
105 * @return contextlist The list of contexts containing user info for the user.
106 */
107 public static function get_contexts_for_userid(int $userid) : contextlist {
432a90f1 108 $contextlist = new contextlist();
827c8e98 109
f63745ea 110 // Policies a user has modified.
827c8e98
SA
111 $sql = "SELECT c.id
112 FROM {context} c
f63745ea
AN
113 JOIN {tool_policy_versions} v ON v.usermodified = :userid
114 WHERE c.contextlevel = :contextlevel";
115 $params = [
116 'contextlevel' => CONTEXT_SYSTEM,
117 'userid' => $userid,
118 ];
119 $contextlist->add_from_sql($sql, $params);
120
121 // Policies a user has accepted.
122 $sql = "SELECT c.id
123 FROM {context} c
124 JOIN {tool_policy_acceptances} a ON c.instanceid = a.userid
125 WHERE
126 c.contextlevel = :contextlevel
127 AND (
128 a.userid = :userid OR a.usermodified = :usermodified
129 )";
827c8e98
SA
130 $params = [
131 'contextlevel' => CONTEXT_USER,
f63745ea 132 'userid' => $userid,
827c8e98 133 'usermodified' => $userid,
827c8e98
SA
134 ];
135 $contextlist->add_from_sql($sql, $params);
136
432a90f1 137 return $contextlist;
24966a26
SA
138 }
139
f6843ac9
MH
140 /**
141 * Get the list of users who have data within a context.
142 *
143 * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
144 */
145 public static function get_users_in_context(userlist $userlist) {
146 $context = $userlist->get_context();
147
148 // Users that have modified any policies, if fetching for system context.
149 if (is_a($context, \context_system::class)) {
150 $sql = "SELECT v.usermodified AS userid
151 FROM {tool_policy_versions} v";
152 $userlist->add_from_sql('userid', $sql, []);
153 }
154
155 // Users that have accepted any policies, if fetching for user context.
156 if (is_a($context, \context_user::class)) {
157 $sql = "SELECT a.userid, a.usermodified
158 FROM {tool_policy_acceptances} a
159 WHERE a.userid = :instanceid";
160 $params = ['instanceid' => $context->instanceid];
161
162 $userlist->add_from_sql('userid', $sql, $params);
163 $userlist->add_from_sql('usermodified', $sql, $params);
164 }
165 }
166
24966a26
SA
167 /**
168 * Export personal data for the given approved_contextlist. User and context information is contained within the contextlist.
169 *
170 * @param approved_contextlist $contextlist A list of contexts approved for export.
171 */
172 public static function export_user_data(approved_contextlist $contextlist) {
432a90f1 173 global $DB;
827c8e98 174
827c8e98 175 // Export user agreements.
5e84f521 176 foreach ($contextlist->get_contexts() as $context) {
f63745ea
AN
177 if ($context->contextlevel == CONTEXT_USER) {
178 static::export_policy_agreements_for_context($context);
179 } else if ($context->contextlevel == CONTEXT_SYSTEM) {
180 static::export_authored_policies($contextlist->get_user());
432a90f1
MG
181 }
182 }
f63745ea
AN
183 }
184
185 /**
186 * Delete all data for all users in the specified context.
187 *
188 * We never delete user agreements to the policies because they are part of privacy data.
5e84f521 189 * We never delete policy versions because they are part of privacy data.
f63745ea
AN
190 *
191 * @param \context $context The context to delete in.
192 */
193 public static function delete_data_for_all_users_in_context(\context $context) {
194 }
195
196 /**
197 * Delete all user data for the specified user, in the specified contexts.
198 *
199 * We never delete user agreements to the policies because they are part of privacy data.
5e84f521 200 * We never delete policy versions because they are part of privacy data.
f63745ea
AN
201 *
202 * @param approved_contextlist $contextlist A list of contexts approved for deletion.
203 */
204 public static function delete_data_for_user(approved_contextlist $contextlist) {
205 }
206
f6843ac9
MH
207 /**
208 * Delete multiple users within a single context.
209 *
210 * We never delete user agreements to the policies because they are part of privacy data.
211 * We never delete policy versions because they are part of privacy data.
212 *
213 * @param approved_userlist $userlist The approved context and user information to delete information for.
214 */
215 public static function delete_data_for_users(approved_userlist $userlist) {
216 }
217
f63745ea
AN
218 /**
219 * Export all policy agreements relating to the specified user context.
220 *
221 * @param \context_user $context The context to export
222 */
223 protected static function export_policy_agreements_for_context(\context_user $context) {
224 global $DB;
225
5e84f521
SA
226 $sysctx = \context_system::instance();
227 $fs = get_file_storage();
f63745ea
AN
228 $agreementsql = "
229 SELECT
5e84f521
SA
230 a.id AS agreementid, a.userid, a.timemodified, a.note, a.status,
231 a.policyversionid AS versionid, a.usermodified, a.timecreated,
232 v.id, v.archived, v.name, v.revision,
f63745ea
AN
233 v.summary, v.summaryformat,
234 v.content, v.contentformat,
235 p.currentversionid
236 FROM {tool_policy_acceptances} a
237 JOIN {tool_policy_versions} v ON v.id = a.policyversionid
238 JOIN {tool_policy} p ON v.policyid = p.id
239 WHERE a.userid = :userid OR a.usermodified = :usermodified";
240
241 // Fetch all agreements related to this user.
242 $agreements = $DB->get_recordset_sql($agreementsql, [
243 'userid' => $context->instanceid,
244 'usermodified' => $context->instanceid,
245 ]);
246
247 $basecontext = [
248 get_string('privacyandpolicies', 'admin'),
249 get_string('useracceptances', 'tool_policy'),
250 ];
251
252 foreach ($agreements as $agreement) {
253 $subcontext = array_merge($basecontext, [get_string('policynamedversion', 'tool_policy', $agreement)]);
254
255 $summary = writer::with_context($context)->rewrite_pluginfile_urls(
256 $subcontext,
257 'tool_policy',
258 'policydocumentsummary',
259 $agreement->versionid,
260 $agreement->summary
261 );
262 $content = writer::with_context($context)->rewrite_pluginfile_urls(
263 $subcontext,
264 'tool_policy',
265 'policydocumentcontent',
266 $agreement->versionid,
267 $agreement->content
268 );
269 $agreementcontent = (object) [
270 'name' => $agreement->name,
271 'revision' => $agreement->revision,
5e84f521 272 'isactive' => transform::yesno($agreement->versionid == $agreement->currentversionid),
f63745ea
AN
273 'isagreed' => transform::yesno($agreement->status),
274 'agreedby' => transform::user($agreement->usermodified),
275 'timecreated' => transform::datetime($agreement->timecreated),
276 'timemodified' => transform::datetime($agreement->timemodified),
277 'note' => $agreement->note,
278 'summary' => format_text($summary, $agreement->summaryformat),
279 'content' => format_text($content, $agreement->contentformat),
280 ];
281
5e84f521
SA
282 writer::with_context($context)->export_data($subcontext, $agreementcontent);
283 // Manually export the files as they reside in the system context so we can't use
284 // the write's helper methods.
285 foreach ($fs->get_area_files($sysctx->id, 'tool_policy', 'policydocumentsummary', $agreement->versionid) as $file) {
286 writer::with_context($context)->export_file($subcontext, $file);
287 }
288 foreach ($fs->get_area_files($sysctx->id, 'tool_policy', 'policydocumentcontent', $agreement->versionid) as $file) {
289 writer::with_context($context)->export_file($subcontext, $file);
290 }
f63745ea
AN
291 }
292 $agreements->close();
293 }
827c8e98 294
f63745ea
AN
295 /**
296 * Export all policy agreements that the user authored.
5e84f521
SA
297 *
298 * @param stdClass $user The user who has created the policies to export.
f63745ea
AN
299 */
300 protected static function export_authored_policies(\stdClass $user) {
301 global $DB;
302
303 // Authored policies are exported against the system.
827c8e98 304 $context = \context_system::instance();
f63745ea
AN
305 $basecontext = [
306 get_string('policydocuments', 'tool_policy'),
827c8e98 307 ];
f63745ea 308
827c8e98
SA
309 $sql = "SELECT v.id,
310 v.name,
311 v.revision,
312 v.summary,
313 v.content,
314 v.archived,
315 v.usermodified,
316 v.timecreated,
317 v.timemodified,
318 p.currentversionid
319 FROM {tool_policy_versions} v
320 JOIN {tool_policy} p ON p.id = v.policyid
f63745ea
AN
321 WHERE v.usermodified = :userid";
322 $versions = $DB->get_recordset_sql($sql, ['userid' => $user->id]);
827c8e98 323 foreach ($versions as $version) {
f63745ea
AN
324 $subcontext = array_merge($basecontext, [get_string('policynamedversion', 'tool_policy', $version)]);
325
827c8e98
SA
326 $versioncontent = (object) [
327 'name' => $version->name,
328 'revision' => $version->revision,
f63745ea 329 'summary' => writer::with_context($context)->rewrite_pluginfile_urls(
827c8e98
SA
330 $subcontext,
331 'tool_policy',
332 'policydocumentsummary',
333 $version->id,
334 $version->summary
335 ),
f63745ea 336 'content' => writer::with_context($context)->rewrite_pluginfile_urls(
827c8e98
SA
337 $subcontext,
338 'tool_policy',
339 'policydocumentcontent',
340 $version->id,
341 $version->content
342 ),
343 'isactive' => transform::yesno($version->id == $version->currentversionid),
344 'isarchived' => transform::yesno($version->archived),
f63745ea 345 'createdbyme' => transform::yesno($version->usermodified == $user->id),
827c8e98
SA
346 'timecreated' => transform::datetime($version->timecreated),
347 'timemodified' => transform::datetime($version->timemodified),
348 ];
f63745ea
AN
349 writer::with_context($context)
350 ->export_data($subcontext, $versioncontent)
351 ->export_area_files($subcontext, 'tool_policy', 'policydocumentsummary', $version->id)
352 ->export_area_files($subcontext, 'tool_policy', 'policydocumentcontent', $version->id);
827c8e98 353 }
f63745ea 354 $versions->close();
24966a26
SA
355 }
356}