See Release Notes
Long Term Support Release
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 * Data provider. 19 * 20 * @package mod_wiki 21 * @copyright 2018 Marina Glancy 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace mod_wiki\privacy; 26 27 use core_privacy\local\metadata\collection; 28 use core_privacy\local\request\approved_contextlist; 29 use core_privacy\local\request\approved_userlist; 30 use core_privacy\local\request\contextlist; 31 use context_user; 32 use context; 33 use core_privacy\local\request\helper; 34 use core_privacy\local\request\transform; 35 use core_privacy\local\request\userlist; 36 use core_privacy\local\request\writer; 37 38 defined('MOODLE_INTERNAL') || die(); 39 40 /** 41 * Data provider class. 42 * 43 * @package mod_wiki 44 * @copyright 2018 Marina Glancy 45 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 46 */ 47 class provider implements 48 \core_privacy\local\metadata\provider, 49 \core_privacy\local\request\core_userlist_provider, 50 \core_privacy\local\request\plugin\provider { 51 52 /** 53 * Returns metadata. 54 * 55 * @param collection $collection The initialised collection to add items to. 56 * @return collection A listing of user data stored through this system. 57 */ 58 public static function get_metadata(collection $collection) : collection { 59 60 $collection->add_database_table('wiki_subwikis', [ 61 'userid' => 'privacy:metadata:wiki_subwikis:userid', 62 'groupid' => 'privacy:metadata:wiki_subwikis:groupid', 63 ], 'privacy:metadata:wiki_subwikis'); 64 65 $collection->add_database_table('wiki_pages', [ 66 'userid' => 'privacy:metadata:wiki_pages:userid', 67 'title' => 'privacy:metadata:wiki_pages:title', 68 'cachedcontent' => 'privacy:metadata:wiki_pages:cachedcontent', 69 'timecreated' => 'privacy:metadata:wiki_pages:timecreated', 70 'timemodified' => 'privacy:metadata:wiki_pages:timemodified', 71 'timerendered' => 'privacy:metadata:wiki_pages:timerendered', 72 'pageviews' => 'privacy:metadata:wiki_pages:pageviews', 73 'readonly' => 'privacy:metadata:wiki_pages:readonly', 74 ], 'privacy:metadata:wiki_pages'); 75 76 $collection->add_database_table('wiki_versions', [ 77 'userid' => 'privacy:metadata:wiki_versions:userid', 78 'content' => 'privacy:metadata:wiki_versions:content', 79 'contentformat' => 'privacy:metadata:wiki_versions:contentformat', 80 'version' => 'privacy:metadata:wiki_versions:version', 81 'timecreated' => 'privacy:metadata:wiki_versions:timecreated', 82 ], 'privacy:metadata:wiki_versions'); 83 84 $collection->add_database_table('wiki_locks', [ 85 'userid' => 'privacy:metadata:wiki_locks:userid', 86 'sectionname' => 'privacy:metadata:wiki_locks:sectionname', 87 'lockedat' => 'privacy:metadata:wiki_locks:lockedat', 88 ], 'privacy:metadata:wiki_locks'); 89 90 $collection->link_subsystem('core_files', 'privacy:metadata:core_files'); 91 $collection->link_subsystem('core_tag', 'privacy:metadata:core_tag'); 92 $collection->link_subsystem('core_comment', 'privacy:metadata:core_comment'); 93 94 // We do not report on wiki, wiki_synonyms, wiki_links because this is just context-related data. 95 96 return $collection; 97 } 98 99 /** 100 * Get the list of contexts that contain user information for the specified user. 101 * 102 * @param int $userid The user to search. 103 * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. 104 */ 105 public static function get_contexts_for_userid(int $userid) : contextlist { 106 $contextlist = new contextlist(); 107 108 $contextlist->add_from_sql('SELECT ctx.id 109 FROM {modules} m 110 JOIN {course_modules} cm ON cm.module = m.id AND m.name = :modname 111 JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :contextlevel 112 JOIN {wiki_subwikis} s ON cm.instance = s.wikiid 113 LEFT JOIN {wiki_pages} p ON p.subwikiid = s.id 114 LEFT JOIN {wiki_versions} v ON v.pageid = p.id AND v.userid = :userid3 115 LEFT JOIN {wiki_locks} l ON l.pageid = p.id AND l.userid = :userid4 116 LEFT JOIN {comments} com ON com.itemid = p.id AND com.commentarea = :commentarea 117 AND com.contextid = ctx.id AND com.userid = :userid5 118 WHERE s.userid = :userid1 OR p.userid = :userid2 OR v.id IS NOT NULL OR l.id IS NOT NULL OR com.id IS NOT NULL', 119 ['modname' => 'wiki', 'contextlevel' => CONTEXT_MODULE, 'userid1' => $userid, 'userid2' => $userid, 120 'userid3' => $userid, 'userid4' => $userid, 'commentarea' => 'wiki_page', 'userid5' => $userid]); 121 122 return $contextlist; 123 } 124 125 /** 126 * Get the list of users who have data within a context. 127 * 128 * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. 129 */ 130 public static function get_users_in_context(userlist $userlist) { 131 $context = $userlist->get_context(); 132 133 if (!is_a($context, \context_module::class)) { 134 return; 135 } 136 137 $params = [ 138 'modname' => 'wiki', 139 'contextlevel' => CONTEXT_MODULE, 140 'contextid' => $context->id, 141 ]; 142 143 $sql = " 144 SELECT s.userid 145 FROM {modules} m 146 JOIN {course_modules} cm ON cm.module = m.id AND m.name = :modname 147 JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :contextlevel 148 JOIN {wiki_subwikis} s ON cm.instance = s.wikiid 149 WHERE ctx.id = :contextid"; 150 151 $userlist->add_from_sql('userid', $sql, $params); 152 153 $sql = " 154 SELECT p.userid 155 FROM {modules} m 156 JOIN {course_modules} cm ON cm.module = m.id AND m.name = :modname 157 JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :contextlevel 158 JOIN {wiki_subwikis} s ON cm.instance = s.wikiid 159 JOIN {wiki_pages} p ON p.subwikiid = s.id 160 WHERE ctx.id = :contextid"; 161 162 $userlist->add_from_sql('userid', $sql, $params); 163 164 $sql = " 165 SELECT v.userid 166 FROM {modules} m 167 JOIN {course_modules} cm ON cm.module = m.id AND m.name = :modname 168 JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :contextlevel 169 JOIN {wiki_subwikis} s ON cm.instance = s.wikiid 170 JOIN {wiki_pages} p ON p.subwikiid = s.id 171 JOIN {wiki_versions} v ON v.pageid = p.id 172 WHERE ctx.id = :contextid"; 173 174 $userlist->add_from_sql('userid', $sql, $params); 175 176 $sql = " 177 SELECT l.userid 178 FROM {modules} m 179 JOIN {course_modules} cm ON cm.module = m.id AND m.name = :modname 180 JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :contextlevel 181 JOIN {wiki_subwikis} s ON cm.instance = s.wikiid 182 JOIN {wiki_pages} p ON p.subwikiid = s.id 183 JOIN {wiki_locks} l ON l.pageid = p.id 184 WHERE ctx.id = :contextid"; 185 186 $userlist->add_from_sql('userid', $sql, $params); 187 \core_comment\privacy\provider::get_users_in_context_from_sql($userlist, 'com', 'mod_wiki', 'wiki_page', $context->id); 188 } 189 190 /** 191 * Add one subwiki to the export 192 * 193 * Each page is added as related data because all pages in one subwiki share the same filearea 194 * 195 * @param stdClass $user 196 * @param context $context 197 * @param array $subwiki 198 * @param string $wikimode 199 */ 200 protected static function export_subwiki($user, context $context, $subwiki, $wikimode) { 201 if (empty($subwiki)) { 202 return; 203 } 204 $subwikiid = key($subwiki); 205 $pages = $subwiki[$subwikiid]['pages']; 206 unset($subwiki[$subwikiid]['pages']); 207 writer::with_context($context)->export_data([$subwikiid], (object)$subwiki[$subwikiid]); 208 $allfiles = $wikimode === 'individual'; // Whether to export all files or only the ones that are used. 209 210 $alltexts = ''; // Store all texts that reference files to search which files are used. 211 foreach ($pages as $page => $entry) { 212 // Preprocess current page contents. 213 if (!$allfiles && self::text_has_files($entry['page']['cachedcontent'])) { 214 $alltexts .= $entry['page']['cachedcontent']; 215 } 216 $entry['page']['cachedcontent'] = format_text(writer::with_context($context) 217 ->rewrite_pluginfile_urls([$subwikiid], 'mod_wiki', 'attachments', 218 $subwikiid, $entry['page']['cachedcontent']), FORMAT_HTML, ['context' => $context]); 219 // Add page tags. 220 $pagetags = \core_tag_tag::get_item_tags_array('mod_wiki', 'page', $entry['page']['id']); 221 if ($pagetags) { 222 $entry['page']['tags'] = $pagetags; 223 } 224 225 // Preprocess revisions. 226 if (!empty($entry['revisions'])) { 227 // For each revision this user has made preprocess the contents. 228 foreach ($entry['revisions'] as &$revision) { 229 if ((!$allfiles && self::text_has_files($revision['content']))) { 230 $alltexts .= $revision['content']; 231 } 232 $revision['content'] = writer::with_context($context) 233 ->rewrite_pluginfile_urls([$subwikiid], 'mod_wiki', 'attachments', $subwikiid, $revision['content']); 234 } 235 } 236 $comments = self::get_page_comments($user, $context, $entry['page']['id'], !array_key_exists('userid', $entry['page'])); 237 if ($comments) { 238 $entry['page']['comments'] = $comments; 239 } 240 writer::with_context($context)->export_related_data([$subwikiid], $page, $entry); 241 } 242 243 if ($allfiles) { 244 // Export all files. 245 writer::with_context($context)->export_area_files([$subwikiid], 'mod_wiki', 'attachments', $subwikiid); 246 } else { 247 // Analyze which files are used in the texts. 248 self::export_used_files($context, $subwikiid, $alltexts); 249 } 250 } 251 252 /** 253 * Retrieves page comments 254 * 255 * We can not use \core_comment\privacy\provider::export_comments() because it expects each item to have a separate 256 * subcontext and we store wiki pages as related data to subwiki because the files are shared between pages. 257 * 258 * @param stdClass $user 259 * @param \context $context 260 * @param int $pageid 261 * @param bool $onlyforthisuser 262 * @return array 263 */ 264 protected static function get_page_comments($user, \context $context, $pageid, $onlyforthisuser = true) { 265 global $USER, $DB; 266 $params = [ 267 'contextid' => $context->id, 268 'commentarea' => 'wiki_page', 269 'itemid' => $pageid 270 ]; 271 $sql = "SELECT c.id, c.content, c.format, c.timecreated, c.userid 272 FROM {comments} c 273 WHERE c.contextid = :contextid AND 274 c.commentarea = :commentarea AND 275 c.itemid = :itemid"; 276 if ($onlyforthisuser) { 277 $sql .= " AND c.userid = :userid"; 278 $params['userid'] = $USER->id; 279 } 280 $sql .= " ORDER BY c.timecreated DESC"; 281 282 $rs = $DB->get_recordset_sql($sql, $params); 283 $comments = []; 284 foreach ($rs as $record) { 285 if ($record->userid != $user->id) { 286 // Clean HTML in comments that were added by other users. 287 $comment = ['content' => format_text($record->content, $record->format, ['context' => $context])]; 288 } else { 289 // Export comments made by this user as they are stored. 290 $comment = ['content' => $record->content, 'contentformat' => $record->format]; 291 } 292 $comment += [ 293 'time' => transform::datetime($record->timecreated), 294 'userid' => transform::user($record->userid), 295 ]; 296 $comments[] = (object)$comment; 297 } 298 $rs->close(); 299 return $comments; 300 } 301 302 /** 303 * Check if text has embedded files 304 * 305 * @param string $str 306 * @return bool 307 */ 308 protected static function text_has_files($str) { 309 return strpos($str, '@@PLUGINFILE@@') !== false; 310 } 311 312 /** 313 * Analyze which files are used in the texts and export 314 * @param context $context 315 * @param int $subwikiid 316 * @param string $alltexts 317 * @return int|void 318 */ 319 protected static function export_used_files($context, $subwikiid, $alltexts) { 320 if (!self::text_has_files($alltexts)) { 321 return; 322 } 323 $fs = get_file_storage(); 324 $files = $fs->get_area_files($context->id, 'mod_wiki', 'attachments', $subwikiid, 325 'filepath, filename', false); 326 if (empty($files)) { 327 return; 328 } 329 usort($files, function($file1, $file2) { 330 return strcmp($file2->get_filepath(), $file1->get_filename()); 331 }); 332 foreach ($files as $file) { 333 $filepath = $file->get_filepath() . $file->get_filename(); 334 $needles = ['@@PLUGINFILE@@' . s($filepath), 335 '@@PLUGINFILE@@' . $filepath, 336 '@@PLUGINFILE@@' . str_replace(' ', '%20', $filepath), 337 '@@PLUGINFILE@@' . s($filepath), 338 '@@PLUGINFILE@@' . s(str_replace(' ', '%20', $filepath)) 339 ]; 340 $needles = array_unique($needles); 341 $newtext = str_replace($needles, '', $alltexts); 342 if ($newtext !== $alltexts) { 343 $alltexts = $newtext; 344 writer::with_context($context)->export_file([$subwikiid], $file); 345 if (!self::text_has_files($alltexts)) { 346 return; 347 } 348 } 349 } 350 } 351 352 /** 353 * Export all user data for the specified user, in the specified contexts. 354 * 355 * @param approved_contextlist $contextlist The approved contexts to export information for. 356 */ 357 public static function export_user_data(approved_contextlist $contextlist) { 358 global $DB; 359 360 foreach ($contextlist as $context) { 361 if ($context->contextlevel != CONTEXT_MODULE) { 362 continue; 363 } 364 $user = $contextlist->get_user(); 365 366 $rs = $DB->get_recordset_sql('SELECT w.wikimode, s.id AS subwikiid, 367 s.groupid AS subwikigroupid, s.userid AS subwikiuserid, 368 p.id AS pageid, p.userid AS pageuserid, p.title, p.cachedcontent, p.timecreated AS pagetimecreated, 369 p.timemodified AS pagetimemodified, p.timerendered AS pagetimerendered, p.pageviews, p.readonly, 370 v.id AS versionid, v.content, v.contentformat, v.version, v.timecreated AS versiontimecreated, 371 l.id AS lockid, l.sectionname, l.lockedat 372 FROM {course_modules} cm 373 JOIN {wiki} w ON w.id = cm.instance 374 JOIN {wiki_subwikis} s ON cm.instance = s.wikiid 375 LEFT JOIN {wiki_pages} p ON p.subwikiid = s.id 376 LEFT JOIN {wiki_versions} v ON v.pageid = p.id AND v.userid = :user4 377 LEFT JOIN {wiki_locks} l ON l.pageid = p.id AND l.userid = :user5 378 WHERE cm.id = :cmid AND (s.userid = :user1 OR p.userid = :user2 OR v.userid = :user3 OR l.userid = :user6 OR 379 EXISTS (SELECT 1 FROM {comments} com WHERE com.itemid = p.id AND com.commentarea = :commentarea 380 AND com.contextid = :ctxid AND com.userid = :user7) 381 ) 382 ORDER BY s.id, p.id, v.id', 383 ['cmid' => $context->instanceid, 384 'user1' => $user->id, 'user2' => $user->id, 'user3' => $user->id, 'user4' => $user->id, 385 'user5' => $user->id, 'user6' => $user->id, 'user7' => $user->id, 'commentarea' => 'wiki_page', 386 'ctxid' => $context->id]); 387 388 if (!$rs->current()) { 389 $rs->close(); 390 continue; 391 } 392 393 $subwiki = []; 394 $wikimode = null; 395 foreach ($rs as $record) { 396 if ($wikimode === null) { 397 $wikimode = $record->wikimode; 398 } 399 if (!isset($subwiki[$record->subwikiid])) { 400 self::export_subwiki($user, $context, $subwiki, $wikimode); 401 $subwiki = [$record->subwikiid => [ 402 'groupid' => $record->subwikigroupid, 403 'userid' => $record->subwikiuserid ? transform::user($record->subwikiuserid) : 0, 404 'pages' => [] 405 ]]; 406 } 407 408 if (!$record->pageid) { 409 // This is an empty individual wiki. 410 continue; 411 } 412 413 // Prepend page title with the page id to guarantee uniqueness. 414 $pagetitle = format_string($record->title, true, ['context' => $context]); 415 $page = $record->pageid . ' ' . $pagetitle; 416 if (!isset($subwiki[$record->subwikiid]['pages'][$page])) { 417 // Export basic details about the page. 418 $subwiki[$record->subwikiid]['pages'][$page] = ['page' => [ 419 'id' => $record->pageid, 420 'title' => $pagetitle, 421 'cachedcontent' => $record->cachedcontent, 422 ]]; 423 if ($record->pageuserid == $user->id) { 424 // This page belongs to this user. Export all details. 425 $subwiki[$record->subwikiid]['pages'][$page]['page'] += [ 426 'userid' => transform::user($user->id), 427 'timecreated' => transform::datetime($record->pagetimecreated), 428 'timemodified' => transform::datetime($record->pagetimemodified), 429 'timerendered' => transform::datetime($record->pagetimerendered), 430 'pageviews' => $record->pageviews, 431 'readonly' => $record->readonly, 432 ]; 433 434 $subwiki[$record->subwikiid]['pages'][$page]['page']['userid'] = transform::user($user->id); 435 } 436 } 437 438 if ($record->versionid) { 439 $subwiki[$record->subwikiid]['pages'][$page]['revisions'][$record->versionid] = [ 440 'content' => $record->content, 441 'contentformat' => $record->contentformat, 442 'version' => $record->version, 443 'timecreated' => transform::datetime($record->versiontimecreated) 444 ]; 445 } 446 447 if ($record->lockid) { 448 $subwiki[$record->subwikiid]['pages'][$page]['locks'][$record->lockid] = [ 449 'sectionname' => $record->sectionname, 450 'lockedat' => transform::datetime($record->lockedat), 451 ]; 452 } 453 454 } 455 self::export_subwiki($user, $context, $subwiki, $wikimode); 456 457 if ($subwiki) { 458 // Export wiki itself. 459 $contextdata = helper::get_context_data($context, $user); 460 helper::export_context_files($context, $user); 461 writer::with_context($context)->export_data([], $contextdata); 462 } 463 464 $rs->close(); 465 } 466 } 467 468 /** 469 * Delete all data for all users in the specified context. 470 * 471 * @param context $context The specific context to delete data for. 472 */ 473 public static function delete_data_for_all_users_in_context(context $context) { 474 global $DB; 475 476 if ($context->contextlevel != CONTEXT_MODULE) { 477 return; 478 } 479 480 $subwikis = $DB->get_fieldset_sql('SELECT s.id 481 FROM {course_modules} cm 482 JOIN {modules} m ON m.name = :wiki AND cm.module = m.id 483 JOIN {wiki_subwikis} s ON s.wikiid = cm.instance 484 WHERE cm.id = :cmid', 485 ['cmid' => $context->instanceid, 'wiki' => 'wiki']); 486 if (!$subwikis) { 487 return; 488 } 489 490 $fs = get_file_storage(); 491 $fs->delete_area_files($context->id, 'mod_wiki', 'attachments'); 492 493 \core_tag\privacy\provider::delete_item_tags($context, 'mod_wiki', 'page'); 494 495 \core_comment\privacy\provider::delete_comments_for_all_users($context, 'mod_wiki', 'wiki_page'); 496 497 list($sql, $params) = $DB->get_in_or_equal($subwikis); 498 $DB->delete_records_select('wiki_locks', 'pageid IN (SELECT id FROM {wiki_pages} WHERE subwikiid '.$sql.')', $params); 499 $DB->delete_records_select('wiki_versions', 'pageid IN (SELECT id FROM {wiki_pages} WHERE subwikiid '.$sql.')', $params); 500 $DB->delete_records_select('wiki_synonyms', 'subwikiid '.$sql, $params); 501 $DB->delete_records_select('wiki_links', 'subwikiid '.$sql, $params); 502 $DB->delete_records_select('wiki_pages', 'subwikiid '.$sql, $params); 503 $DB->delete_records_select('wiki_subwikis', 'id '.$sql, $params); 504 505 $DB->delete_records('tag_instance', ['contextid' => $context->id, 'component' => 'mod_wiki', 'itemtype' => 'page']); 506 } 507 508 /** 509 * Delete all user data for the specified user, in the specified contexts. 510 * 511 * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. 512 */ 513 public static function delete_data_for_user(approved_contextlist $contextlist) { 514 global $DB; 515 516 $contextids = $contextlist->get_contextids(); 517 518 if (!$contextids) { 519 return; 520 } 521 522 // Remove only individual subwikis. Contributions to collaborative wikis is not considered personal contents. 523 list($ctxsql, $ctxparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED); 524 $subwikis = $DB->get_records_sql_menu('SELECT s.id, ctx.id AS ctxid 525 FROM {context} ctx 526 JOIN {course_modules} cm ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextmod 527 JOIN {modules} m ON m.name = :wiki AND cm.module = m.id 528 JOIN {wiki_subwikis} s ON s.wikiid = cm.instance AND s.userid = :userid 529 WHERE ctx.id ' . $ctxsql, 530 ['userid' => (int)$contextlist->get_user()->id, 'wiki' => 'wiki', 'contextmod' => CONTEXT_MODULE] + $ctxparams); 531 532 if ($subwikis) { 533 // We found individual subwikis that need to be deleted completely. 534 535 $fs = get_file_storage(); 536 foreach ($subwikis as $subwikiid => $contextid) { 537 $fs->delete_area_files($contextid, 'mod_wiki', 'attachments', $subwikiid); 538 \core_comment\privacy\provider::delete_comments_for_all_users_select(context::instance_by_id($contextid), 539 'mod_wiki', 'wiki_page', "IN (SELECT id FROM {wiki_pages} WHERE subwikiid=:subwikiid)", 540 ['subwikiid' => $subwikiid]); 541 } 542 543 list($sql, $params) = $DB->get_in_or_equal(array_keys($subwikis), SQL_PARAMS_NAMED); 544 545 $DB->execute("DELETE FROM {tag_instance} WHERE component=:component AND itemtype=:itemtype AND itemid IN 546 (SELECT id FROM {wiki_pages} WHERE subwikiid $sql)", 547 ['component' => 'mod_wiki', 'itemtype' => 'page'] + $params); 548 549 $DB->delete_records_select('wiki_locks', 'pageid IN (SELECT id FROM {wiki_pages} WHERE subwikiid ' . $sql . ')', 550 $params); 551 $DB->delete_records_select('wiki_versions', 'pageid IN (SELECT id FROM {wiki_pages} WHERE subwikiid ' . $sql . ')', 552 $params); 553 $DB->delete_records_select('wiki_synonyms', 'subwikiid ' . $sql, $params); 554 $DB->delete_records_select('wiki_links', 'subwikiid ' . $sql, $params); 555 $DB->delete_records_select('wiki_pages', 'subwikiid ' . $sql, $params); 556 $DB->delete_records_select('wiki_subwikis', 'id ' . $sql, $params); 557 } 558 559 // Remove comments made by this user on all other wiki pages. 560 \core_comment\privacy\provider::delete_comments_for_user($contextlist, 'mod_wiki', 'wiki_page'); 561 } 562 563 /** 564 * Delete multiple users within a single context. 565 * 566 * @param approved_userlist $userlist The approved context and user information to delete information for. 567 */ 568 public static function delete_data_for_users(approved_userlist $userlist) { 569 global $DB; 570 $context = $userlist->get_context(); 571 $userids = $userlist->get_userids(); 572 573 if ($context->contextlevel != CONTEXT_MODULE) { 574 return; 575 } 576 577 // Remove only individual subwikis. Contributions to collaborative wikis is not considered personal contents. 578 list($insql, $inparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); 579 $params = [ 580 'wiki' => 'wiki', 581 'contextmod' => CONTEXT_MODULE, 582 'contextid' => $context->id, 583 ]; 584 585 $params = array_merge($inparams, $params); 586 $sql = "SELECT s.id 587 FROM {context} ctx 588 JOIN {course_modules} cm ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextmod 589 JOIN {modules} m ON m.name = :wiki AND cm.module = m.id 590 JOIN {wiki_subwikis} s ON s.wikiid = cm.instance 591 WHERE ctx.id = :contextid 592 AND s.userid {$insql}"; 593 594 $subwikis = $DB->get_fieldset_sql($sql, $params); 595 596 if ($subwikis) { 597 // We found individual subwikis that need to be deleted completely. 598 599 $fs = get_file_storage(); 600 foreach ($subwikis as $subwikiid) { 601 $fs->delete_area_files($context->id, 'mod_wiki', 'attachments', $subwikiid); 602 \core_comment\privacy\provider::delete_comments_for_all_users_select(context::instance_by_id($context->id), 603 'mod_wiki', 'wiki_page', "IN (SELECT id FROM {wiki_pages} WHERE subwikiid=:subwikiid)", 604 ['subwikiid' => $subwikiid]); 605 } 606 607 list($insql, $inparams) = $DB->get_in_or_equal($subwikis, SQL_PARAMS_NAMED); 608 $params = ['component' => 'mod_wiki', 'itemtype' => 'page']; 609 $params = array_merge($inparams, $params); 610 $sql = "DELETE FROM {tag_instance} 611 WHERE component=:component 612 AND itemtype=:itemtype 613 AND itemid IN 614 (SELECT id 615 FROM {wiki_pages} 616 WHERE subwikiid $insql)"; 617 618 $DB->execute($sql, $params); 619 620 $DB->delete_records_select('wiki_locks', "pageid IN (SELECT id FROM {wiki_pages} WHERE subwikiid {$insql})", $params); 621 $DB->delete_records_select('wiki_versions', "pageid IN (SELECT id FROM {wiki_pages} WHERE subwikiid {$insql})", 622 $params); 623 $DB->delete_records_select('wiki_synonyms', "subwikiid {$insql}", $params); 624 $DB->delete_records_select('wiki_links', "subwikiid {$insql}", $params); 625 $DB->delete_records_select('wiki_pages', "subwikiid {$insql}", $params); 626 $DB->delete_records_select('wiki_subwikis', "id {$insql}", $params); 627 } 628 629 // Remove comments made by this user on all other wiki pages. 630 \core_comment\privacy\provider::delete_comments_for_users($userlist, 'mod_wiki', 'wiki_page'); 631 } 632 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body