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 declare(strict_types=1); 18 19 namespace core_reportbuilder\privacy; 20 21 use context; 22 use stdClass; 23 use core_privacy\local\metadata\collection; 24 use core_privacy\local\request\approved_contextlist; 25 use core_privacy\local\request\approved_userlist; 26 use core_privacy\local\request\contextlist; 27 use core_privacy\local\request\core_userlist_provider; 28 use core_privacy\local\request\transform; 29 use core_privacy\local\request\userlist; 30 use core_privacy\local\request\user_preference_provider; 31 use core_privacy\local\request\writer; 32 use core_reportbuilder\manager; 33 use core_reportbuilder\local\helpers\user_filter_manager; 34 use core_reportbuilder\local\helpers\schedule as schedule_helper; 35 use core_reportbuilder\local\models\audience; 36 use core_reportbuilder\local\models\column; 37 use core_reportbuilder\local\models\filter; 38 use core_reportbuilder\local\models\report; 39 use core_reportbuilder\local\models\schedule; 40 41 /** 42 * Privacy Subsystem for core_reportbuilder 43 * 44 * @package core_reportbuilder 45 * @copyright 2021 David Matamoros <davidmc@moodle.com> 46 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 47 */ 48 class provider implements 49 \core_privacy\local\metadata\provider, 50 \core_privacy\local\request\plugin\provider, 51 core_userlist_provider, 52 user_preference_provider { 53 54 /** 55 * Returns metadata about the component 56 * 57 * @param collection $collection 58 * @return collection 59 */ 60 public static function get_metadata(collection $collection): collection { 61 $collection->add_database_table(report::TABLE, [ 62 'name' => 'privacy:metadata:report:name', 63 'source' => 'privacy:metadata:report:source', 64 'conditiondata' => 'privacy:metadata:report:conditiondata', 65 'settingsdata' => 'privacy:metadata:report:settingsdata', 66 'uniquerows' => 'privacy:metadata:report:uniquerows', 67 'usercreated' => 'privacy:metadata:report:usercreated', 68 'usermodified' => 'privacy:metadata:report:usermodified', 69 'timecreated' => 'privacy:metadata:report:timecreated', 70 'timemodified' => 'privacy:metadata:report:timemodified', 71 ], 'privacy:metadata:report'); 72 73 $collection->add_database_table(column::TABLE, [ 74 'uniqueidentifier' => 'privacy:metadata:column:uniqueidentifier', 75 'usercreated' => 'privacy:metadata:column:usercreated', 76 'usermodified' => 'privacy:metadata:column:usermodified', 77 ], 'privacy:metadata:column'); 78 79 $collection->add_database_table(filter::TABLE, [ 80 'uniqueidentifier' => 'privacy:metadata:filter:uniqueidentifier', 81 'usercreated' => 'privacy:metadata:filter:usercreated', 82 'usermodified' => 'privacy:metadata:filter:usermodified', 83 ], 'privacy:metadata:filter'); 84 85 $collection->add_database_table(audience::TABLE, [ 86 'classname' => 'privacy:metadata:audience:classname', 87 'configdata' => 'privacy:metadata:audience:configdata', 88 'heading' => 'privacy:metadata:audience:heading', 89 'usercreated' => 'privacy:metadata:audience:usercreated', 90 'usermodified' => 'privacy:metadata:audience:usermodified', 91 'timecreated' => 'privacy:metadata:audience:timecreated', 92 'timemodified' => 'privacy:metadata:audience:timemodified', 93 ], 'privacy:metadata:audience'); 94 95 $collection->add_database_table(schedule::TABLE, [ 96 'name' => 'privacy:metadata:schedule:name', 97 'enabled' => 'privacy:metadata:schedule:enabled', 98 'audiences' => 'privacy:metadata:schedule:audiences', 99 'format' => 'privacy:metadata:schedule:format', 100 'subject' => 'privacy:metadata:schedule:subject', 101 'message' => 'privacy:metadata:schedule:message', 102 'userviewas' => 'privacy:metadata:schedule:userviewas', 103 'timescheduled' => 'privacy:metadata:schedule:timescheduled', 104 'recurrence' => 'privacy:metadata:schedule:recurrence', 105 'reportempty' => 'privacy:metadata:schedule:reportempty', 106 'usercreated' => 'privacy:metadata:schedule:usercreated', 107 'usermodified' => 'privacy:metadata:schedule:usermodified', 108 'timecreated' => 'privacy:metadata:schedule:timecreated', 109 'timemodified' => 'privacy:metadata:schedule:timemodified', 110 ], 'privacy:metadata:schedule'); 111 112 $collection->add_user_preference('core_reportbuilder', 'privacy:metadata:preference:reportfilter'); 113 114 return $collection; 115 } 116 117 /** 118 * Export all user preferences for the component 119 * 120 * @param int $userid 121 */ 122 public static function export_user_preferences(int $userid): void { 123 $preferencestring = get_string('privacy:metadata:preference:reportfilter', 'core_reportbuilder'); 124 125 $filters = user_filter_manager::get_all_for_user($userid); 126 foreach ($filters as $key => $filter) { 127 writer::export_user_preference('core_reportbuilder', 128 $key, 129 json_encode($filter, JSON_PRETTY_PRINT), 130 $preferencestring 131 ); 132 } 133 } 134 135 /** 136 * Get export sub context for a report 137 * 138 * @param report $report 139 * @return array 140 */ 141 public static function get_export_subcontext(report $report): array { 142 $reportnode = implode('-', [ 143 $report->get('id'), 144 clean_filename($report->get_formatted_name()), 145 ]); 146 147 return [get_string('reportbuilder', 'core_reportbuilder'), $reportnode]; 148 } 149 150 /** 151 * Get the list of contexts that contain user information for the specified user 152 * 153 * @param int $userid 154 * @return contextlist 155 */ 156 public static function get_contexts_for_userid(int $userid): contextlist { 157 $contextlist = new contextlist(); 158 159 // Locate all contexts for reports the user has created, or reports they have created audience/schedules for. 160 $sql = ' 161 SELECT r.contextid 162 FROM {' . report::TABLE . '} r 163 WHERE r.type = 0 164 AND (r.usercreated = ? 165 OR r.usermodified = ? 166 OR r.id IN ( 167 SELECT a.reportid 168 FROM {' . audience::TABLE . '} a 169 WHERE a.usercreated = ? OR a.usermodified = ? 170 UNION 171 SELECT s.reportid 172 FROM {' . schedule::TABLE . '} s 173 WHERE s.usercreated = ? OR s.usermodified = ? 174 ) 175 )'; 176 177 return $contextlist->add_from_sql($sql, array_fill(0, 6, $userid)); 178 } 179 180 /** 181 * Get users in context 182 * 183 * @param userlist $userlist 184 */ 185 public static function get_users_in_context(userlist $userlist): void { 186 $select = 'r.type = :type AND r.contextid = :contextid'; 187 $params = ['type' => 0, 'contextid' => $userlist->get_context()->id]; 188 189 // Users who have created reports. 190 $sql = 'SELECT r.usercreated, r.usermodified 191 FROM {' . report::TABLE . '} r 192 WHERE ' . $select; 193 $userlist->add_from_sql('usercreated', $sql, $params); 194 $userlist->add_from_sql('usermodified', $sql, $params); 195 196 // Users who have created audiences. 197 $sql = 'SELECT a.usercreated, a.usermodified 198 FROM {' . audience::TABLE . '} a 199 JOIN {' . report::TABLE . '} r ON r.id = a.reportid 200 WHERE ' . $select; 201 $userlist->add_from_sql('usercreated', $sql, $params); 202 $userlist->add_from_sql('usermodified', $sql, $params); 203 204 // Users who have created schedules. 205 $sql = 'SELECT s.usercreated, s.usermodified 206 FROM {' . schedule::TABLE . '} s 207 JOIN {' . report::TABLE . '} r ON r.id = s.reportid 208 WHERE ' . $select; 209 $userlist->add_from_sql('usercreated', $sql, $params); 210 $userlist->add_from_sql('usermodified', $sql, $params); 211 } 212 213 /** 214 * Export all user data for the specified user in the specified contexts 215 * 216 * @param approved_contextlist $contextlist 217 */ 218 public static function export_user_data(approved_contextlist $contextlist): void { 219 if (empty($contextlist->count())) { 220 return; 221 } 222 223 $user = $contextlist->get_user(); 224 225 // We need to get all reports that the user has created, or reports they have created audience/schedules for. 226 $select = 'type = 0 AND (usercreated = ? OR usermodified = ? OR id IN ( 227 SELECT a.reportid 228 FROM {' . audience::TABLE . '} a 229 WHERE a.usercreated = ? OR a.usermodified = ? 230 UNION 231 SELECT s.reportid 232 FROM {' . schedule::TABLE . '} s 233 WHERE s.usercreated = ? OR s.usermodified = ? 234 ))'; 235 $params = array_fill(0, 6, $user->id); 236 237 foreach (report::get_records_select($select, $params) as $report) { 238 $subcontext = static::get_export_subcontext($report); 239 240 self::export_report($subcontext, $report); 241 242 $select = 'reportid = ? AND (usercreated = ? OR usermodified = ?)'; 243 $params = [$report->get('id'), $user->id, $user->id]; 244 245 // Audiences. 246 if ($audiences = audience::get_records_select($select, $params)) { 247 static::export_audiences($report->get_context(), $subcontext, $audiences); 248 } 249 250 // Schedules. 251 if ($schedules = schedule::get_records_select($select, $params)) { 252 static::export_schedules($report->get_context(), $subcontext, $schedules); 253 } 254 } 255 } 256 257 /** 258 * Delete data for all users in context 259 * 260 * @param context $context 261 */ 262 public static function delete_data_for_all_users_in_context(context $context): void { 263 // We don't perform any deletion of user data. 264 } 265 266 /** 267 * Delete data for user 268 * 269 * @param approved_contextlist $contextlist 270 */ 271 public static function delete_data_for_user(approved_contextlist $contextlist): void { 272 // We don't perform any deletion of user data. 273 } 274 275 /** 276 * Delete data for users 277 * 278 * @param approved_userlist $userlist 279 */ 280 public static function delete_data_for_users(approved_userlist $userlist): void { 281 // We don't perform any deletion of user data. 282 } 283 284 /** 285 * Export given report in context 286 * 287 * @param array $subcontext 288 * @param report $report 289 */ 290 protected static function export_report(array $subcontext, report $report): void { 291 // Show the source name, if it exists. 292 $source = $report->get('source'); 293 if (manager::report_source_exists($source)) { 294 $source = call_user_func([$source, 'get_name']); 295 } 296 297 $reportdata = (object) [ 298 'name' => $report->get_formatted_name(), 299 'source' => $source, 300 'conditiondata' => $report->get('conditiondata'), 301 'settingsdata' => $report->get('settingsdata'), 302 'uniquerows' => transform::yesno($report->get('uniquerows')), 303 'usercreated' => transform::user($report->get('usercreated')), 304 'usermodified' => transform::user($report->get('usermodified')), 305 'timecreated' => transform::datetime($report->get('timecreated')), 306 'timemodified' => transform::datetime($report->get('timemodified')), 307 ]; 308 309 writer::with_context($report->get_context())->export_data($subcontext, $reportdata); 310 } 311 312 /** 313 * Export given audiences in context 314 * 315 * @param context $context 316 * @param array $subcontext 317 * @param audience[] $audiences 318 */ 319 protected static function export_audiences(context $context, array $subcontext, array $audiences): void { 320 $audiencedata = array_map(static function(audience $audience) use ($context): stdClass { 321 // Show the audience name, if it exists. 322 $classname = $audience->get('classname'); 323 if (class_exists($classname)) { 324 $classname = $classname::instance()->get_name(); 325 } 326 327 return (object) [ 328 'classname' => $classname, 329 'heading' => $audience->get_formatted_heading($context), 330 'configdata' => $audience->get('configdata'), 331 'usercreated' => transform::user($audience->get('usercreated')), 332 'usermodified' => transform::user($audience->get('usermodified')), 333 'timecreated' => transform::datetime($audience->get('timecreated')), 334 'timemodified' => transform::datetime($audience->get('timemodified')), 335 ]; 336 }, $audiences); 337 338 writer::with_context($context)->export_related_data($subcontext, 'audiences', (object) ['data' => $audiencedata]); 339 } 340 341 /** 342 * Export given schedules in context 343 * 344 * @param context $context 345 * @param array $subcontext 346 * @param schedule[] $schedules 347 */ 348 protected static function export_schedules(context $context, array $subcontext, array $schedules): void { 349 $formatoptions = schedule_helper::get_format_options(); 350 $recurrenceoptions = schedule_helper::get_recurrence_options(); 351 $viewasoptions = schedule_helper::get_viewas_options(); 352 $reportemptyoptions = schedule_helper::get_report_empty_options(); 353 354 $scheduledata = array_map(static function(schedule $schedule) use ( 355 $context, $formatoptions, $recurrenceoptions, $viewasoptions, $reportemptyoptions): stdClass { 356 357 // The "User view as" property will be either creator, recipient or a specific userid. 358 $userviewas = $schedule->get('userviewas'); 359 360 return (object) [ 361 'name' => $schedule->get_formatted_name($context), 362 'enabled' => transform::yesno($schedule->get('enabled')), 363 'format' => $formatoptions[$schedule->get('format')], 364 'timescheduled' => transform::datetime($schedule->get('timescheduled')), 365 'recurrence' => $recurrenceoptions[$schedule->get('recurrence')], 366 'userviewas' => $viewasoptions[$userviewas] ?? transform::user($userviewas), 367 'audiences' => $schedule->get('audiences'), 368 'subject' => $schedule->get('subject'), 369 'message' => format_text($schedule->get('message'), $schedule->get('messageformat'), ['context' => $context]), 370 'reportempty' => $reportemptyoptions[$schedule->get('reportempty')], 371 'usercreated' => transform::user($schedule->get('usercreated')), 372 'usermodified' => transform::user($schedule->get('usermodified')), 373 'timecreated' => transform::datetime($schedule->get('timecreated')), 374 'timemodified' => transform::datetime($schedule->get('timemodified')), 375 ]; 376 }, $schedules); 377 378 writer::with_context($context)->export_related_data($subcontext, 'schedules', (object) ['data' => $scheduledata]); 379 } 380 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body