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 * This file contains the core_privacy\local\request helper. 19 * 20 * @package core_privacy 21 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk> 22 * 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25namespace core_privacy\local\request; 26 27use \core_privacy\local\request\writer; 28 29defined('MOODLE_INTERNAL') || die(); 30 31require_once($CFG->libdir . '/modinfolib.php'); 32require_once($CFG->dirroot . '/course/modlib.php'); 33 34/** 35 * The core_privacy\local\request\helper class with useful shared functionality. 36 * 37 * @package core_privacy 38 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk> 39 * 40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 41 */ 42class helper { 43 44 /** 45 * Add core-controlled contexts which are related to a component but that component may know about. 46 * 47 * For example, most activities are not aware of activity completion, but the course implements it for them. 48 * These should be included. 49 * 50 * @param int $userid The user being added for. 51 * @param contextlist $contextlist The contextlist being appended to. 52 * @return contextlist The final contextlist 53 */ 54 public static function add_shared_contexts_to_contextlist_for(int $userid, contextlist $contextlist) : contextlist { 55 if (strpos($contextlist->get_component(), 'mod_') === 0) { 56 // Activity modules support data stored by core about them - for example, activity completion. 57 $contextlist = static::add_shared_contexts_to_contextlist_for_course_module($userid, $contextlist); 58 } 59 60 return $contextlist; 61 } 62 63 /** 64 * Add core-controlled contexts which are related to a component but that component may know about. 65 * 66 * For example, most activities are not aware of activity completion, but the course implements it for them. 67 * These should be included. 68 * 69 * @param \core_privacy\local\request\userlist $userlist 70 * @return contextlist The final contextlist 71 */ 72 public static function add_shared_users_to_userlist(\core_privacy\local\request\userlist $userlist) { 73 } 74 75 /** 76 * Handle export of standard data for a plugin which implements the null provider and does not normally store data 77 * of its own. 78 * 79 * This is used in cases such as activities like mod_resource, which do not store their own data, but may still have 80 * data on them (like Activity Completion). 81 * 82 * Any context provided in a contextlist should have base data exported as a minimum. 83 * 84 * @param approved_contextlist $contextlist The approved contexts to export information for. 85 */ 86 public static function export_data_for_null_provider(approved_contextlist $contextlist) { 87 $user = $contextlist->get_user(); 88 foreach ($contextlist as $context) { 89 $data = static::get_context_data($context, $user); 90 static::export_context_files($context, $user); 91 92 writer::with_context($context)->export_data([], $data); 93 } 94 } 95 96 /** 97 * Handle removal of 'standard' data for any plugin. 98 * 99 * This will handle deletion for things such as activity completion. 100 * 101 * @param string $component The component being deleted for. 102 * @param context $context The specific context to delete data for. 103 */ 104 public static function delete_data_for_all_users_in_context(string $component, \context $context) { 105 // Activity modules support data stored by core about them - for example, activity completion. 106 static::delete_data_for_all_users_in_context_course_module($component, $context); 107 } 108 109 /** 110 * Delete all 'standard' user data for the specified user, in the specified contexts. 111 * 112 * This will handle deletion for things such as activity completion. 113 * 114 * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. 115 */ 116 public static function delete_data_for_user(approved_contextlist $contextlist) { 117 $component = $contextlist->get_component(); 118 119 // Activity modules support data stored by core about them - for example, activity completion. 120 static::delete_data_for_user_in_course_module($contextlist); 121 } 122 123 /** 124 * Get all general data for this context. 125 * 126 * @param \context $context The context to retrieve data for. 127 * @param \stdClass $user The user being written. 128 * @return \stdClass 129 */ 130 public static function get_context_data(\context $context, \stdClass $user) : \stdClass { 131 global $DB; 132 133 $basedata = (object) []; 134 if ($context instanceof \context_module) { 135 return static::get_context_module_data($context, $user); 136 } 137 if ($context instanceof \context_block) { 138 return static::get_context_block_data($context, $user); 139 } 140 141 return $basedata; 142 } 143 144 /** 145 * Export all files for this context. 146 * 147 * @param \context $context The context to export files for. 148 * @param \stdClass $user The user being written. 149 * @return \stdClass 150 */ 151 public static function export_context_files(\context $context, \stdClass $user) { 152 if ($context instanceof \context_module) { 153 return static::export_context_module_files($context, $user); 154 } 155 } 156 157 /** 158 * Add core-controlled contexts which are related to a component but that component may know about. 159 * 160 * For example, most activities are not aware of activity completion, but the course implements it for them. 161 * These should be included. 162 * 163 * @param int $userid The user being added for. 164 * @param contextlist $contextlist The contextlist being appended to. 165 * @return contextlist The final contextlist 166 */ 167 protected static function add_shared_contexts_to_contextlist_for_course_module(int $userid, contextlist $contextlist) : contextlist { 168 // Fetch all contexts where the user has activity completion enabled. 169 $sql = "SELECT 170 c.id 171 FROM {course_modules_completion} cmp 172 INNER JOIN {course_modules} cm ON cm.id = cmp.coursemoduleid 173 INNER JOIN {modules} m ON m.id = cm.module 174 INNER JOIN {context} c ON c.instanceid = cm.id AND c.contextlevel = :contextlevel 175 WHERE cmp.userid = :userid 176 AND m.name = :modname"; 177 $params = [ 178 'userid' => $userid, 179 // Strip the mod_ from the name. 180 'modname' => substr($contextlist->get_component(), 4), 181 'contextlevel' => CONTEXT_MODULE, 182 ]; 183 184 $contextlist->add_from_sql($sql, $params); 185 186 return $contextlist; 187 } 188 189 /** 190 * Get all general data for the activity module at this context. 191 * 192 * @param \context_module $context The context to retrieve data for. 193 * @param \stdClass $user The user being written. 194 * @return \stdClass 195 */ 196 protected static function get_context_module_data(\context_module $context, \stdClass $user) : \stdClass { 197 global $DB; 198 199 $coursecontext = $context->get_course_context(); 200 $modinfo = get_fast_modinfo($coursecontext->instanceid); 201 $cm = $modinfo->cms[$context->instanceid]; 202 $component = "mod_{$cm->modname}"; 203 $course = $cm->get_course(); 204 $moduledata = $DB->get_record($cm->modname, ['id' => $cm->instance]); 205 206 $basedata = (object) [ 207 'name' => $cm->get_formatted_name(), 208 ]; 209 210 if (plugin_supports('mod', $cm->modname, FEATURE_MOD_INTRO, true)) { 211 $intro = $moduledata->intro; 212 213 $intro = writer::with_context($context) 214 ->rewrite_pluginfile_urls([], $component, 'intro', 0, $intro); 215 216 $options = [ 217 'noclean' => true, 218 'para' => false, 219 'context' => $context, 220 'overflowdiv' => true, 221 ]; 222 $basedata->intro = format_text($intro, $moduledata->introformat, $options); 223 } 224 225 // Completion tracking. 226 $completiondata = \core_completion\privacy\provider::get_activity_completion_info($user, $course, $cm); 227 if (isset($completiondata->completionstate)) { 228 $basedata->completion = (object) [ 229 'state' => $completiondata->completionstate, 230 ]; 231 } 232 233 return $basedata; 234 } 235 236 /** 237 * Get all general data for the block at this context. 238 * 239 * @param \context_block $context The context to retrieve data for. 240 * @param \stdClass $user The user being written. 241 * @return \stdClass General data about this block instance. 242 */ 243 protected static function get_context_block_data(\context_block $context, \stdClass $user) : \stdClass { 244 global $DB; 245 246 $block = $DB->get_record('block_instances', ['id' => $context->instanceid]); 247 248 $basedata = (object) [ 249 'blocktype' => get_string('pluginname', 'block_' . $block->blockname) 250 ]; 251 252 return $basedata; 253 } 254 255 /** 256 * Get all general data for the activity module at this context. 257 * 258 * @param \context_module $context The context to retrieve data for. 259 * @param \stdClass $user The user being written. 260 * @return \stdClass 261 */ 262 protected static function export_context_module_files(\context_module $context, \stdClass $user) { 263 $coursecontext = $context->get_course_context(); 264 $modinfo = get_fast_modinfo($coursecontext->instanceid); 265 $cm = $modinfo->cms[$context->instanceid]; 266 $component = "mod_{$cm->modname}"; 267 268 writer::with_context($context) 269 // Export the files for the intro. 270 ->export_area_files([], $component, 'intro', 0); 271 } 272 273 /** 274 * Handle removal of 'standard' data for course modules. 275 * 276 * This will handle deletion for things such as activity completion. 277 * 278 * @param string $component The component being deleted for. 279 * @param \context $context The context to delete all data for. 280 */ 281 public static function delete_data_for_all_users_in_context_course_module(string $component, \context $context) { 282 global $DB; 283 284 if ($context instanceof \context_module) { 285 // Delete course completion data for this context. 286 \core_completion\privacy\provider::delete_completion(null, null, $context->instanceid); 287 } 288 } 289 290 /** 291 * Delete all 'standard' user data for the specified user in course modules. 292 * 293 * This will handle deletion for things such as activity completion. 294 * 295 * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. 296 */ 297 protected static function delete_data_for_user_in_course_module(approved_contextlist $contextlist) { 298 global $DB; 299 300 foreach ($contextlist as $context) { 301 if ($context instanceof \context_module) { 302 // Delete course completion data for this context. 303 \core_completion\privacy\provider::delete_completion($contextlist->get_user(), null, $context->instanceid); 304 } 305 } 306 307 } 308} 309