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 report_stats. 19 * 20 * @package report_stats 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 report_stats\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\userlist; 33 use \core_privacy\local\request\approved_userlist; 34 35 /** 36 * Privacy Subsystem for report_stats implementing provider. 37 * 38 * @copyright 2018 Zig Tan <zig@moodle.com> 39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 */ 41 class provider implements 42 \core_privacy\local\metadata\provider, 43 \core_privacy\local\request\core_userlist_provider, 44 \core_privacy\local\request\subsystem\provider{ 45 46 /** 47 * Returns information about the user data stored in this component. 48 * 49 * @param collection $collection A list of information about this component 50 * @return collection The collection object filled out with information about this component. 51 */ 52 public static function get_metadata(collection $collection) : collection { 53 $statsuserdaily = [ 54 'courseid' => 'privacy:metadata:courseid', 55 'userid' => 'privacy:metadata:userid', 56 'roleid' => 'privacy:metadata:roleid', 57 'timeend' => 'privacy:metadata:timeend', 58 'statsreads' => 'privacy:metadata:statsreads', 59 'statswrites' => 'privacy:metadata:statswrites', 60 'stattype' => 'privacy:metadata:stattype' 61 ]; 62 63 $statsuserweekly = [ 64 'courseid' => 'privacy:metadata:courseid', 65 'userid' => 'privacy:metadata:userid', 66 'roleid' => 'privacy:metadata:roleid', 67 'timeend' => 'privacy:metadata:timeend', 68 'statsreads' => 'privacy:metadata:statsreads', 69 'statswrites' => 'privacy:metadata:statswrites', 70 'stattype' => 'privacy:metadata:stattype' 71 ]; 72 73 $statsusermonthly = [ 74 'courseid' => 'privacy:metadata:courseid', 75 'userid' => 'privacy:metadata:userid', 76 'roleid' => 'privacy:metadata:roleid', 77 'timeend' => 'privacy:metadata:timeend', 78 'statsreads' => 'privacy:metadata:statsreads', 79 'statswrites' => 'privacy:metadata:statswrites', 80 'stattype' => 'privacy:metadata:stattype' 81 ]; 82 $collection->add_database_table('stats_user_daily', $statsuserdaily, 'privacy:metadata:statssummary'); 83 $collection->add_database_table('stats_user_weekly', $statsuserweekly, 'privacy:metadata:statssummary'); 84 $collection->add_database_table('stats_user_monthly', $statsusermonthly, 'privacy:metadata:statssummary'); 85 return $collection; 86 } 87 88 /** 89 * Get the list of contexts that contain user information for the specified user. 90 * 91 * @param int $userid The user to search. 92 * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. 93 */ 94 public static function get_contexts_for_userid(int $userid) : contextlist { 95 $params = ['userid' => $userid, 'contextcourse' => CONTEXT_COURSE]; 96 $sql = "SELECT ctx.id 97 FROM {context} ctx 98 JOIN {stats_user_daily} sud ON sud.courseid = ctx.instanceid AND sud.userid = :userid 99 WHERE ctx.contextlevel = :contextcourse"; 100 101 $contextlist = new contextlist(); 102 $contextlist->add_from_sql($sql, $params); 103 104 $sql = "SELECT ctx.id 105 FROM {context} ctx 106 JOIN {stats_user_weekly} suw ON suw.courseid = ctx.instanceid AND suw.userid = :userid 107 WHERE ctx.contextlevel = :contextcourse"; 108 $contextlist->add_from_sql($sql, $params); 109 110 $sql = "SELECT ctx.id 111 FROM {context} ctx 112 JOIN {stats_user_monthly} sum ON sum.courseid = ctx.instanceid AND sum.userid = :userid 113 WHERE ctx.contextlevel = :contextcourse"; 114 $contextlist->add_from_sql($sql, $params); 115 116 return $contextlist; 117 } 118 119 /** 120 * Get the list of users within a specific context. 121 * 122 * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. 123 */ 124 public static function get_users_in_context(userlist $userlist) { 125 $context = $userlist->get_context(); 126 127 if (!$context instanceof \context_course) { 128 return; 129 } 130 131 $params = ['courseid' => $context->instanceid]; 132 133 $sql = "SELECT userid FROM {stats_user_daily} WHERE courseid = :courseid"; 134 $userlist->add_from_sql('userid', $sql, $params); 135 136 $sql = "SELECT userid FROM {stats_user_weekly} WHERE courseid = :courseid"; 137 $userlist->add_from_sql('userid', $sql, $params); 138 139 $sql = "SELECT userid FROM {stats_user_monthly} WHERE courseid = :courseid"; 140 $userlist->add_from_sql('userid', $sql, $params); 141 } 142 143 /** 144 * Export all user data for the specified user, in the specified contexts. 145 * 146 * @param approved_contextlist $contextlist The approved contexts to export information for. 147 */ 148 public static function export_user_data(approved_contextlist $contextlist) { 149 global $DB; 150 151 // Some sneeky person might have sent us the wrong context list. We should check. 152 if ($contextlist->get_component() != 'report_stats') { 153 return; 154 } 155 156 // Got to check that someone hasn't foolishly added a context between creating the context list and then filtering down 157 // to an approved context. 158 $contexts = array_filter($contextlist->get_contexts(), function($context) { 159 if ($context->contextlevel == CONTEXT_COURSE) { 160 return $context; 161 } 162 }); 163 164 $tables = [ 165 'stats_user_daily' => get_string('privacy:dailypath', 'report_stats'), 166 'stats_user_weekly' => get_string('privacy:weeklypath', 'report_stats'), 167 'stats_user_monthly' => get_string('privacy:monthlypath', 'report_stats') 168 ]; 169 170 $courseids = array_map(function($context) { 171 return $context->instanceid; 172 }, $contexts); 173 174 foreach ($tables as $table => $path) { 175 176 list($insql, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); 177 $sql = "SELECT s.id, c.fullname, s.roleid, s.timeend, s.statsreads, s.statswrites, s.stattype, c.id as courseid 178 FROM {" . $table . "} s 179 JOIN {course} c ON s.courseid = c.id 180 WHERE s.userid = :userid AND c.id $insql 181 ORDER BY c.id ASC"; 182 $params['userid'] = $contextlist->get_user()->id; 183 $records = $DB->get_records_sql($sql, $params); 184 185 $statsrecords = []; 186 foreach ($records as $record) { 187 $context = \context_course::instance($record->courseid); 188 if (!isset($statsrecords[$record->courseid])) { 189 $statsrecords[$record->courseid] = new \stdClass(); 190 $statsrecords[$record->courseid]->context = $context; 191 } 192 $statsrecords[$record->courseid]->entries[] = [ 193 'course' => format_string($record->fullname, true, ['context' => $context]), 194 'roleid' => $record->roleid, 195 'timeend' => \core_privacy\local\request\transform::datetime($record->timeend), 196 'statsreads' => $record->statsreads, 197 'statswrites' => $record->statswrites, 198 'stattype' => $record->stattype 199 ]; 200 } 201 foreach ($statsrecords as $coursestats) { 202 \core_privacy\local\request\writer::with_context($coursestats->context)->export_data([$path], 203 (object) $coursestats->entries); 204 } 205 } 206 } 207 208 /** 209 * Delete all data for all users in the specified context. 210 * 211 * @param context $context The specific context to delete data for. 212 */ 213 public static function delete_data_for_all_users_in_context(\context $context) { 214 // Check that this context is a course context. 215 if ($context->contextlevel == CONTEXT_COURSE) { 216 static::delete_stats($context->instanceid); 217 } 218 } 219 220 /** 221 * Delete all user data for the specified user, in the specified contexts. 222 * 223 * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. 224 */ 225 public static function delete_data_for_user(approved_contextlist $contextlist) { 226 if ($contextlist->get_component() != 'report_stats') { 227 return; 228 } 229 foreach ($contextlist->get_contexts() as $context) { 230 if ($context->contextlevel == CONTEXT_COURSE) { 231 static::delete_stats($context->instanceid, $contextlist->get_user()->id); 232 } 233 } 234 } 235 236 /** 237 * Delete multiple users within a single context. 238 * 239 * @param approved_userlist $userlist The approved context and user information to delete information for. 240 */ 241 public static function delete_data_for_users(approved_userlist $userlist) { 242 global $DB; 243 244 $context = $userlist->get_context(); 245 246 if ($context instanceof \context_course) { 247 list($usersql, $userparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED); 248 $select = "courseid = :courseid AND userid {$usersql}"; 249 $params = ['courseid' => $context->instanceid] + $userparams; 250 251 $DB->delete_records_select('stats_user_daily', $select, $params); 252 $DB->delete_records_select('stats_user_weekly', $select, $params); 253 $DB->delete_records_select('stats_user_monthly', $select, $params); 254 } 255 } 256 257 /** 258 * Deletes stats for a given course. 259 * 260 * @param int $courseid The course ID to delete the stats for. 261 * @param int $userid Optionally a user id to delete records with. 262 */ 263 protected static function delete_stats(int $courseid, int $userid = null) { 264 global $DB; 265 $params = (isset($userid)) ? ['courseid' => $courseid, 'userid' => $userid] : ['courseid' => $courseid]; 266 $DB->delete_records('stats_user_daily', $params); 267 $DB->delete_records('stats_user_weekly', $params); 268 $DB->delete_records('stats_user_monthly', $params); 269 } 270 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body