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_tag. 19 * 20 * @package core_tag 21 * @copyright 2018 Zig Tan <zig@moodle.com> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace core_tag\privacy; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 use \core_privacy\local\metadata\collection; 30 use core_privacy\local\request\approved_contextlist; 31 use core_privacy\local\request\contextlist; 32 use core_privacy\local\request\transform; 33 use core_privacy\local\request\writer; 34 use core_privacy\local\request\userlist; 35 use core_privacy\local\request\approved_userlist; 36 37 /** 38 * Privacy Subsystem implementation for core_tag. 39 * 40 * @copyright 2018 Zig Tan <zig@moodle.com> 41 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 42 */ 43 class provider implements 44 // Tags store user data. 45 \core_privacy\local\metadata\provider, 46 47 // The tag subsystem provides data to other components. 48 \core_privacy\local\request\subsystem\plugin_provider, 49 50 // This plugin is capable of determining which users have data within it. 51 \core_privacy\local\request\core_userlist_provider, 52 53 // The tag subsystem may have data that belongs to this user. 54 \core_privacy\local\request\plugin\provider, 55 56 \core_privacy\local\request\shared_userlist_provider 57 { 58 59 /** 60 * Returns meta data about this system. 61 * 62 * @param collection $collection The initialised collection to add items to. 63 * @return collection A listing of user data stored through this system. 64 */ 65 public static function get_metadata(collection $collection) : collection { 66 // The table 'tag' contains data that a user has entered. 67 // It is currently linked with a userid, but this field will hopefulyl go away. 68 // Note: The userid is not necessarily 100% accurate. See MDL-61555. 69 $collection->add_database_table('tag', [ 70 'name' => 'privacy:metadata:tag:name', 71 'rawname' => 'privacy:metadata:tag:rawname', 72 'description' => 'privacy:metadata:tag:description', 73 'flag' => 'privacy:metadata:tag:flag', 74 'timemodified' => 'privacy:metadata:tag:timemodified', 75 'userid' => 'privacy:metadata:tag:userid', 76 ], 'privacy:metadata:tag'); 77 78 // The table 'tag_instance' contains user data. 79 // It links the user of a specific tag, to the item which is tagged. 80 // In some cases the userid who 'owns' the tag is also stored. 81 $collection->add_database_table('tag_instance', [ 82 'tagid' => 'privacy:metadata:taginstance:tagid', 83 'ordering' => 'privacy:metadata:taginstance:ordering', 84 'timecreated' => 'privacy:metadata:taginstance:timecreated', 85 'timemodified' => 'privacy:metadata:taginstance:timemodified', 86 'tiuserid' => 'privacy:metadata:taginstance:tiuserid', 87 ], 'privacy:metadata:taginstance'); 88 89 // The table 'tag_area' does not contain any specific user data. 90 // It links components and item types to collections and describes how they can be associated. 91 92 // The table 'tag_coll' does not contain any specific user data. 93 // It describes a list of tag collections configured by the administrator. 94 95 // The table 'tag_correlation' does not contain any user data. 96 // It is a cache for other data already stored. 97 98 return $collection; 99 } 100 101 /** 102 * Store all tags which match the specified component, itemtype, and itemid. 103 * 104 * In most situations you will want to specify $onlyuser as false. 105 * This will fetch only tags where the user themselves set the tag, or where tags are a shared resource. 106 * 107 * If you specify $onlyuser as true, only the tags created by that user will be included. 108 * 109 * @param int $userid The user whose information is to be exported 110 * @param \context $context The context to export for 111 * @param array $subcontext The subcontext within the context to export this information 112 * @param string $component The component to fetch data from 113 * @param string $itemtype The itemtype that the data was exported in within the component 114 * @param int $itemid The itemid within that tag 115 * @param bool $onlyuser Whether to only export ratings that the current user has made, or all tags 116 */ 117 public static function export_item_tags( 118 int $userid, 119 \context $context, 120 array $subcontext, 121 string $component, 122 string $itemtype, 123 int $itemid, 124 bool $onlyuser = false 125 ) { 126 global $DB; 127 128 // Ignore mdl_tag.userid here because it only reflects the user who originally created the tag. 129 $sql = "SELECT 130 t.rawname 131 FROM {tag} t 132 INNER JOIN {tag_instance} ti ON ti.tagid = t.id 133 WHERE ti.component = :component 134 AND ti.itemtype = :itemtype 135 AND ti.itemid = :itemid 136 "; 137 138 if ($onlyuser) { 139 $sql .= "AND ti.tiuserid = :userid"; 140 } else { 141 $sql .= "AND (ti.tiuserid = 0 OR ti.tiuserid = :userid)"; 142 } 143 144 $params = [ 145 'component' => $component, 146 'itemtype' => $itemtype, 147 'itemid' => $itemid, 148 'userid' => $userid, 149 ]; 150 151 if ($tags = $DB->get_fieldset_sql($sql, $params)) { 152 $writer = \core_privacy\local\request\writer::with_context($context) 153 ->export_related_data($subcontext, 'tags', $tags); 154 } 155 } 156 157 /** 158 * Deletes all tag instances for given context, component, itemtype, itemid 159 * 160 * In most situations you will want to specify $userid as null. Per-user tag instances 161 * are possible in Tags API, however there are no components or standard plugins that actually use them. 162 * 163 * @param \context $context The context to export for 164 * @param string $component Tagarea component 165 * @param string $itemtype Tagarea item type 166 * @param int $itemid The itemid within that component and itemtype (optional) 167 * @param int $userid Only delete tag instances made by this user, per-user tags must be enabled for the tagarea 168 */ 169 public static function delete_item_tags(\context $context, $component, $itemtype, 170 $itemid = null, $userid = null) { 171 global $DB; 172 $params = ['contextid' => $context->id, 'component' => $component, 'itemtype' => $itemtype]; 173 if ($itemid) { 174 $params['itemid'] = $itemid; 175 } 176 if ($userid) { 177 $params['tiuserid'] = $userid; 178 } 179 $DB->delete_records('tag_instance', $params); 180 } 181 182 /** 183 * Deletes all tag instances for given context, component, itemtype using subquery for itemids 184 * 185 * In most situations you will want to specify $userid as null. Per-user tag instances 186 * are possible in Tags API, however there are no components or standard plugins that actually use them. 187 * 188 * @param \context $context The context to export for 189 * @param string $component Tagarea component 190 * @param string $itemtype Tagarea item type 191 * @param string $itemidstest an SQL fragment that the itemid must match. Used 192 * in the query like WHERE itemid $itemidstest. Must use named parameters, 193 * and may not use named parameters called contextid, component or itemtype. 194 * @param array $params any query params used by $itemidstest. 195 */ 196 public static function delete_item_tags_select(\context $context, $component, $itemtype, 197 $itemidstest, $params = []) { 198 global $DB; 199 $params += ['contextid' => $context->id, 'component' => $component, 'itemtype' => $itemtype]; 200 $DB->delete_records_select('tag_instance', 201 'contextid = :contextid AND component = :component AND itemtype = :itemtype AND itemid ' . $itemidstest, 202 $params); 203 } 204 205 /** 206 * Get the list of contexts that contain user information for the specified user. 207 * 208 * @param int $userid The user to search. 209 * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. 210 */ 211 public static function get_contexts_for_userid(int $userid) : contextlist { 212 $contextlist = new contextlist(); 213 $contextlist->add_from_sql("SELECT c.id 214 FROM {context} c 215 JOIN {tag} t ON t.userid = :userid 216 WHERE contextlevel = :contextlevel", 217 ['userid' => $userid, 'contextlevel' => CONTEXT_SYSTEM]); 218 return $contextlist; 219 } 220 221 /** 222 * Get the list of users within a specific context. 223 * 224 * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. 225 */ 226 public static function get_users_in_context(userlist $userlist) { 227 $context = $userlist->get_context(); 228 229 if (!$context instanceof \context_system) { 230 return; 231 } 232 233 $sql = "SELECT userid 234 FROM {tag}"; 235 236 $userlist->add_from_sql('userid', $sql, []); 237 } 238 239 /** 240 * Export all user data for the specified user, in the specified contexts. 241 * 242 * @param approved_contextlist $contextlist The approved contexts to export information for. 243 */ 244 public static function export_user_data(approved_contextlist $contextlist) { 245 global $DB; 246 $context = \context_system::instance(); 247 if (!$contextlist->count() || !in_array($context->id, $contextlist->get_contextids())) { 248 return; 249 } 250 251 $user = $contextlist->get_user(); 252 $sql = "SELECT id, userid, tagcollid, name, rawname, isstandard, description, descriptionformat, flag, timemodified 253 FROM {tag} WHERE userid = ?"; 254 $rs = $DB->get_recordset_sql($sql, [$user->id]); 255 foreach ($rs as $record) { 256 $subcontext = [get_string('tags', 'tag'), $record->id]; 257 $tag = (object)[ 258 'id' => $record->id, 259 'userid' => transform::user($record->userid), 260 'name' => $record->name, 261 'rawname' => $record->rawname, 262 'isstandard' => transform::yesno($record->isstandard), 263 'description' => writer::with_context($context)->rewrite_pluginfile_urls($subcontext, 264 'tag', 'description', $record->id, strval($record->description)), 265 'descriptionformat' => $record->descriptionformat, 266 'flag' => $record->flag, 267 'timemodified' => transform::datetime($record->timemodified), 268 269 ]; 270 writer::with_context($context)->export_data($subcontext, $tag); 271 writer::with_context($context)->export_area_files($subcontext, 'tag', 'description', $record->id); 272 } 273 $rs->close(); 274 } 275 276 /** 277 * Delete all data for all users in the specified context. 278 * 279 * We do not delete tag instances in this method - this should be done by the components that define tagareas. 280 * We only delete tags themselves in case of system context. 281 * 282 * @param context $context The specific context to delete data for. 283 */ 284 public static function delete_data_for_all_users_in_context(\context $context) { 285 global $DB; 286 // Tags can only be defined in system context. 287 if ($context->id == \context_system::instance()->id) { 288 $DB->delete_records('tag_instance'); 289 $DB->delete_records('tag', []); 290 } 291 } 292 293 /** 294 * Delete multiple users within a single context. 295 * 296 * @param approved_userlist $userlist The approved context and user information to delete information for. 297 */ 298 public static function delete_data_for_users(approved_userlist $userlist) { 299 global $DB; 300 301 $context = $userlist->get_context(); 302 303 if ($context instanceof \context_system) { 304 // Do not delete tags themselves in case they are used by somebody else. 305 // If the user is the only one using the tag, it will be automatically deleted anyway during the 306 // next cron cleanup. 307 list($usersql, $userparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED); 308 $DB->set_field_select('tag', 'userid', 0, "userid {$usersql}", $userparams); 309 } 310 } 311 312 /** 313 * Delete all user data for the specified user, in the specified contexts. 314 * 315 * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. 316 */ 317 public static function delete_data_for_user(approved_contextlist $contextlist) { 318 global $DB; 319 $context = \context_system::instance(); 320 if (!$contextlist->count() || !in_array($context->id, $contextlist->get_contextids())) { 321 return; 322 } 323 324 // Do not delete tags themselves in case they are used by somebody else. 325 // If the user is the only one using the tag, it will be automatically deleted anyway during the next cron cleanup. 326 $DB->set_field_select('tag', 'userid', 0, 'userid = ?', [$contextlist->get_user()->id]); 327 } 328 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body