MDL-63702 core_block: Update to use new userlist interface.
[moodle.git] / blocks / 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  * Data provider.
19  *
20  * @package    core_block
21  * @copyright  2018 Frédéric Massart
22  * @author     Frédéric Massart <fred@branchup.tech>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 namespace core_block\privacy;
27 defined('MOODLE_INTERNAL') || die();
29 use context;
30 use context_block;
31 use core_privacy\local\metadata\collection;
32 use core_privacy\local\request\approved_contextlist;
33 use core_privacy\local\request\transform;
34 use core_privacy\local\request\writer;
36 /**
37  * Data provider class.
38  *
39  * @package    core_block
40  * @copyright  2018 Frédéric Massart
41  * @author     Frédéric Massart <fred@branchup.tech>
42  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
43  */
44 class provider implements
45     \core_privacy\local\metadata\provider,
46     \core_privacy\local\request\subsystem\provider,
47     \core_privacy\local\request\user_preference_provider,
48     \core_privacy\local\request\core_userlist_provider {
50     /**
51      * Returns metadata.
52      *
53      * @param collection $collection The initialised collection to add items to.
54      * @return collection A listing of user data stored through this system.
55      */
56     public static function get_metadata(collection $collection) : collection {
57         $collection->add_user_preference('blockIDhidden', 'privacy:metadata:userpref:hiddenblock');
58         $collection->add_user_preference('docked_block_instance_ID', 'privacy:metadata:userpref:dockedinstance');
59         return $collection;
60     }
62     /**
63      * Get the list of contexts that contain user information for the specified user.
64      *
65      * @param int $userid The user to search.
66      * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
67      */
68     public static function get_contexts_for_userid(int $userid) : \core_privacy\local\request\contextlist {
69         global $DB;
70         $contextlist = new \core_privacy\local\request\contextlist();
72         // Fetch the block instance IDs.
73         $likehidden = $DB->sql_like('name', ':hidden', false, false);
74         $likedocked = $DB->sql_like('name', ':docked', false, false);
75         $sql = "userid = :userid AND ($likehidden OR $likedocked)";
76         $params = [
77             'userid' => $userid,
78             'hidden' => 'block%hidden',
79             'docked' => 'docked_block_instance_%',
80         ];
81         $prefs = $DB->get_fieldset_select('user_preferences', 'name', $sql, $params);
83         $instanceids = array_unique(array_map(function($prefname) {
84             if (preg_match('/^block(\d+)hidden$/', $prefname, $matches)) {
85                 return $matches[1];
86             } else if (preg_match('/^docked_block_instance_(\d+)$/', $prefname, $matches)) {
87                 return $matches[1];
88             }
89             return 0;
90         }, $prefs));
92         // Find the context of the instances.
93         if (!empty($instanceids)) {
94             list($insql, $inparams) = $DB->get_in_or_equal($instanceids, SQL_PARAMS_NAMED);
95             $sql = "
96                 SELECT ctx.id
97                   FROM {context} ctx
98                  WHERE ctx.instanceid $insql
99                    AND ctx.contextlevel = :blocklevel";
100             $params = array_merge($inparams, ['blocklevel' => CONTEXT_BLOCK]);
101             $contextlist->add_from_sql($sql, $params);
102         }
104         return $contextlist;
105     }
107     /**
108      * Get the list of contexts that contain user information for the specified user.
109      *
110      * @param   \core_privacy\local\request\userlist    $userlist   The userlist containing the list of users who have data in this context/plugin combination.
111      */
112     public static function get_users_in_context(\core_privacy\local\request\userlist $userlist) {
113         global $DB;
115         $context = $userlist->get_context();
116         if ($context->contextlevel != CONTEXT_BLOCK) {
117             return;
118         }
120         $params = ['docked' => 'docked_block_instance_' . $context->instanceid,
121                    'hidden' => 'block' . $context->instanceid . 'hidden'];
123         $sql = "SELECT userid
124                   FROM {user_preferences}
125                  WHERE name = :hidden OR name = :docked";
126         $userlist->add_from_sql('userid', $sql, $params);
127     }
129     /**
130      * Export all user data for the specified user, in the specified contexts.
131      *
132      * @param approved_contextlist $contextlist The approved contexts to export information for.
133      */
134     public static function export_user_data(approved_contextlist $contextlist) {
135         global $DB;
136         $userid = $contextlist->get_user()->id;
138         // Extract the block instance IDs.
139         $instanceids = array_reduce($contextlist->get_contexts(), function($carry, $context) {
140             if ($context->contextlevel == CONTEXT_BLOCK) {
141                 $carry[] = $context->instanceid;
142             }
143             return $carry;
144         }, []);
145         if (empty($instanceids)) {
146             return;
147         }
149         // Query the blocks and their preferences.
150         list($insql, $inparams) = $DB->get_in_or_equal($instanceids, SQL_PARAMS_NAMED);
151         $hiddenkey = $DB->sql_concat("'block'", 'bi.id', "'hidden'");
152         $dockedkey = $DB->sql_concat("'docked_block_instance_'", 'bi.id');
153         $sql = "
154             SELECT bi.id, h.value AS prefhidden, d.value AS prefdocked
155               FROM {block_instances} bi
156          LEFT JOIN {user_preferences} h
157                 ON h.userid = :userid1
158                AND h.name = $hiddenkey
159          LEFT JOIN {user_preferences} d
160                 ON d.userid = :userid2
161                AND d.name = $dockedkey
162              WHERE bi.id $insql
163                AND (h.id IS NOT NULL
164                 OR d.id IS NOT NULL)";
165         $params = array_merge($inparams, [
166             'userid1' => $userid,
167             'userid2' => $userid,
168         ]);
170         // Export all the things.
171         $dockedstr = get_string('privacy:request:blockisdocked', 'core_block');
172         $hiddenstr = get_string('privacy:request:blockishidden', 'core_block');
173         $recordset = $DB->get_recordset_sql($sql, $params);
174         foreach ($recordset as $record) {
175             $context = context_block::instance($record->id);
176             if ($record->prefdocked !== null) {
177                 writer::with_context($context)->export_user_preference(
178                     'core_block',
179                     'block_is_docked',
180                     transform::yesno($record->prefdocked),
181                     $dockedstr
182                 );
183             }
184             if ($record->prefhidden !== null) {
185                 writer::with_context($context)->export_user_preference(
186                     'core_block',
187                     'block_is_hidden',
188                     transform::yesno($record->prefhidden),
189                     $hiddenstr
190                 );
191             }
192         }
193         $recordset->close();
194     }
196     /**
197      * Export all user preferences for the plugin.
198      *
199      * @param int $userid The userid of the user whose data is to be exported.
200      */
201     public static function export_user_preferences(int $userid) {
202       // Our preferences aren't site-wide so they are exported in export_user_data.
203     }
205     /**
206      * Delete all data for all users in the specified context.
207      *
208      * @param context $context The specific context to delete data for.
209      */
210     public static function delete_data_for_all_users_in_context(context $context) {
211         global $DB;
212         if ($context->contextlevel != CONTEXT_BLOCK) {
213             return;
214         }
216         // Delete the user preferences.
217         $instanceid = $context->instanceid;
218         $DB->delete_records_list('user_preferences', 'name', [
219             "block{$instanceid}hidden",
220             "docked_block_instance_{$instanceid}"
221         ]);
222     }
224     /**
225      * Delete all user data for the specified user, in the specified contexts.
226      *
227      * @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
228      */
229     public static function delete_data_for_user(approved_contextlist $contextlist) {
230         global $DB;
231         $userid = $contextlist->get_user()->id;
232         $prefnames = array_reduce($contextlist->get_contexts(), function($carry, $context) {
233             if ($context->contextlevel == CONTEXT_BLOCK) {
234                 $carry[] = "block{$context->instanceid}hidden";
235                 $carry[] = "docked_block_instance_{$context->instanceid}";
236             }
237             return $carry;
238         }, []);
240         if (empty($prefnames)) {
241             return;
242         }
244         list($insql, $inparams) = $DB->get_in_or_equal($prefnames, SQL_PARAMS_NAMED);
245         $sql = "userid = :userid AND name $insql";
246         $params = array_merge($inparams, ['userid' => $userid]);
247         $DB->delete_records_select('user_preferences', $sql, $params);
248     }
251     /**
252      * Delete multiple users within a single context.
253      *
254      * @param \core_privacy\local\request\approved_userlist $userlist The approved context and user information to delete
255      * information for.
256      */
257     public static function delete_data_for_users(\core_privacy\local\request\approved_userlist $userlist) {
258         global $DB;
259         $context = $userlist->get_context();
260         if ($context->contextlevel != CONTEXT_BLOCK) {
261             return;
262         }
264         list($insql, $params) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
265         $params['hidden'] = 'block' . $context->instanceid . 'hidden';
266         $params['docked'] = 'docked_block_instance_' . $context->instanceid;
268         $DB->delete_records_select('user_preferences', "(name = :hidden OR name = :docked) AND userid $insql", $params);
269     }