MDL-63664 tool_policy: Add support for removal of context users
[moodle.git] / admin / tool / policy / 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/>.
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  */
25 namespace tool_policy\privacy;
27 use core_privacy\local\metadata\collection;
28 use core_privacy\local\request\approved_contextlist;
29 use core_privacy\local\request\approved_userlist;
30 use core_privacy\local\request\contextlist;
31 use core_privacy\local\request\moodle_content_writer;
32 use core_privacy\local\request\userlist;
33 use core_privacy\local\request\transform;
34 use core_privacy\local\request\writer;
36 defined('MOODLE_INTERNAL') || die();
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  */
44 class provider implements
45         // This tool stores user data.
46         \core_privacy\local\metadata\provider,
48         // This plugin is capable of determining which users have data within it.
49         \core_privacy\local\request\core_userlist_provider,
51         // This tool may provide access to and deletion of user data.
52         \core_privacy\local\request\plugin\provider {
54     /**
55      * Return the fields which contain personal data.
56      *
57      * @param   collection $collection The initialised collection to add items to.
58      * @return  collection A listing of user data stored through this system.
59      */
60     public static function get_metadata(collection $collection) : collection {
61         $collection->add_database_table(
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         );
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         );
96         $collection->add_subsystem_link('core_files', [], 'privacy:metadata:subsystem:corefiles');
98         return $collection;
99     }
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 {
108         $contextlist = new contextlist();
110         // Policies a user has modified.
111         $sql = "SELECT c.id
112                   FROM {context} c
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);
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                    )";
130         $params = [
131             'contextlevel' => CONTEXT_USER,
132             'userid' => $userid,
133             'usermodified' => $userid,
134         ];
135         $contextlist->add_from_sql($sql, $params);
137         return $contextlist;
138     }
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();
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         }
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];
162             $userlist->add_from_sql('userid', $sql, $params);
163             $userlist->add_from_sql('usermodified', $sql, $params);
164         }
165     }
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) {
173         global $DB;
175         // Export user agreements.
176         foreach ($contextlist->get_contexts() as $context) {
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());
181             }
182         }
183     }
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.
189      * We never delete policy versions because they are part of privacy data.
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     }
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.
200      * We never delete policy versions because they are part of privacy data.
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     }
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     }
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;
226         $sysctx = \context_system::instance();
227         $fs = get_file_storage();
228         $agreementsql = "
229             SELECT
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,
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";
241         // Fetch all agreements related to this user.
242         $agreements = $DB->get_recordset_sql($agreementsql, [
243             'userid' => $context->instanceid,
244             'usermodified' => $context->instanceid,
245         ]);
247         $basecontext = [
248             get_string('privacyandpolicies', 'admin'),
249             get_string('useracceptances', 'tool_policy'),
250         ];
252         foreach ($agreements as $agreement) {
253             $subcontext = array_merge($basecontext, [get_string('policynamedversion', 'tool_policy', $agreement)]);
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,
272                 'isactive' => transform::yesno($agreement->versionid == $agreement->currentversionid),
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             ];
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             }
291         }
292         $agreements->close();
293     }
295     /**
296      * Export all policy agreements that the user authored.
297      *
298      * @param stdClass $user The user who has created the policies to export.
299      */
300     protected static function export_authored_policies(\stdClass $user) {
301         global $DB;
303         // Authored policies are exported against the system.
304         $context = \context_system::instance();
305         $basecontext = [
306             get_string('policydocuments', 'tool_policy'),
307         ];
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
321                  WHERE v.usermodified = :userid";
322         $versions = $DB->get_recordset_sql($sql, ['userid' => $user->id]);
323         foreach ($versions as $version) {
324             $subcontext = array_merge($basecontext, [get_string('policynamedversion', 'tool_policy', $version)]);
326             $versioncontent = (object) [
327                 'name' => $version->name,
328                 'revision' => $version->revision,
329                 'summary' => writer::with_context($context)->rewrite_pluginfile_urls(
330                     $subcontext,
331                     'tool_policy',
332                     'policydocumentsummary',
333                     $version->id,
334                     $version->summary
335                 ),
336                 'content' => writer::with_context($context)->rewrite_pluginfile_urls(
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),
345                 'createdbyme' => transform::yesno($version->usermodified == $user->id),
346                 'timecreated' => transform::datetime($version->timecreated),
347                 'timemodified' => transform::datetime($version->timemodified),
348             ];
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);
353         }
354         $versions->close();
355     }