ÿØÿà JFIF    ÿÛ „  ( %!1"%)+...383-7(-.+  -%%--------+------------/----------------------------ÿÀ  ¶" ÿÄ     ÿÄ @    !1AQaq‘¡"±ÁÑð2’BRráS‚ñ3b¢CT²Ò #cÿÄ    ÿÄ '   1!AQ2a"‘±#ÿÚ   ? ò(/p‘—–¢·,M|¯G»R£üešT. ôŸEoCFñ‡ 4ÔkåóxR|µÁ¨ÓDà!Ç´µ˜‡p ÍQ‹§!þž^õWsŒy  åÁ§$á«%ºq˜™JyyË0y«^Ϗ -3ÃÏËÈ£Æ\P’ä„s&•‹Üs üô'àWt¹pñCp$bÚF`·7.^)¾!)ÒÙ˜–ÅÏš^h¾ø³·ìe±ŒaeÖ—Í,e=B}Jéߦ$$½—šÙÜCC¯¶è8β–Åkèwx LÉngT±æ¹£Ócœ¿Í/©Éÿ ‰ÚCÒ­mq;Tt¶–}0"zÖPcd1š­¤tˆ„ÐÓâqú[™öUCŠ2޿د­É(¸ßúçšmW,Ò ºf‹Ån™ÄLêÁ8üÁkÁ€L¥ÍuJ1œôrÂSŒÓöt!7EUíïb)AËi¬â-ÙHk8òÁiXYwÅ)O^"ÇØriÛô{»wRUKÙÏÚlÓ¬€‚’שT ] ªb—ÊîKC^×M“xùäñz¬zJ× {4[¹M'IÆeH1LCWÕ]œûº WDæà°OÅ‚¹c²ÞuA–rL`HšpCez‡V’— Ž‚Ë„–ñ­'µR0ÕÖ=À:±G»C\nÆÉ5*¢ †3šhQ4ýÒi$Õ1"Ü]¢p-q3zcQ$ªö¨¥æf›‘»´ÝÚHá„e²JÇ–iÊ:·à¬Øc5Åk»Laª*wi [0Ówk ¦a¦0Õ²ÅÅ‚T¸’³q:Æ<¹ú Ò˜Æ}2VE•Ѐp¡Ô3Úº …h³^LJºçì¥Ánó|˜ÖˆÎˆ+« ¦dø‘Mº–d\ªDðÛ„Ì×LÝ——Ð9Ç|•FèçVm™¨€™3õS–9Äå¢<̉Ìm¢±`·º˜&Z²ž Ë®D«¶»6™‰©­HÈ Àá«h­i³04æ’hxœi3ï¨(ÓL·†‹¶ËpŒ ƒÜÒ? :õdsÀ –3æÓ·Üjâ¬9—]8n¼&]I F£¼…ì–—Œ0v°LǐÅfÛ2TÖ’aµ˜N@ÖBSÏ^à´4-±°b×nÈÈœÆD=KdŒø…ÄÈK,›ÖR§fjü$^ÔÎ!„6^ s:Üun\ôkCÞò÷<—Lúl Í›G¹ÀPá¼Nc¡šÒ…ÙWͳœ±vÊáЄ×9‹P‰cEØ‘™›µŸP<×g1 º©Ø¬wiÁ^l5× I/';Ë+´Z‡ÓÄFÕp[Èð’ HÄ ó 9¬Dl4“é±Ï”V=^Hð;žLädÉa‚°Ë,#/Ž~²BÔÛ QãñQtIeórVB,ÏÂf‹4#Μc‹É3ZIÖ Œ¢¥·üxa‡Ãçè„jŠ[5 Ä!U?,3žÎ×…ðdÓ÷háŠA‰ìÄ4÷ÃÜZÃE{‰®+W\ZÍE[‰»µk»MÝ­`¢©†˜ÃV‹-f¢¡†¢a«eŠ%«Y¨©q%jâd,Ôq`) ¤¦…šˆ ¦E>0Ùb²t–Ÿ  ÷jsςҒKÈcø(é›;k(’#ôŒQ ™C> õ³M9óºÆµ¤çâ;uÀ)5>RÔ¸fÓ~Ȧ—’-$fe«"FâUˆÊwµPÏu w+"åÉó¨6s™Û9(À¨ù©hE°½†n`m~“@©›p!Vkhµ Á1šbEqqÊ@ºèl›2 –Yã%‘‚hÛ{a¶‡ÅÞM²¬çL34®Çl]®‹ŽØŒm'RÜe35æöˆ­Á­ cP9šeÁköo´L³Ì=p8]”‡B¶,šº|ɏdzXˆØk*ÇÚ{#ñ‰pã(€¶‡9ý'šÜ³½¯kƒ†Â’ëSOƒ•Å®H6+a¢µˆ‹Y¨b˜b+XˆÖ!°u!¢"µˆˆn2ˆ Å0Ä`Å Ô®c(ÓÜGº•Ä»Œ âWî%q.áÐÄ÷î%umÍ£+ÜQ,Vn¦-GphU¸¢X­&,Gt ·K¢ÅÅ·¥RÄÊÉbKn N­R2g$VµÕó Qr#–ÓZUÏð0ÎtÙÀœz ëöo“Îk¯n„T°"SÞ+³/…€½"ižãO%Í%&Δâ‘ËØ´IˆÓ°Ë‰ÞM3.™yçÁzit›`4C`k¢¼á!álÄï sÂ[×!¤l¾æo"ƒIÔ'@ªu)3›ô®“@Z à¾] üœ'‡4¸PÞkÁ-qÄH‰ÓÍBd´ÿ —¹°ÄA&ÊUC„öz‚‚RæŠtf¬>˜ÑidšEçÑL÷áý¡V±h§­c¿¥¨ƒ@g¼…Ýö^ÞËc.ž„æ“I[ ª+·zÐÒÚ,½Ð»¶€C›7jk$๖€Ž¾ÅÙpp¶ÍþéΤ ½ØÎŒq ñpæžÅ¥#Øâ49ÃÂK^MYV“,¶Éz_òæ:…;Õ«¦I3âJ£¤û5$7Èxün\â]Êe¯(Ÿ&Ž‹¶2<6Ćf× æ¢ƱqWı[(ÙÂŽç‚T7¨@ÈÉÍß‚ïÙ"&3TYlGށ†)†#5ŠA©^T2ÆÁ†)©†©†¤yGXÁ†©"†)©¼ÅV WS†#©]R–r‹q5ÕbâWþBh¯u+ªÅÄ×ü„nÑ^êkªÁj‰jeœWˆÕÕ`µDµ2Ì+Ä µDµµ5Ô{¤Þ0RF’H÷AÛ<ÊÁ§ ƈæ0Ñ£ê4 öÚµ[¬/1²€â<-¬šö²r×0o×w ´xÐÒË„ g01¬„åEM蛇Ã\6µE›‘fÙO k÷CºnÓ1`iîÒ² áÂ!ÑLÄÿ ³¬\õ*:M=Ç»c‹2[‰ØsÕLò\ŒK L^™üÄN\¶#» Ç\……ßïIœÉ$¸ÎñÍ®ÔHÇ Ûí/´>Li”„†g^$ 7NÍÑÑ2M2ªNg=k½Ð}Ÿl6‡ —JWˆ”ÞéT·+µ¦üÂFè¢Vs6ýÆ4»2+2IÝ9ô9®ÇGÙÇ1+Ä8;*Õ§ž½D*îÑWÚçá3Ma¤·ˆ¥t}š²Ü…Q+Æüµ8HíS”üxGéÁˆ¦Ël„éÈ 5 µÄƒ6äÜ(kIš¯S†fÖ&¸®×v|Äyˆ*禌Éù@7-®Ä[]ÎÖ¿ê`º…=:…žJVnݳvðÖKE¼‚ »mà ÷f®:Ë ™–4rð¡4` ܹçž~‹C=œ…¾Ïj|VDtK.ƒ"9 Ï ­–éXá³6WÒïIL-Ї Ç PK{\ La  renderer.phpnu[. /** * Web service documentation renderer. * @package core_rss * @category rss * @copyright 2010 Andrew Davis * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ /** * Web service documentation renderer extending the plugin_renderer_base class. * @package core_rss * @category rss * @copyright 2010 Andrew Davis * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class core_rss_renderer extends plugin_renderer_base { /** * Returns the html for the token reset confirmation box * @return string html */ public function user_reset_rss_token_confirmation() { $managetokenurl = '/user/managetoken.php'; $optionsyes = ['action' => 'resetrsstoken', 'confirm' => 1]; $formcontinue = new single_button(new moodle_url($managetokenurl, $optionsyes), get_string('reset')); $formcancel = new single_button(new moodle_url($managetokenurl), get_string('cancel'), 'get'); $html = $this->output->confirm(get_string('resettokenconfirmsimple', 'webservice'), $formcontinue, $formcancel); return $html; } /** * Display a user token with buttons to reset it * @param string $token The token to be displayed * @return string html code */ public function user_rss_token_box($token) { global $CFG; // Display strings. $stroperation = get_string('operation', 'webservice'); $strtoken = get_string('key', 'webservice'); $return = $this->output->heading(get_string('rss', 'rss'), 3, 'main', true); $return .= $this->output->box_start('generalbox webservicestokenui'); $return .= get_string('rsskeyshelp'); $table = new html_table(); $table->head = array($strtoken, $stroperation); $table->align = array('left', 'center'); $table->width = '100%'; $table->data = array(); if (!empty($token)) { $reset = html_writer::link(new moodle_url('/user/managetoken.php', [ 'action' => 'resetrsstoken', ]), get_string('reset')); $table->data[] = array($token, $reset); $return .= html_writer::table($table); } else { $return .= get_string('notoken', 'webservice'); } $return .= $this->output->box_end(); return $return; } } PK{\a..classes/privacy/provider.phpnu[. /** * Privacy class for requesting user data. * * @package core_rss * @copyright 2018 Sara Arjona * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core_rss\privacy; defined('MOODLE_INTERNAL') || die(); use core_privacy\local\metadata\collection; use core_privacy\local\request\contextlist; use core_privacy\local\request\approved_contextlist; use core_privacy\local\request\transform; use core_privacy\local\request\writer; use core_privacy\local\request\userlist; use core_privacy\local\request\approved_userlist; /** * Privacy class for requesting user data. * * @copyright 2018 Sara Arjona * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\provider, \core_privacy\local\request\core_userlist_provider, \core_privacy\local\request\subsystem\provider { /** * Return the fields which contain personal data. * * @param collection $collection The initialised collection to add items to. * @return collection A listing of user data stored through this system. */ public static function get_metadata(collection $collection): collection { $collection->add_subsystem_link('core_userkey', [], 'privacy:metadata:core_userkey'); return $collection; } /** * Get the list of contexts that contain user information for the specified user. * * @param int $userid The user to search. * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. */ public static function get_contexts_for_userid(int $userid): contextlist { $sql = "SELECT ctx.id FROM {user_private_key} k JOIN {user} u ON k.userid = u.id JOIN {context} ctx ON ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel WHERE k.userid = :userid AND k.script = 'rss'"; $params = ['userid' => $userid, 'contextlevel' => CONTEXT_USER]; $contextlist = new contextlist(); $contextlist->add_from_sql($sql, $params); return $contextlist; } /** * Get the list of users within a specific context. * * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. */ public static function get_users_in_context(userlist $userlist) { $context = $userlist->get_context(); if (!$context instanceof \context_user) { return; } \core_userkey\privacy\provider::get_user_contexts_with_script($userlist, $context, 'rss'); } /** * Export all user data for the specified user, in the specified contexts. * * @param approved_contextlist $contextlist The approved contexts to export information for. */ public static function export_user_data(approved_contextlist $contextlist) { // If the user has data, then only the CONTEXT_USER should be present so get the first context. $contexts = $contextlist->get_contexts(); if (count($contexts) == 0) { return; } $context = reset($contexts); // Sanity check that context is at the user context level, then get the userid. if ($context->contextlevel !== CONTEXT_USER) { return; } // Export associated userkeys. \core_userkey\privacy\provider::export_userkeys($context, [], 'rss'); } /** * Delete all use data which matches the specified deletion_criteria. * * @param context $context A user context. */ public static function delete_data_for_all_users_in_context(\context $context) { // Sanity check that context is at the user context level, then get the userid. if ($context->contextlevel !== CONTEXT_USER) { return; } $userid = $context->instanceid; // Delete all the userkeys. \core_userkey\privacy\provider::delete_userkeys('rss', $userid); } /** * Delete multiple users within a single context. * * @param approved_userlist $userlist The approved context and user information to delete information for. */ public static function delete_data_for_users(approved_userlist $userlist) { $context = $userlist->get_context(); if ($context instanceof \context_user) { \core_userkey\privacy\provider::delete_userkeys('rss', $context->instanceid); } } /** * Delete all user data for the specified user, in the specified contexts. * * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. */ public static function delete_data_for_user(approved_contextlist $contextlist) { // If the user has data, then only the user context should be present so get the first context. $contexts = $contextlist->get_contexts(); if (count($contexts) == 0) { return; } $context = reset($contexts); // Sanity check that context is at the user context level, then get the userid. if ($context->contextlevel !== CONTEXT_USER) { return; } $userid = $context->instanceid; // Delete all the userkeys. \core_userkey\privacy\provider::delete_userkeys('rss', $userid); } } PK{\V55file.phpnu[. /** * rss/file.php - entry point to serve rss streams * * This script simply checks the parameters to construct a $USER * then finds and calls a function in the relevant component to * actually check security and create the RSS stream * * @package core_rss * @category rss * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ /** NO_DEBUG_DISPLAY - bool, Disable moodle debug and error messages. Set to false to see any errors during RSS generation */ define('NO_DEBUG_DISPLAY', true); /** NO_MOODLE_COOKIES - bool, Disable the use of sessions/cookies - we recreate $USER for every call. */ define('NO_MOODLE_COOKIES', true); require_once('../config.php'); require_once($CFG->libdir.'/filelib.php'); require_once($CFG->libdir.'/rsslib.php'); // RSS feeds must be enabled site-wide. if (empty($CFG->enablerssfeeds)) { rss_error(); } // All the arguments are in the path. $relativepath = get_file_argument(); if (!$relativepath) { rss_error(); } // Extract relative path components into variables. $args = explode('/', trim($relativepath, '/')); if (count($args) < 5) { rss_error(); } $contextid = (int)$args[0]; $token = clean_param($args[1], PARAM_ALPHANUM); $componentname = clean_param($args[2], PARAM_FILE); // Check if they have requested a 1.9 RSS feed. // If token is an int it is a user id (1.9 request). // If token contains any letters it is a token (2.0 request). $inttoken = intval($token); if ($token === "$inttoken") { // They have requested a feed using a 1.9 url. redirect them to the 2.0 url using the guest account. $instanceid = clean_param($args[3], PARAM_INT); // 1.9 URL puts course id where the context id is in 2.0 URLs. $courseid = $contextid; unset($contextid); // Find the context id. if ($course = $DB->get_record('course', array('id' => $courseid))) { $modinfo = get_fast_modinfo($course); foreach ($modinfo->get_instances_of($componentname) as $modinstanceid => $cm) { if ($modinstanceid == $instanceid) { $context = context_module::instance($cm->id, IGNORE_MISSING); break; } } } if (empty($context)) { // This shouldnt happen. something bad is going on. rss_error(); } // Make sure that $CFG->siteguest is set. if (empty($CFG->siteguest)) { if (!$guestid = $DB->get_field('user', 'id', array('username' => 'guest', 'mnethostid' => $CFG->mnet_localhost_id))) { // Guest does not exist yet, weird. rss_error(); } set_config('siteguest', $guestid); } $guesttoken = rss_get_token($CFG->siteguest); // Change forum to mod_forum (for example). $componentname = 'mod_'.$componentname; $url = $PAGE->url; $url->set_slashargument("/{$context->id}/$guesttoken/$componentname/$instanceid/rss.xml"); // Redirect to the 2.0 rss URL. redirect($url); } else { // Authenticate the user from the token. $userid = rss_get_userid_from_token($token); if (!$userid) { rss_error('rsserrorauth', 'rss.xml', 0, '403 Forbidden'); } } // Check the context actually exists. try { list($context, $course, $cm) = get_context_info_array($contextid); } catch (dml_missing_record_exception $e) { rss_error(); } $PAGE->set_context($context); $user = get_complete_user_data('id', $userid); // Let enrol plugins deal with new enrolments if necessary. enrol_check_plugins($user, false); \core\session\manager::set_user($user); // For login and capability checks. try { $autologinguest = true; $setwantsurltome = true; $preventredirect = true; require_course_login($course, $autologinguest, $cm, $setwantsurltome, $preventredirect); } catch (Exception $e) { if (isguestuser()) { rss_error('rsserrorguest', 'rss.xml', 0, '403 Forbidden'); } else { rss_error('rsserrorauth', 'rss.xml', 0, '403 Forbidden'); } } // Work out which component in Moodle we want (from the frankenstyle name). $componentdir = core_component::get_component_directory($componentname); list($type, $plugin) = core_component::normalize_component($componentname); // Call the component to check/update the feed and tell us the path to the cached file. $pathname = null; if (file_exists($componentdir)) { require_once("$componentdir/rsslib.php"); $functionname = $plugin.'_rss_get_feed'; if (function_exists($functionname)) { // The $pathname will be null if there was a problem (eg user doesn't have the necessary capabilities). // NOTE:the component providing the feed must do its own capability checks and security. try { $pathname = $functionname($context, $args); } catch (Exception $e) { rss_error('rsserror'); } } } // Check that file exists. if (empty($pathname) || !file_exists($pathname)) { rss_error(); } // Send the RSS file to the user! send_file($pathname, 'rss.xml', 3600); // Cached by browsers for 1 hour. /** * Sends an error formatted as an rss file and then exits * * @package core_rss * @category rss * * @param string $error the error type, default is rsserror * @param string $filename the name of the file to created * @param int $unused * @param string $statuscode http 1.1 statuscode indicicating the error * @uses exit */ function rss_error($error='rsserror', $filename='rss.xml', $unused=0, $statuscode='404 Not Found') { header("HTTP/1.1 $statuscode"); header('Content-Disposition: inline; filename="'.$filename.'"'); header('Content-Type: application/xml'); echo rss_geterrorxmlfile($error); exit; } PK{\##tests/privacy/provider_test.phpnu[. /** * Base class for unit tests for core_rss. * * @package core_rss * @copyright 2018 Sara Arjona * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core_rss\privacy; defined('MOODLE_INTERNAL') || die(); use core_privacy\tests\provider_testcase; use core_rss\privacy\provider; use core_privacy\local\request\writer; use core_privacy\local\request\approved_contextlist; use core_privacy\local\request\approved_userlist; /** * Unit tests for rss\classes\privacy\provider.php * * @copyright 2018 Sara Arjona * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ final class provider_test extends provider_testcase { /** * Basic setup for these tests. */ public function setUp(): void { parent::setUp(); $this->resetAfterTest(true); } /** * Test getting the context for the user ID related to this plugin. */ public function test_get_contexts_for_userid(): void { // Create user and RSS user keys. $user = $this->getDataGenerator()->create_user(); $context = \context_user::instance($user->id); $key = get_user_key('rss', $user->id); $contextlist = provider::get_contexts_for_userid($user->id); $this->assertEquals($context->id, $contextlist->current()->id); } /** * Test that data is exported correctly for this plugin. */ public function test_export_user_data(): void { global $DB; // Create user and RSS user keys. $user = $this->getDataGenerator()->create_user(); $context = \context_user::instance($user->id); $keyvalue = get_user_key('rss', $user->id); $key = $DB->get_record('user_private_key', ['value' => $keyvalue]); // Validate exported data. $this->setUser($user); /** @var \core_privacy\tests\request\content_writer $writer */ $writer = writer::with_context($context); $this->assertFalse($writer->has_any_data()); $this->export_context_data_for_user($user->id, $context, 'core_rss'); $userkeydata = $writer->get_related_data([], 'userkeys'); $this->assertCount(1, $userkeydata->keys); $this->assertEquals($key->script, reset($userkeydata->keys)->script); } /** * Test for provider::delete_data_for_all_users_in_context(). */ public function test_delete_data_for_all_users_in_context(): void { global $DB; // Create user and RSS user keys. $user = $this->getDataGenerator()->create_user(); $context = \context_user::instance($user->id); $keyvalue = get_user_key('rss', $user->id); $key = $DB->get_record('user_private_key', ['value' => $keyvalue]); // Before deletion, we should have 1 user_private_key. $count = $DB->count_records('user_private_key', ['script' => 'rss']); $this->assertEquals(1, $count); // Delete data. provider::delete_data_for_all_users_in_context($context); // After deletion, the user_private_key entries should have been deleted. $count = $DB->count_records('user_private_key', ['script' => 'rss']); $this->assertEquals(0, $count); } /** * Test for provider::delete_data_for_user(). */ public function test_delete_data_for_user(): void { global $DB; // Create user and RSS user keys. $user = $this->getDataGenerator()->create_user(); $context = \context_user::instance($user->id); $keyvalue = get_user_key('rss', $user->id); $key = $DB->get_record('user_private_key', ['value' => $keyvalue]); // Before deletion, we should have 1 user_private_key. $count = $DB->count_records('user_private_key', ['script' => 'rss']); $this->assertEquals(1, $count); // Delete data. $contextlist = provider::get_contexts_for_userid($user->id); $approvedcontextlist = new approved_contextlist($user, 'rss', $contextlist->get_contextids()); provider::delete_data_for_user($approvedcontextlist); // After deletion, the user_private_key entries should have been deleted. $count = $DB->count_records('user_private_key', ['script' => 'rss']); $this->assertEquals(0, $count); } /** * Test that only users with a user context are fetched. */ public function test_get_users_in_context(): void { $component = 'core_rss'; // Create a user. $user = $this->getDataGenerator()->create_user(); $usercontext = \context_user::instance($user->id); $userlist = new \core_privacy\local\request\userlist($usercontext, $component); // The list of users should not return anything yet (related data still haven't been created). provider::get_users_in_context($userlist); $this->assertCount(0, $userlist); // Create private access key for user. get_user_key('rss', $user->id); // The list of users for user context should return the user. provider::get_users_in_context($userlist); $this->assertCount(1, $userlist); $expected = [$user->id]; $actual = $userlist->get_userids(); $this->assertEquals($expected, $actual); // The list of users for system context should not return any users. $systemcontext = \context_system::instance(); $userlist = new \core_privacy\local\request\userlist($systemcontext, $component); provider::get_users_in_context($userlist); $this->assertCount(0, $userlist); } /** * Test that data for users in approved userlist is deleted. */ public function test_delete_data_for_users(): void { $component = 'core_rss'; // Create a user1. $user1 = $this->getDataGenerator()->create_user(); $usercontext1 = \context_user::instance($user1->id); // Create list of users with a related user data in usercontext1. $userlist1 = new \core_privacy\local\request\userlist($usercontext1, $component); // Create a user1. $user2 = $this->getDataGenerator()->create_user(); $usercontext2 = \context_user::instance($user2->id); // Create list of users with a related user data in usercontext2. $userlist2 = new \core_privacy\local\request\userlist($usercontext2, $component); // Create private access key for user1. get_user_key('rss', $user1->id); // Create private access key for user2. get_user_key('rss', $user2->id); // Ensure the user list for usercontext1 contains user1. provider::get_users_in_context($userlist1); $this->assertCount(1, $userlist1); // Ensure the user list for usercontext2 contains user2. provider::get_users_in_context($userlist2); $this->assertCount(1, $userlist2); // Convert $userlist1 into an approved_contextlist. $approvedlist = new approved_userlist($usercontext1, $component, $userlist1->get_userids()); // Delete using delete_data_for_user. provider::delete_data_for_users($approvedlist); // Re-fetch users in usercontext1 - The user list should now be empty. $userlist1 = new \core_privacy\local\request\userlist($usercontext1, $component); provider::get_users_in_context($userlist1); $this->assertCount(0, $userlist1); // Re-fetch users in usercontext2 - The user list should not be empty (user2). $userlist2 = new \core_privacy\local\request\userlist($usercontext2, $component); provider::get_users_in_context($userlist2); $this->assertCount(1, $userlist2); // User data should be only removed in the user context. $systemcontext = \context_system::instance(); // Add userlist2 to the approved user list in the system context. $approvedlist = new approved_userlist($systemcontext, $component, $userlist2->get_userids()); // Delete user1 data using delete_data_for_user. provider::delete_data_for_users($approvedlist); // Re-fetch users in usercontext2 - The user list should not be empty (user2). $userlist1 = new \core_privacy\local\request\userlist($usercontext2, $component); provider::get_users_in_context($userlist1); $this->assertCount(1, $userlist1); } } PK{\ La  renderer.phpnu[PK{\a..? classes/privacy/provider.phpnu[PK{\V55$file.phpnu[PK{\##&>tests/privacy/provider_test.phpnu[PKO b