See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 401] [Versions 401 and 402] [Versions 401 and 403]
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 class for requesting user data. 19 * 20 * @package core_course 21 * @copyright 2018 Adrian Greeve <adrian@moodle.com> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace core_course\privacy; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 use \core_privacy\local\metadata\collection; 30 use \core_privacy\local\request\contextlist; 31 use \core_privacy\local\request\approved_contextlist; 32 use \core_privacy\local\request\approved_userlist; 33 use \core_privacy\local\request\transform; 34 use \core_privacy\local\request\userlist; 35 use \core_privacy\local\request\writer; 36 37 /** 38 * Privacy class for requesting user data. 39 * 40 * @copyright 2018 Adrian Greeve <adrian@moodle.com> 41 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 42 */ 43 class provider implements 44 \core_privacy\local\metadata\provider, 45 \core_privacy\local\request\context_aware_provider, 46 \core_privacy\local\request\core_userlist_provider, 47 \core_privacy\local\request\plugin\provider, 48 \core_privacy\local\request\user_preference_provider { 49 50 /** 51 * Returns meta data about this system. 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_subsystem_link('core_completion', [], 'privacy:metadata:completionsummary'); 58 $collection->add_subsystem_link('core_favourites', [], 'privacy:metadata:favouritessummary'); 59 $collection->add_subsystem_link('core_favourites', [], 'privacy:metadata:activityfavouritessummary'); 60 $collection->add_user_preference('coursecat_management_perpage', 'privacy:perpage'); 61 return $collection; 62 } 63 64 /** 65 * Get the list of contexts that contain user information for the specified user. 66 * 67 * @param int $userid The user to search. 68 * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. 69 */ 70 public static function get_contexts_for_userid(int $userid) : contextlist { 71 list($join, $where, $params) = \core_completion\privacy\provider::get_course_completion_join_sql($userid, 'cc', 'c.id'); 72 $sql = "SELECT ctx.id 73 FROM {context} ctx 74 JOIN {course} c ON ctx.instanceid = c.id AND ctx.contextlevel = :contextcourse 75 {$join} 76 WHERE {$where}"; 77 $params['contextcourse'] = CONTEXT_COURSE; 78 $contextlist = new contextlist(); 79 $contextlist->add_from_sql($sql, $params); 80 81 \core_favourites\privacy\provider::add_contexts_for_userid($contextlist, $userid, 'core_course', 'courses'); 82 83 return $contextlist; 84 } 85 86 /** 87 * Get the list of users who have data within a context. 88 * 89 * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. 90 */ 91 public static function get_users_in_context(userlist $userlist) { 92 $context = $userlist->get_context(); 93 94 if (!$context instanceof \context_course) { 95 return; 96 } 97 98 \core_completion\privacy\provider::add_course_completion_users_to_userlist($userlist); 99 \core_favourites\privacy\provider::add_userids_for_context($userlist, 'courses'); 100 } 101 102 /** 103 * Export all user data for the specified user, in the specified contexts. 104 * 105 * @param approved_contextlist $contextlist The approved contexts to export information for. 106 */ 107 public static function export_user_data(approved_contextlist $contextlist) { 108 global $DB; 109 110 // Get the course. 111 list($select, $params) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); 112 $params['contextcourse'] = CONTEXT_COURSE; 113 114 $sql = "SELECT c.* 115 FROM {course} c 116 JOIN {context} ctx ON c.id = ctx.instanceid AND ctx.contextlevel = :contextcourse 117 WHERE ctx.id $select"; 118 119 $courses = $DB->get_recordset_sql($sql, $params); 120 121 foreach ($courses as $course) { 122 $coursecompletion = \core_completion\privacy\provider::get_course_completion_info($contextlist->get_user(), $course); 123 writer::with_context(\context_course::instance($course->id))->export_data( 124 [get_string('privacy:completionpath', 'course')], (object) $coursecompletion); 125 // Get user's favourites information for the particular course. 126 $coursefavourite = \core_favourites\privacy\provider::get_favourites_info_for_user($contextlist->get_user()->id, 127 \context_course::instance($course->id), 'core_course', 'courses', $course->id); 128 if ($coursefavourite) { // If the course has been favourited by the user, include it in the export. 129 writer::with_context(\context_course::instance($course->id))->export_data( 130 [get_string('privacy:favouritespath', 'course')], (object) $coursefavourite); 131 } 132 } 133 $courses->close(); 134 } 135 136 /** 137 * Give the component a chance to include any contextual information deemed relevant to any child contexts which are 138 * exporting personal data. 139 * 140 * By giving the component access to the full list of contexts being exported across all components, it can determine whether a 141 * descendant context is being exported, and decide whether to add relevant contextual information about itself. Having access 142 * to the full list of contexts being exported is what makes this component a context aware provider. 143 * 144 * E.g. 145 * If, during the core export process, a course module is included in the contextlist_collection but the course containing the 146 * module is not (perhaps there's no longer a user enrolment), then the course should include general contextual information in 147 * the export so we know basic details about which course the module belongs to. This method allows the course to make that 148 * decision, based on the existence of any decendant module contexts in the collection. 149 * 150 * @param \core_privacy\local\request\contextlist_collection $contextlistcollection 151 */ 152 public static function export_context_data(\core_privacy\local\request\contextlist_collection $contextlistcollection) { 153 global $DB; 154 155 $coursecontextids = $DB->get_records_menu('context', ['contextlevel' => CONTEXT_COURSE], '', 'id, instanceid'); 156 $courseids = []; 157 foreach ($contextlistcollection as $component) { 158 foreach ($component->get_contexts() as $context) { 159 // All course contexts have been accounted for, so skip all checks. 160 if (empty($coursecontextids)) { 161 break; 162 } 163 // Only course, module, and block contexts are checked. 164 if (in_array($context->contextlevel, [CONTEXT_USER, CONTEXT_SYSTEM, CONTEXT_COURSECAT])) { 165 continue; 166 } 167 // If the context is a course, then we just add it without the need to check context path. 168 if ($context->contextlevel == CONTEXT_COURSE) { 169 $courseids[$context->id] = $context->instanceid; 170 unset($coursecontextids[$context->id]); 171 continue; 172 } 173 // Otherwise, we need to check all the course context paths, to see if this context is a descendant. 174 foreach ($coursecontextids as $contextid => $instanceid) { 175 if (stripos($context->path, '/' . $contextid . '/') !== false) { 176 $courseids[$contextid] = $instanceid; 177 unset($coursecontextids[$contextid]); 178 } 179 } 180 } 181 } 182 if (empty($courseids)) { 183 return; 184 } 185 186 // Export general data for these contexts. 187 list($sql, $params) = $DB->get_in_or_equal($courseids); 188 $sql = 'id ' . $sql; 189 $coursedata = $DB->get_records_select('course', $sql, $params); 190 191 foreach ($coursedata as $course) { 192 $context = \context_course::instance($course->id); 193 $courseformat = $course->format !== 'site' ? get_string('pluginname', 'format_' . $course->format) : get_string('site'); 194 $data = (object) [ 195 'fullname' => format_string($course->fullname, true, ['context' => $context]), 196 'shortname' => $course->shortname, 197 'idnumber' => $course->idnumber, 198 'summary' => format_text( 199 writer::with_context($context)->rewrite_pluginfile_urls( 200 [], 201 'course', 202 'summary', 203 0, 204 $course->summary 205 ), $course->summaryformat, ['context' => $context]), 206 'format' => $courseformat, 207 'startdate' => transform::datetime($course->startdate), 208 'enddate' => transform::datetime($course->enddate) 209 ]; 210 writer::with_context($context) 211 ->export_area_files([], 'course', 'summary', 0) 212 ->export_area_files([], 'course', 'overviewfiles', 0) 213 ->export_data([], $data); 214 } 215 } 216 217 /** 218 * Export all user preferences for the plugin. 219 * 220 * @param int $userid The userid of the user whose data is to be exported. 221 */ 222 public static function export_user_preferences(int $userid) { 223 $perpage = get_user_preferences('coursecat_management_perpage', null, $userid); 224 if (isset($perpage)) { 225 writer::export_user_preference('core_course', 226 'coursecat_management_perpage', 227 $perpage, 228 get_string('privacy:perpage', 'course') 229 ); 230 } 231 } 232 233 /** 234 * Delete all data for all users in the specified context. 235 * 236 * @param context $context The specific context to delete data for. 237 */ 238 public static function delete_data_for_all_users_in_context(\context $context) { 239 // Check what context we've been delivered. 240 if (!$context instanceof \context_course) { 241 return; 242 } 243 // Delete course completion data. 244 \core_completion\privacy\provider::delete_completion(null, $context->instanceid); 245 // Delete course favourite data. 246 \core_favourites\privacy\provider::delete_favourites_for_all_users($context, 'core_course', 247 'courses'); 248 } 249 250 /** 251 * Delete all user data for the specified user, in the specified contexts. 252 * 253 * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. 254 */ 255 public static function delete_data_for_user(approved_contextlist $contextlist) { 256 foreach ($contextlist as $context) { 257 // Check what context we've been delivered. 258 if ($context instanceof \context_course) { 259 // Delete course completion data. 260 \core_completion\privacy\provider::delete_completion($contextlist->get_user(), $context->instanceid); 261 // Delete course favourite data. 262 \core_favourites\privacy\provider::delete_favourites_for_user($contextlist, 'core_course', 263 'courses'); 264 } 265 } 266 } 267 268 /** 269 * Delete multiple users within a single context. 270 * 271 * @param approved_userlist $userlist The approved context and user information to delete information for. 272 */ 273 public static function delete_data_for_users(approved_userlist $userlist) { 274 $context = $userlist->get_context(); 275 276 // Check what context we've been delivered. 277 if (!$context instanceof \context_course) { 278 return; 279 } 280 // Delete course completion data. 281 \core_completion\privacy\provider::delete_completion_by_approved_userlist($userlist, $context->instanceid); 282 // Delete course favourite data. 283 \core_favourites\privacy\provider::delete_favourites_for_userlist($userlist, 'courses'); 284 } 285 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body