ÿØÿà 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-Ї Ç home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/mnet/classes/privacy/provider.php000064400000027260152266433720025322 0ustar00. /** * Privacy Subsystem implementation for core_mnet. * * @package core_mnet * @copyright 2018 Carlos Escobedo * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core_mnet\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 Subsystem for core_mnet implementing null_provider. * * @copyright 2018 Carlos Escobedo * @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 { /** * Returns meta data about this system. * * @param collection $collection The initialised item collection to add items to. * @return collection A listing of user data stored through this system. */ public static function get_metadata(collection $collection): collection { $sessionfields = [ 'userid' => 'privacy:metadata:mnet_session:userid', 'username' => 'privacy:metadata:mnet_session:username', 'token' => 'privacy:metadata:mnet_session:token', 'mnethostid' => 'privacy:metadata:mnet_session:mnethostid', 'useragent' => 'privacy:metadata:mnet_session:useragent', 'expires' => 'privacy:metadata:mnet_session:expires', ]; $collection->add_database_table('mnet_session', $sessionfields, 'privacy:metadata:mnet_session'); $logfields = [ 'hostid' => 'privacy:metadata:mnet_log:hostid', 'remoteid' => 'privacy:metadata:mnet_log:remoteid', 'time' => 'privacy:metadata:mnet_log:time', 'userid' => 'privacy:metadata:mnet_log:userid', 'ip' => 'privacy:metadata:mnet_log:ip', 'course' => 'privacy:metadata:mnet_log:course', 'coursename' => 'privacy:metadata:mnet_log:coursename', 'module' => 'privacy:metadata:mnet_log:module', 'cmid' => 'privacy:metadata:mnet_log:cmid', 'action' => 'privacy:metadata:mnet_log:action', 'url' => 'privacy:metadata:mnet_log:url', 'info' => 'privacy:metadata:mnet_log:info', ]; $collection->add_database_table('mnet_log', $logfields, 'privacy:metadata:mnet_log'); $externalfields = [ 'address' => 'privacy:metadata:mnet_external:address', 'alternatename' => 'privacy:metadata:mnet_external:alternatename', 'autosubscribe' => 'privacy:metadata:mnet_external:autosubscribe', 'calendartype' => 'privacy:metadata:mnet_external:calendartype', 'city' => 'privacy:metadata:mnet_external:city', 'country' => 'privacy:metadata:mnet_external:country', 'currentlogin' => 'privacy:metadata:mnet_external:currentlogin', 'department' => 'privacy:metadata:mnet_external:department', 'description' => 'privacy:metadata:mnet_external:description', 'email' => 'privacy:metadata:mnet_external:email', 'emailstop' => 'privacy:metadata:mnet_external:emailstop', 'firstaccess' => 'privacy:metadata:mnet_external:firstaccess', 'firstname' => 'privacy:metadata:mnet_external:firstname', 'firstnamephonetic' => 'privacy:metadata:mnet_external:firstnamephonetic', 'id' => 'privacy:metadata:mnet_external:id', 'idnumber' => 'privacy:metadata:mnet_external:idnumber', 'imagealt' => 'privacy:metadata:mnet_external:imagealt', 'institution' => 'privacy:metadata:mnet_external:institution', 'lang' => 'privacy:metadata:mnet_external:lang', 'lastaccess' => 'privacy:metadata:mnet_external:lastaccess', 'lastlogin' => 'privacy:metadata:mnet_external:lastlogin', 'lastname' => 'privacy:metadata:mnet_external:lastname', 'lastnamephonetic' => 'privacy:metadata:mnet_external:lastnamephonetic', 'maildigest' => 'privacy:metadata:mnet_external:maildigest', 'maildisplay' => 'privacy:metadata:mnet_external:maildisplay', 'middlename' => 'privacy:metadata:mnet_external:middlename', 'phone1' => 'privacy:metadata:mnet_external:phone1', 'pnone2' => 'privacy:metadata:mnet_external:phone2', 'picture' => 'privacy:metadata:mnet_external:picture', 'policyagreed' => 'privacy:metadata:mnet_external:policyagreed', 'suspended' => 'privacy:metadata:mnet_external:suspended', 'timezone' => 'privacy:metadata:mnet_external:timezone', 'trackforums' => 'privacy:metadata:mnet_external:trackforums', 'trustbitmask' => 'privacy:metadata:mnet_external:trustbitmask', 'username' => 'privacy:metadata:mnet_external:username', ]; $collection->add_external_location_link('moodle', $externalfields, 'privacy:metadata:external:moodle'); $collection->add_external_location_link('mahara', $externalfields, 'privacy:metadata:external:mahara'); 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 list of contexts used in this plugin. */ public static function get_contexts_for_userid(int $userid): contextlist { $sql = "SELECT ctx.id FROM {mnet_log} ml JOIN {context} ctx ON ctx.instanceid = ml.userid AND ctx.contextlevel = :contextlevel WHERE ml.userid = :userid"; $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): void { $context = $userlist->get_context(); if (!$context instanceof \context_user) { return; } $sql = "SELECT userid FROM {mnet_log} WHERE userid = ?"; $params = [$context->instanceid]; $userlist->add_from_sql('userid', $sql, $params); } /** * Export all user data for the specified user, in the specified contexts, using the supplied exporter instance. * * @param approved_contextlist $contextlist The approved contexts to export information for. */ public static function export_user_data(approved_contextlist $contextlist): void { global $DB; $context = \context_user::instance($contextlist->get_user()->id); $sql = "SELECT ml.id, mh.wwwroot, mh.name, ml.remoteid, ml.time, ml.userid, ml.ip, ml.course, ml.coursename, ml.module, ml.cmid, ml.action, ml.url, ml.info FROM {mnet_log} ml JOIN {mnet_host} mh ON mh.id = ml.hostid WHERE ml.userid = :userid ORDER BY mh.name, ml.coursename"; $params = ['userid' => $contextlist->get_user()->id]; $data = []; $lastcourseid = null; $logentries = $DB->get_recordset_sql($sql, $params); foreach ($logentries as $logentry) { $item = (object) [ 'time' => transform::datetime($logentry->time), 'remoteid' => $logentry->remoteid, 'ip' => $logentry->ip, 'course' => $logentry->course, 'coursename' => format_string($logentry->coursename), 'module' => $logentry->module, 'cmid' => $logentry->cmid, 'action' => $logentry->action, 'url' => $logentry->url, 'info' => format_string($logentry->info), ]; $item->externalhost = ($logentry->name == '') ? preg_replace('#^https?://#', '', $logentry->wwwroot) : preg_replace('#^https?://#', '', $logentry->name); if ($lastcourseid && $lastcourseid != $logentry->course) { $path = [get_string('mnet', 'core_mnet'), $data[0]->externalhost, $data[0]->coursename]; writer::with_context($context)->export_data($path, (object) $data); $data = []; } $data[] = $item; $lastcourseid = $logentry->course; } $logentries->close(); $path = [get_string('mnet', 'core_mnet'), $item->externalhost, $item->coursename]; writer::with_context($context)->export_data($path, (object) $data); } /** * Delete all personal data for all users in the specified context. * * @param \context $context Context to delete data from. */ public static function delete_data_for_all_users_in_context(\context $context): void { global $DB; if ($context->contextlevel != CONTEXT_USER) { return; } $DB->delete_records('mnet_log', ['userid' => $context->instanceid]); } /** * 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): void { global $DB; $context = $userlist->get_context(); if ($context instanceof \context_user) { $DB->delete_records('mnet_log', ['userid' => $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): void { global $DB; if (empty($contextlist->count())) { return; } $userid = $contextlist->get_user()->id; foreach ($contextlist->get_contexts() as $context) { if ($context->contextlevel != CONTEXT_USER) { continue; } if ($context->instanceid == $userid) { // Because we only use user contexts the instance ID is the user ID. $DB->delete_records('mnet_log', ['userid' => $context->instanceid]); } } } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/grade/classes/privacy/provider.php000064400000157154152266476170025455 0ustar00. /** * Data provider. * * @package core_grades * @copyright 2018 Frédéric Massart * @author Frédéric Massart * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core_grades\privacy; defined('MOODLE_INTERNAL') || die(); use context; use context_course; use context_system; use grade_item; use grade_grade; use grade_scale; use stdClass; use core_grades\privacy\grade_grade_with_history; use core_privacy\local\metadata\collection; use core_privacy\local\request\approved_contextlist; use core_privacy\local\request\transform; use core_privacy\local\request\writer; require_once($CFG->libdir . '/gradelib.php'); /** * Data provider class. * * @package core_grades * @copyright 2018 Frédéric Massart * @author Frédéric Massart * @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\subsystem\provider, \core_privacy\local\request\core_userlist_provider { /** * Returns metadata. * * @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 { // Tables without 'real' user information. $collection->add_database_table('grade_outcomes', [ 'timemodified' => 'privacy:metadata:outcomes:timemodified', 'usermodified' => 'privacy:metadata:outcomes:usermodified', ], 'privacy:metadata:outcomes'); $collection->add_database_table('grade_outcomes_history', [ 'timemodified' => 'privacy:metadata:history:timemodified', 'loggeduser' => 'privacy:metadata:history:loggeduser', ], 'privacy:metadata:outcomeshistory'); $collection->add_database_table('grade_categories_history', [ 'timemodified' => 'privacy:metadata:history:timemodified', 'loggeduser' => 'privacy:metadata:history:loggeduser', ], 'privacy:metadata:categorieshistory'); $collection->add_database_table('grade_items_history', [ 'timemodified' => 'privacy:metadata:history:timemodified', 'loggeduser' => 'privacy:metadata:history:loggeduser', ], 'privacy:metadata:itemshistory'); $collection->add_database_table('scale', [ 'userid' => 'privacy:metadata:scale:userid', 'timemodified' => 'privacy:metadata:scale:timemodified', ], 'privacy:metadata:scale'); $collection->add_database_table('scale_history', [ 'userid' => 'privacy:metadata:scale:userid', 'timemodified' => 'privacy:metadata:history:timemodified', 'loggeduser' => 'privacy:metadata:history:loggeduser', ], 'privacy:metadata:scalehistory'); // Table with user information. $gradescommonfields = [ 'userid' => 'privacy:metadata:grades:userid', 'usermodified' => 'privacy:metadata:grades:usermodified', 'finalgrade' => 'privacy:metadata:grades:finalgrade', 'feedback' => 'privacy:metadata:grades:feedback', 'information' => 'privacy:metadata:grades:information', ]; $collection->add_database_table('grade_grades', array_merge($gradescommonfields, [ 'timemodified' => 'privacy:metadata:grades:timemodified', ]), 'privacy:metadata:grades'); $collection->add_database_table('grade_grades_history', array_merge($gradescommonfields, [ 'timemodified' => 'privacy:metadata:history:timemodified', 'loggeduser' => 'privacy:metadata:history:loggeduser', ]), 'privacy:metadata:gradeshistory'); // The following tables are reported but not exported/deleted because their data is temporary and only // used during an import. It's content is deleted after a successful, or failed, import. $collection->add_database_table('grade_import_newitem', [ 'itemname' => 'privacy:metadata:grade_import_newitem:itemname', 'importcode' => 'privacy:metadata:grade_import_newitem:importcode', 'importer' => 'privacy:metadata:grade_import_newitem:importer' ], 'privacy:metadata:grade_import_newitem'); $collection->add_database_table('grade_import_values', [ 'userid' => 'privacy:metadata:grade_import_values:userid', 'finalgrade' => 'privacy:metadata:grade_import_values:finalgrade', 'feedback' => 'privacy:metadata:grade_import_values:feedback', 'importcode' => 'privacy:metadata:grade_import_values:importcode', 'importer' => 'privacy:metadata:grade_import_values:importer', 'importonlyfeedback' => 'privacy:metadata:grade_import_values:importonlyfeedback' ], 'privacy:metadata:grade_import_values'); $collection->link_subsystem('core_files', 'privacy:metadata:filepurpose'); 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): \core_privacy\local\request\contextlist { $contextlist = new \core_privacy\local\request\contextlist(); // Add where we modified outcomes. $sql = " SELECT DISTINCT ctx.id FROM {grade_outcomes} go JOIN {context} ctx ON (go.courseid > 0 AND ctx.instanceid = go.courseid AND ctx.contextlevel = :courselevel) OR ((go.courseid IS NULL OR go.courseid < 1) AND ctx.id = :syscontextid) WHERE go.usermodified = :userid"; $params = ['userid' => $userid, 'courselevel' => CONTEXT_COURSE, 'syscontextid' => SYSCONTEXTID]; $contextlist->add_from_sql($sql, $params); // Add where we modified scales. $sql = " SELECT DISTINCT ctx.id FROM {scale} s JOIN {context} ctx ON (s.courseid > 0 AND ctx.instanceid = s.courseid AND ctx.contextlevel = :courselevel) OR (s.courseid = 0 AND ctx.id = :syscontextid) WHERE s.userid = :userid"; $params = ['userid' => $userid, 'courselevel' => CONTEXT_COURSE, 'syscontextid' => SYSCONTEXTID]; $contextlist->add_from_sql($sql, $params); // Add where appear in the history of outcomes, categories, scales or items. $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {grade_outcomes_history} goh ON goh.loggeduser = :userid1 AND goh.courseid > 0 AND goh.courseid = ctx.instanceid AND ctx.contextlevel = :courselevel1"; $params = [ 'courselevel1' => CONTEXT_COURSE, 'userid1' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {grade_outcomes_history} goh ON goh.loggeduser = :userid1 AND (goh.courseid IS NULL OR goh.courseid < 1) AND ctx.id = :syscontextid1"; $params = [ 'syscontextid1' => SYSCONTEXTID, 'courselevel1' => CONTEXT_COURSE, 'userid1' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {grade_categories_history} gch ON gch.loggeduser = :userid2 AND gch.courseid = ctx.instanceid AND ctx.contextlevel = :courselevel2"; $params = [ 'courselevel2' => CONTEXT_COURSE, 'userid2' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {grade_items_history} gih ON gih.loggeduser = :userid3 AND gih.courseid = ctx.instanceid AND ctx.contextlevel = :courselevel3"; $params = [ 'courselevel3' => CONTEXT_COURSE, 'userid3' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {scale_history} sh ON sh.userid = :userid4 AND sh.courseid > 0 AND sh.courseid = ctx.instanceid AND ctx.contextlevel = :courselevel4"; $params = [ 'courselevel4' => CONTEXT_COURSE, 'userid4' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {scale_history} sh ON sh.loggeduser = :userid5 AND sh.courseid > 0 AND sh.courseid = ctx.instanceid AND ctx.contextlevel = :courselevel4"; $params = [ 'courselevel4' => CONTEXT_COURSE, 'userid5' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {scale_history} sh ON sh.userid = :userid4 AND sh.courseid = 0 AND ctx.id = :syscontextid2"; $params = [ 'syscontextid2' => SYSCONTEXTID, 'userid4' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {scale_history} sh ON sh.loggeduser = :userid5 AND sh.courseid = 0 AND ctx.id = :syscontextid2"; $params = [ 'syscontextid2' => SYSCONTEXTID, 'userid5' => $userid, ]; $contextlist->add_from_sql($sql, $params); // Add where we were graded or modified grades, including in the history table. $sql = " SELECT DISTINCT ctx.id FROM {grade_items} gi JOIN {context} ctx ON ctx.instanceid = gi.courseid AND ctx.contextlevel = :courselevel JOIN {grade_grades} gg ON gg.itemid = gi.id WHERE gg.userid = :userid1 OR gg.usermodified = :userid2"; $params = [ 'courselevel' => CONTEXT_COURSE, 'userid1' => $userid, 'userid2' => $userid ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {grade_items} gi JOIN {context} ctx ON ctx.instanceid = gi.courseid AND ctx.contextlevel = :courselevel JOIN {grade_grades_history} ggh ON ggh.itemid = gi.id WHERE ggh.userid = :userid1 OR ggh.loggeduser = :userid2 OR ggh.usermodified = :userid3"; $params = [ 'courselevel' => CONTEXT_COURSE, 'userid1' => $userid, 'userid2' => $userid, 'userid3' => $userid ]; $contextlist->add_from_sql($sql, $params); // Historical grades can be made orphans when the corresponding itemid is deleted. When that happens // we cannot tie the historical grade to a course context, so we report the user context as a last resort. $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {grade_grades_history} ggh ON ctx.contextlevel = :userlevel AND ggh.userid = ctx.instanceid AND ( ggh.userid = :userid1 OR ggh.usermodified = :userid2 OR ggh.loggeduser = :userid3 ) LEFT JOIN {grade_items} gi ON ggh.itemid = gi.id WHERE gi.id IS NULL"; $params = [ 'userlevel' => CONTEXT_USER, 'userid1' => $userid, 'userid2' => $userid, 'userid3' => $userid ]; $contextlist->add_from_sql($sql, $params); return $contextlist; } /** * Get the list of contexts that contain user information for the specified user. * * @param \core_privacy\local\request\userlist $userlist The userlist containing the list of users who have data * in this context/plugin combination. */ public static function get_users_in_context(\core_privacy\local\request\userlist $userlist) { $context = $userlist->get_context(); if ($context->contextlevel == CONTEXT_COURSE) { $params = ['contextinstanceid' => $context->instanceid]; $sql = "SELECT usermodified FROM {grade_outcomes} WHERE courseid = :contextinstanceid"; $userlist->add_from_sql('usermodified', $sql, $params); $sql = "SELECT loggeduser FROM {grade_outcomes_history} WHERE courseid = :contextinstanceid"; $userlist->add_from_sql('loggeduser', $sql, $params); $sql = "SELECT userid FROM {scale} WHERE courseid = :contextinstanceid"; $userlist->add_from_sql('userid', $sql, $params); $sql = "SELECT loggeduser, userid FROM {scale_history} WHERE courseid = :contextinstanceid"; $userlist->add_from_sql('loggeduser', $sql, $params); $userlist->add_from_sql('userid', $sql, $params); $sql = "SELECT loggeduser FROM {grade_items_history} WHERE courseid = :contextinstanceid"; $userlist->add_from_sql('loggeduser', $sql, $params); $sql = "SELECT ggh.userid FROM {grade_grades_history} ggh JOIN {grade_items} gi ON ggh.itemid = gi.id WHERE gi.courseid = :contextinstanceid"; $userlist->add_from_sql('userid', $sql, $params); $sql = "SELECT gg.userid, gg.usermodified FROM {grade_grades} gg JOIN {grade_items} gi ON gg.itemid = gi.id WHERE gi.courseid = :contextinstanceid"; $userlist->add_from_sql('userid', $sql, $params); $userlist->add_from_sql('usermodified', $sql, $params); $sql = "SELECT loggeduser FROM {grade_categories_history} WHERE courseid = :contextinstanceid"; $userlist->add_from_sql('loggeduser', $sql, $params); } // None of these are currently used (user deletion). if ($context->contextlevel == CONTEXT_SYSTEM) { $params = ['contextinstanceid' => 0]; $sql = "SELECT usermodified FROM {grade_outcomes} WHERE (courseid IS NULL OR courseid < 1)"; $userlist->add_from_sql('usermodified', $sql, []); $sql = "SELECT loggeduser FROM {grade_outcomes_history} WHERE (courseid IS NULL OR courseid < 1)"; $userlist->add_from_sql('loggeduser', $sql, []); $sql = "SELECT userid FROM {scale} WHERE courseid = :contextinstanceid"; $userlist->add_from_sql('userid', $sql, $params); $sql = "SELECT loggeduser, userid FROM {scale_history} WHERE courseid = :contextinstanceid"; $userlist->add_from_sql('loggeduser', $sql, $params); $userlist->add_from_sql('userid', $sql, $params); } if ($context->contextlevel == CONTEXT_USER) { // If the grade item has been removed and we have an orphan entry then we link to the // user context. $sql = "SELECT ggh.userid FROM {grade_grades_history} ggh LEFT JOIN {grade_items} gi ON ggh.itemid = gi.id WHERE gi.id IS NULL AND ggh.userid = :contextinstanceid"; $userlist->add_from_sql('userid', $sql, ['contextinstanceid' => $context->instanceid]); } } /** * 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) { global $DB; $user = $contextlist->get_user(); $userid = $user->id; $contexts = array_reduce($contextlist->get_contexts(), function($carry, $context) use ($userid) { if ($context->contextlevel == CONTEXT_COURSE) { $carry[$context->contextlevel][] = $context; } else if ($context->contextlevel == CONTEXT_USER) { $carry[$context->contextlevel][] = $context; } return $carry; }, [ CONTEXT_USER => [], CONTEXT_COURSE => [] ]); $rootpath = [get_string('grades', 'core_grades')]; $relatedtomepath = array_merge($rootpath, [get_string('privacy:path:relatedtome', 'core_grades')]); // Export the outcomes. static::export_user_data_outcomes_in_contexts($contextlist); // Export the scales. static::export_user_data_scales_in_contexts($contextlist); // Export the historical grades which have become orphans (their grade items were deleted). // We place those in ther user context of the graded user. $userids = array_values(array_map(function($context) { return $context->instanceid; }, $contexts[CONTEXT_USER])); if (!empty($userids)) { // Export own historical grades and related ones. list($inuseridsql, $inuseridparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); list($inusermodifiedsql, $inusermodifiedparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); list($inloggedusersql, $inloggeduserparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); $usercontext = $contexts[CONTEXT_USER]; $gghfields = static::get_fields_sql('grade_grades_history', 'ggh', 'ggh_'); $sql = " SELECT $gghfields, ctx.id as ctxid FROM {grade_grades_history} ggh JOIN {context} ctx ON ctx.instanceid = ggh.userid AND ctx.contextlevel = :userlevel LEFT JOIN {grade_items} gi ON gi.id = ggh.itemid WHERE gi.id IS NULL AND (ggh.userid $inuseridsql OR ggh.usermodified $inusermodifiedsql OR ggh.loggeduser $inloggedusersql) AND (ggh.userid = :userid1 OR ggh.usermodified = :userid2 OR ggh.loggeduser = :userid3) ORDER BY ggh.userid, ggh.timemodified, ggh.id"; $params = array_merge($inuseridparams, $inusermodifiedparams, $inloggeduserparams, ['userid1' => $userid, 'userid2' => $userid, 'userid3' => $userid, 'userlevel' => CONTEXT_USER]); $deletedstr = get_string('privacy:request:unknowndeletedgradeitem', 'core_grades'); $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'ctxid', [], function($carry, $record) use ($deletedstr, $userid) { $context = context::instance_by_id($record->ctxid); $gghrecord = static::extract_record($record, 'ggh_'); // Orphan grades do not have items, so we do not recreate a grade_grade item, and we do not format grades. $carry[] = [ 'name' => $deletedstr, 'graded_user_was_you' => transform::yesno($userid == $gghrecord->userid), 'grade' => $gghrecord->finalgrade, 'feedback' => format_text($gghrecord->feedback, $gghrecord->feedbackformat, ['context' => $context]), 'information' => format_text($gghrecord->information, $gghrecord->informationformat, ['context' => $context]), 'timemodified' => transform::datetime($gghrecord->timemodified), 'logged_in_user_was_you' => transform::yesno($userid == $gghrecord->loggeduser), 'author_of_change_was_you' => transform::yesno($userid == $gghrecord->usermodified), 'action' => static::transform_history_action($gghrecord->action) ]; return $carry; }, function($ctxid, $data) use ($rootpath) { $context = context::instance_by_id($ctxid); writer::with_context($context)->export_related_data($rootpath, 'history', (object) ['grades' => $data]); }); } // Find out the course IDs. $courseids = array_values(array_map(function($context) { return $context->instanceid; }, $contexts[CONTEXT_COURSE])); if (empty($courseids)) { return; } list($incoursesql, $incourseparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); // Ensure that the grades are final and do not need regrading. array_walk($courseids, function($courseid) { grade_regrade_final_grades($courseid); }); // Export own grades. $ggfields = static::get_fields_sql('grade_grade', 'gg', 'gg_'); $gifields = static::get_fields_sql('grade_item', 'gi', 'gi_'); $scalefields = static::get_fields_sql('grade_scale', 'sc', 'sc_'); $sql = " SELECT $ggfields, $gifields, $scalefields FROM {grade_grades} gg JOIN {grade_items} gi ON gi.id = gg.itemid LEFT JOIN {scale} sc ON sc.id = gi.scaleid WHERE gi.courseid $incoursesql AND gg.userid = :userid ORDER BY gi.courseid, gi.id, gg.id"; $params = array_merge($incourseparams, ['userid' => $userid]); $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'gi_courseid', [], function($carry, $record) { $context = context_course::instance($record->gi_courseid); $gg = static::extract_grade_grade_from_record($record); $carry[] = static::transform_grade($gg, $context, false); return $carry; }, function($courseid, $data) use ($rootpath) { $context = context_course::instance($courseid); $pathtofiles = [ get_string('grades', 'core_grades'), get_string('feedbackfiles', 'core_grades') ]; foreach ($data as $key => $grades) { $gg = $grades['gradeobject']; writer::with_context($gg->get_context())->export_area_files($pathtofiles, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA, $gg->id); unset($data[$key]['gradeobject']); // Do not want to export this later. } writer::with_context($context)->export_data($rootpath, (object) ['grades' => $data]); }); // Export own historical grades in courses. $gghfields = static::get_fields_sql('grade_grades_history', 'ggh', 'ggh_'); $sql = " SELECT $gghfields, $gifields, $scalefields FROM {grade_grades_history} ggh JOIN {grade_items} gi ON gi.id = ggh.itemid LEFT JOIN {scale} sc ON sc.id = gi.scaleid WHERE gi.courseid $incoursesql AND ggh.userid = :userid ORDER BY gi.courseid, ggh.timemodified, ggh.id"; $params = array_merge($incourseparams, ['userid' => $userid]); $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'gi_courseid', [], function($carry, $record) { $context = context_course::instance($record->gi_courseid); $gg = static::extract_grade_grade_from_record($record, true); $carry[] = array_merge(static::transform_grade($gg, $context, true), [ 'action' => static::transform_history_action($record->ggh_action) ]); return $carry; }, function($courseid, $data) use ($rootpath) { $context = context_course::instance($courseid); $pathtofiles = [ get_string('grades', 'core_grades'), get_string('feedbackhistoryfiles', 'core_grades') ]; foreach ($data as $key => $grades) { /** @var grade_grade_with_history */ $gg = $grades['gradeobject']; writer::with_context($gg->get_context())->export_area_files($pathtofiles, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA, $gg->historyid); unset($data[$key]['gradeobject']); // Do not want to export this later. } writer::with_context($context)->export_related_data($rootpath, 'history', (object) ['grades' => $data]); }); // Export edits of categories history. $sql = " SELECT gch.id, gch.courseid, gch.fullname, gch.timemodified, gch.action FROM {grade_categories_history} gch WHERE gch.courseid $incoursesql AND gch.loggeduser = :userid ORDER BY gch.courseid, gch.timemodified, gch.id"; $params = array_merge($incourseparams, ['userid' => $userid]); $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'courseid', [], function($carry, $record) { $carry[] = [ 'name' => $record->fullname, 'timemodified' => transform::datetime($record->timemodified), 'logged_in_user_was_you' => transform::yesno(true), 'action' => static::transform_history_action($record->action), ]; return $carry; }, function($courseid, $data) use ($relatedtomepath) { $context = context_course::instance($courseid); writer::with_context($context)->export_related_data($relatedtomepath, 'categories_history', (object) ['modified_records' => $data]); }); // Export edits of items history. $sql = " SELECT gih.id, gih.courseid, gih.itemname, gih.itemmodule, gih.iteminfo, gih.timemodified, gih.action FROM {grade_items_history} gih WHERE gih.courseid $incoursesql AND gih.loggeduser = :userid ORDER BY gih.courseid, gih.timemodified, gih.id"; $params = array_merge($incourseparams, ['userid' => $userid]); $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'courseid', [], function($carry, $record) { $carry[] = [ 'name' => $record->itemname, 'module' => $record->itemmodule, 'info' => $record->iteminfo, 'timemodified' => transform::datetime($record->timemodified), 'logged_in_user_was_you' => transform::yesno(true), 'action' => static::transform_history_action($record->action), ]; return $carry; }, function($courseid, $data) use ($relatedtomepath) { $context = context_course::instance($courseid); writer::with_context($context)->export_related_data($relatedtomepath, 'items_history', (object) ['modified_records' => $data]); }); // Export edits of grades in course. $sql = " SELECT $ggfields, $gifields, $scalefields FROM {grade_grades} gg JOIN {grade_items} gi ON gg.itemid = gi.id LEFT JOIN {scale} sc ON sc.id = gi.scaleid WHERE gi.courseid $incoursesql AND gg.userid <> :userid1 -- Our grades have already been exported. AND gg.usermodified = :userid2 ORDER BY gi.courseid, gg.timemodified, gg.id"; $params = array_merge($incourseparams, ['userid1' => $userid, 'userid2' => $userid]); $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'gi_courseid', [], function($carry, $record) { $context = context_course::instance($record->gi_courseid); $gg = static::extract_grade_grade_from_record($record); $carry[] = array_merge(static::transform_grade($gg, $context, false), [ 'userid' => transform::user($gg->userid), 'created_or_modified_by_you' => transform::yesno(true), ]); return $carry; }, function($courseid, $data) use ($relatedtomepath) { $context = context_course::instance($courseid); $pathtofiles = [ get_string('grades', 'core_grades'), get_string('feedbackfiles', 'core_grades') ]; foreach ($data as $key => $grades) { $gg = $grades['gradeobject']; writer::with_context($gg->get_context())->export_area_files($pathtofiles, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA, $gg->id); unset($data[$key]['gradeobject']); // Do not want to export this later. } writer::with_context($context)->export_related_data($relatedtomepath, 'grades', (object) ['grades' => $data]); }); // Export edits of grades history in course. $sql = " SELECT $gghfields, $gifields, $scalefields, ggh.loggeduser AS loggeduser FROM {grade_grades_history} ggh JOIN {grade_items} gi ON ggh.itemid = gi.id LEFT JOIN {scale} sc ON sc.id = gi.scaleid WHERE gi.courseid $incoursesql AND ggh.userid <> :userid1 -- We've already exported our history. AND (ggh.loggeduser = :userid2 OR ggh.usermodified = :userid3) ORDER BY gi.courseid, ggh.timemodified, ggh.id"; $params = array_merge($incourseparams, ['userid1' => $userid, 'userid2' => $userid, 'userid3' => $userid]); $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'gi_courseid', [], function($carry, $record) use ($userid) { $context = context_course::instance($record->gi_courseid); $gg = static::extract_grade_grade_from_record($record, true); $carry[] = array_merge(static::transform_grade($gg, $context, true), [ 'userid' => transform::user($gg->userid), 'logged_in_user_was_you' => transform::yesno($userid == $record->loggeduser), 'author_of_change_was_you' => transform::yesno($userid == $gg->usermodified), 'action' => static::transform_history_action($record->ggh_action), ]); return $carry; }, function($courseid, $data) use ($relatedtomepath) { $context = context_course::instance($courseid); $pathtofiles = [ get_string('grades', 'core_grades'), get_string('feedbackhistoryfiles', 'core_grades') ]; foreach ($data as $key => $grades) { /** @var grade_grade_with_history */ $gg = $grades['gradeobject']; writer::with_context($gg->get_context())->export_area_files($pathtofiles, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA, $gg->historyid); unset($data[$key]['gradeobject']); // Do not want to export this later. } writer::with_context($context)->export_related_data($relatedtomepath, 'grades_history', (object) ['modified_records' => $data]); }); } /** * Delete all data for all users in the specified context. * * @param context $context The specific context to delete data for. */ public static function delete_data_for_all_users_in_context(context $context) { global $DB; switch ($context->contextlevel) { case CONTEXT_USER: // The user context is only reported when there are orphan historical grades, so we only delete those. static::delete_orphan_historical_grades($context->instanceid); break; case CONTEXT_COURSE: // We must not change the structure of the course, so we only delete user content. $itemids = static::get_item_ids_from_course_ids([$context->instanceid]); if (empty($itemids)) { return; } self::delete_files($itemids, true); self::delete_files($itemids, false); list($insql, $inparams) = $DB->get_in_or_equal($itemids, SQL_PARAMS_NAMED); $DB->delete_records_select('grade_grades', "itemid $insql", $inparams); $DB->delete_records_select('grade_grades_history', "itemid $insql", $inparams); break; } } /** * 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) { global $DB; $userid = $contextlist->get_user()->id; $courseids = []; foreach ($contextlist->get_contexts() as $context) { if ($context->contextlevel == CONTEXT_USER && $userid == $context->instanceid) { // User attempts to delete data in their own context. static::delete_orphan_historical_grades($userid); } else if ($context->contextlevel == CONTEXT_COURSE) { // Log the list of course IDs. $courseids[] = $context->instanceid; } } $itemids = static::get_item_ids_from_course_ids($courseids); if (empty($itemids)) { // Our job here is done! return; } // Delete all the files. self::delete_files($itemids, true, [$userid]); self::delete_files($itemids, false, [$userid]); // Delete all the grades. list($insql, $inparams) = $DB->get_in_or_equal($itemids, SQL_PARAMS_NAMED); $params = array_merge($inparams, ['userid' => $userid]); $DB->delete_records_select('grade_grades', "itemid $insql AND userid = :userid", $params); $DB->delete_records_select('grade_grades_history', "itemid $insql AND userid = :userid", $params); } /** * Delete multiple users within a single context. * * @param \core_privacy\local\request\approved_userlist $userlist The approved context and user information to * delete information for. */ public static function delete_data_for_users(\core_privacy\local\request\approved_userlist $userlist) { global $DB; $context = $userlist->get_context(); $userids = $userlist->get_userids(); if ($context->contextlevel == CONTEXT_USER) { if (array_search($context->instanceid, $userids) !== false) { static::delete_orphan_historical_grades($context->instanceid); } return; } if ($context->contextlevel != CONTEXT_COURSE) { return; } $itemids = static::get_item_ids_from_course_ids([$context->instanceid]); if (empty($itemids)) { // Our job here is done! return; } // Delete all the files. self::delete_files($itemids, true, $userids); self::delete_files($itemids, false, $userids); // Delete all the grades. list($itemsql, $itemparams) = $DB->get_in_or_equal($itemids, SQL_PARAMS_NAMED); list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); $params = array_merge($itemparams, $userparams); $DB->delete_records_select('grade_grades', "itemid $itemsql AND userid $usersql", $params); $DB->delete_records_select('grade_grades_history', "itemid $itemsql AND userid $usersql", $params); } /** * Delete orphan historical grades. * * @param int $userid The user ID. * @return void */ protected static function delete_orphan_historical_grades($userid) { global $DB; $sql = " SELECT ggh.id FROM {grade_grades_history} ggh LEFT JOIN {grade_items} gi ON ggh.itemid = gi.id WHERE gi.id IS NULL AND ggh.userid = :userid"; $ids = $DB->get_fieldset_sql($sql, ['userid' => $userid]); if (empty($ids)) { return; } list($insql, $inparams) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED); // First, let's delete their files. $sql = " SELECT gi.id FROM {grade_grades_history} ggh JOIN {grade_items} gi ON gi.id = ggh.itemid WHERE ggh.userid = :userid"; $params = ['userid' => $userid]; $gradeitems = $DB->get_records_sql($sql, $params); if ($gradeitems) { $itemids = array_keys($gradeitems); self::delete_files($itemids, true, [$userid]); } $DB->delete_records_select('grade_grades_history', "id $insql", $inparams); } /** * Export the user data related to outcomes. * * @param approved_contextlist $contextlist The approved contexts to export information for. * @return void */ protected static function export_user_data_outcomes_in_contexts(approved_contextlist $contextlist) { global $DB; $rootpath = [get_string('grades', 'core_grades')]; $relatedtomepath = array_merge($rootpath, [get_string('privacy:path:relatedtome', 'core_grades')]); $userid = $contextlist->get_user()->id; // Reorganise the contexts. $reduced = array_reduce($contextlist->get_contexts(), function($carry, $context) { if ($context->contextlevel == CONTEXT_SYSTEM) { $carry['in_system'] = true; } else if ($context->contextlevel == CONTEXT_COURSE) { $carry['courseids'][] = $context->instanceid; } return $carry; }, [ 'in_system' => false, 'courseids' => [] ]); // Construct SQL. $sqltemplateparts = []; $templateparams = []; if ($reduced['in_system']) { $sqltemplateparts[] = '{prefix}.courseid IS NULL'; } if (!empty($reduced['courseids'])) { list($insql, $inparams) = $DB->get_in_or_equal($reduced['courseids'], SQL_PARAMS_NAMED); $sqltemplateparts[] = "{prefix}.courseid $insql"; $templateparams = array_merge($templateparams, $inparams); } if (empty($sqltemplateparts)) { return; } $sqltemplate = '(' . implode(' OR ', $sqltemplateparts) . ')'; // Export edited outcomes. $sqlwhere = str_replace('{prefix}', 'go', $sqltemplate); $sql = " SELECT go.id, COALESCE(go.courseid, 0) AS courseid, go.shortname, go.fullname, go.timemodified FROM {grade_outcomes} go WHERE $sqlwhere AND go.usermodified = :userid ORDER BY go.courseid, go.timemodified, go.id"; $params = array_merge($templateparams, ['userid' => $userid]); $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'courseid', [], function($carry, $record) { $carry[] = [ 'shortname' => $record->shortname, 'fullname' => $record->fullname, 'timemodified' => transform::datetime($record->timemodified), 'created_or_modified_by_you' => transform::yesno(true) ]; return $carry; }, function($courseid, $data) use ($relatedtomepath) { $context = $courseid ? context_course::instance($courseid) : context_system::instance(); writer::with_context($context)->export_related_data($relatedtomepath, 'outcomes', (object) ['outcomes' => $data]); }); // Export edits of outcomes history. $sqlwhere = str_replace('{prefix}', 'goh', $sqltemplate); $sql = " SELECT goh.id, COALESCE(goh.courseid, 0) AS courseid, goh.shortname, goh.fullname, goh.timemodified, goh.action FROM {grade_outcomes_history} goh WHERE $sqlwhere AND goh.loggeduser = :userid ORDER BY goh.courseid, goh.timemodified, goh.id"; $params = array_merge($templateparams, ['userid' => $userid]); $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'courseid', [], function($carry, $record) { $carry[] = [ 'shortname' => $record->shortname, 'fullname' => $record->fullname, 'timemodified' => transform::datetime($record->timemodified), 'logged_in_user_was_you' => transform::yesno(true), 'action' => static::transform_history_action($record->action) ]; return $carry; }, function($courseid, $data) use ($relatedtomepath) { $context = $courseid ? context_course::instance($courseid) : context_system::instance(); writer::with_context($context)->export_related_data($relatedtomepath, 'outcomes_history', (object) ['modified_records' => $data]); }); } /** * Export the user data related to scales. * * @param approved_contextlist $contextlist The approved contexts to export information for. * @return void */ protected static function export_user_data_scales_in_contexts(approved_contextlist $contextlist) { global $DB; $rootpath = [get_string('grades', 'core_grades')]; $relatedtomepath = array_merge($rootpath, [get_string('privacy:path:relatedtome', 'core_grades')]); $userid = $contextlist->get_user()->id; // Reorganise the contexts. $reduced = array_reduce($contextlist->get_contexts(), function($carry, $context) { if ($context->contextlevel == CONTEXT_SYSTEM) { $carry['in_system'] = true; } else if ($context->contextlevel == CONTEXT_COURSE) { $carry['courseids'][] = $context->instanceid; } return $carry; }, [ 'in_system' => false, 'courseids' => [] ]); // Construct SQL. $sqltemplateparts = []; $templateparams = []; if ($reduced['in_system']) { $sqltemplateparts[] = '{prefix}.courseid = 0'; } if (!empty($reduced['courseids'])) { list($insql, $inparams) = $DB->get_in_or_equal($reduced['courseids'], SQL_PARAMS_NAMED); $sqltemplateparts[] = "{prefix}.courseid $insql"; $templateparams = array_merge($templateparams, $inparams); } if (empty($sqltemplateparts)) { return; } $sqltemplate = '(' . implode(' OR ', $sqltemplateparts) . ')'; // Export edited scales. $sqlwhere = str_replace('{prefix}', 's', $sqltemplate); $sql = " SELECT s.id, s.courseid, s.name, s.timemodified FROM {scale} s WHERE $sqlwhere AND s.userid = :userid ORDER BY s.courseid, s.timemodified, s.id"; $params = array_merge($templateparams, ['userid' => $userid]); $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'courseid', [], function($carry, $record) { $carry[] = [ 'name' => $record->name, 'timemodified' => transform::datetime($record->timemodified), 'created_or_modified_by_you' => transform::yesno(true) ]; return $carry; }, function($courseid, $data) use ($relatedtomepath) { $context = $courseid ? context_course::instance($courseid) : context_system::instance(); writer::with_context($context)->export_related_data($relatedtomepath, 'scales', (object) ['scales' => $data]); }); // Export edits of scales history. $sqlwhere = str_replace('{prefix}', 'sh', $sqltemplate); $sql = " SELECT sh.id, sh.courseid, sh.name, sh.userid, sh.timemodified, sh.action, sh.loggeduser FROM {scale_history} sh WHERE $sqlwhere AND sh.loggeduser = :userid1 OR sh.userid = :userid2 ORDER BY sh.courseid, sh.timemodified, sh.id"; $params = array_merge($templateparams, ['userid1' => $userid, 'userid2' => $userid]); $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'courseid', [], function($carry, $record) use ($userid) { $carry[] = [ 'name' => $record->name, 'timemodified' => transform::datetime($record->timemodified), 'author_of_change_was_you' => transform::yesno($record->userid == $userid), 'author_of_action_was_you' => transform::yesno($record->loggeduser == $userid), 'action' => static::transform_history_action($record->action) ]; return $carry; }, function($courseid, $data) use ($relatedtomepath) { $context = $courseid ? context_course::instance($courseid) : context_system::instance(); writer::with_context($context)->export_related_data($relatedtomepath, 'scales_history', (object) ['modified_records' => $data]); }); } /** * Extract grade_grade from a record. * * @param stdClass $record The record. * @param bool $ishistory Whether we're extracting a historical grade. * @return grade_grade */ protected static function extract_grade_grade_from_record(stdClass $record, $ishistory = false) { $prefix = $ishistory ? 'ggh_' : 'gg_'; $ggrecord = static::extract_record($record, $prefix); if ($ishistory) { $gg = new grade_grade_with_history($ggrecord, false); } else { $gg = new grade_grade($ggrecord, false); } // There is a grade item in the record. if (!empty($record->gi_id)) { $gi = new grade_item(static::extract_record($record, 'gi_'), false); $gg->grade_item = $gi; // This is a common hack throughout the grades API. } // Load the scale, when it still exists. if (!empty($gi->scaleid) && !empty($record->sc_id)) { $scalerec = static::extract_record($record, 'sc_'); $gi->scale = new grade_scale($scalerec, false); $gi->scale->load_items(); } return $gg; } /** * Extract a record from another one. * * @param object $record The record to extract from. * @param string $prefix The prefix used. * @return object */ protected static function extract_record($record, $prefix) { $result = []; $prefixlength = strlen($prefix); foreach ($record as $key => $value) { if (strpos($key, $prefix) === 0) { $result[substr($key, $prefixlength)] = $value; } } return (object) $result; } /** * Get fields SQL for a grade related object. * * @param string $target The related object. * @param string $alias The table alias. * @param string $prefix A prefix. * @return string */ protected static function get_fields_sql($target, $alias, $prefix) { switch ($target) { case 'grade_category': case 'grade_grade': case 'grade_item': case 'grade_outcome': case 'grade_scale': $obj = new $target([], false); $fields = array_merge(array_keys($obj->optional_fields), $obj->required_fields); break; case 'grade_grades_history': $fields = ['id', 'action', 'oldid', 'source', 'timemodified', 'loggeduser', 'itemid', 'userid', 'rawgrade', 'rawgrademax', 'rawgrademin', 'rawscaleid', 'usermodified', 'finalgrade', 'hidden', 'locked', 'locktime', 'exported', 'overridden', 'excluded', 'feedback', 'feedbackformat', 'information', 'informationformat']; break; default: throw new \coding_exception('Unrecognised target: ' . $target); break; } return implode(', ', array_map(function($field) use ($alias, $prefix) { return "{$alias}.{$field} AS {$prefix}{$field}"; }, $fields)); } /** * Get all the items IDs from course IDs. * * @param array $courseids The course IDs. * @return array */ protected static function get_item_ids_from_course_ids($courseids) { global $DB; if (empty($courseids)) { return []; } list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); return $DB->get_fieldset_select('grade_items', 'id', "courseid $insql", $inparams); } /** * Loop and export from a recordset. * * @param moodle_recordset $recordset The recordset. * @param string $splitkey The record key to determine when to export. * @param mixed $initial The initial data to reduce from. * @param callable $reducer The function to return the dataset, receives current dataset, and the current record. * @param callable $export The function to export the dataset, receives the last value from $splitkey and the dataset. * @return void */ protected static function recordset_loop_and_export(\moodle_recordset $recordset, $splitkey, $initial, callable $reducer, callable $export) { $data = $initial; $lastid = null; foreach ($recordset as $record) { if ($lastid !== null && $record->{$splitkey} != $lastid) { $export($lastid, $data); $data = $initial; } $data = $reducer($data, $record); $lastid = $record->{$splitkey}; } $recordset->close(); if ($lastid !== null) { $export($lastid, $data); } } /** * Transform an history action. * * @param int $action The action. * @return string */ protected static function transform_history_action($action) { switch ($action) { case GRADE_HISTORY_INSERT: return get_string('privacy:request:historyactioninsert', 'core_grades'); break; case GRADE_HISTORY_UPDATE: return get_string('privacy:request:historyactionupdate', 'core_grades'); break; case GRADE_HISTORY_DELETE: return get_string('privacy:request:historyactiondelete', 'core_grades'); break; } return '?'; } /** * Transform a grade. * * @param grade_grade $gg The grade object. * @param context $context The context. * @param bool $ishistory Whether we're extracting a historical grade. * @return array */ protected static function transform_grade(grade_grade $gg, context $context, bool $ishistory) { $gi = $gg->load_grade_item(); $timemodified = $gg->timemodified ? transform::datetime($gg->timemodified) : null; $timecreated = $gg->timecreated ? transform::datetime($gg->timecreated) : $timemodified; // When null we use timemodified. if ($gg instanceof grade_grade_with_history) { $filearea = GRADE_HISTORY_FEEDBACK_FILEAREA; $itemid = $gg->historyid; $subpath = get_string('feedbackhistoryfiles', 'core_grades'); } else { $filearea = GRADE_FEEDBACK_FILEAREA; $itemid = $gg->id; $subpath = get_string('feedbackfiles', 'core_grades'); } $pathtofiles = [ get_string('grades', 'core_grades'), $subpath ]; $gg->feedback = writer::with_context($gg->get_context())->rewrite_pluginfile_urls( $pathtofiles, GRADE_FILE_COMPONENT, $filearea, $itemid, $gg->feedback ); return [ 'gradeobject' => $gg, 'item' => $gi->get_name(), 'grade' => $gg->finalgrade, 'grade_formatted' => grade_format_gradevalue($gg->finalgrade, $gi), 'feedback' => format_text($gg->feedback, $gg->feedbackformat, ['context' => $context]), 'information' => format_text($gg->information, $gg->informationformat, ['context' => $context]), 'timecreated' => $timecreated, 'timemodified' => $timemodified, ]; } /** * Handles deleting files for a given list of grade items. * * If an array of userids if given then it handles deleting files for those users. * * @param array $itemids * @param bool $ishistory * @param array|null $userids * @throws \coding_exception * @throws \dml_exception */ protected static function delete_files(array $itemids, bool $ishistory, ?array $userids = null) { global $DB; list($iteminnsql, $params) = $DB->get_in_or_equal($itemids, SQL_PARAMS_NAMED); if (!is_null($userids)) { list($userinnsql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); $params = array_merge($params, $userparams); } if ($ishistory) { $gradefields = static::get_fields_sql('grade_grades_history', 'ggh', 'ggh_'); $gradetable = 'grade_grades_history'; $tableprefix = 'ggh'; $filearea = GRADE_HISTORY_FEEDBACK_FILEAREA; } else { $gradefields = static::get_fields_sql('grade_grade', 'gg', 'gg_'); $gradetable = 'grade_grades'; $tableprefix = 'gg'; $filearea = GRADE_FEEDBACK_FILEAREA; } $gifields = static::get_fields_sql('grade_item', 'gi', 'gi_'); $fs = new \file_storage(); $sql = "SELECT $gradefields, $gifields FROM {{$gradetable}} $tableprefix JOIN {grade_items} gi ON gi.id = {$tableprefix}.itemid WHERE gi.id $iteminnsql "; if (!is_null($userids)) { $sql .= "AND {$tableprefix}.userid $userinnsql"; } $grades = $DB->get_recordset_sql($sql, $params); foreach ($grades as $grade) { $gg = static::extract_grade_grade_from_record($grade, $ishistory); if ($gg instanceof grade_grade_with_history) { $fileitemid = $gg->historyid; } else { $fileitemid = $gg->id; } $fs->delete_area_files($gg->get_context()->id, GRADE_FILE_COMPONENT, $filearea, $fileitemid); } $grades->close(); } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/media/classes/privacy/provider.php000064400000002740152266542250025431 0ustar00. /** * Privacy provider implementation for core_media. * * @package core_media * @copyright 2018 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core_media\privacy; defined('MOODLE_INTERNAL') || die(); /** * Privacy provider implementation for core_media. * * @copyright 2018 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\null_provider { /** * Get the language string identifier with the component's language * file to explain why this plugin stores no data. * * @return string */ public static function get_reason(): string { return 'privacy:metadata'; } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/files/converter/classes/privacy/provider.php000064400000010717152266546540027474 0ustar00. /** * Data provider. * * @package core_fileconverter * @copyright 2018 Frédéric Massart * @author Frédéric Massart * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core_fileconverter\privacy; defined('MOODLE_INTERNAL') || die(); use context; use core_privacy\local\metadata\collection; use core_privacy\local\request\approved_contextlist; use core_privacy\local\request\userlist; use core_privacy\local\request\approved_userlist; /** * Data provider class. * * @package core_fileconverter * @copyright 2018 Frédéric Massart * @author Frédéric Massart * @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 { /** * Returns metadata. * * @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_plugintype_link('fileconverter', [], 'privacy:metadata:plugintypefileconverter'); 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): \core_privacy\local\request\contextlist { // We cannot associate files with a particular user as it would require to know all the details about // the source file, which only the component owning it knows about. And, the 'usermodified' attribute // of the conversion class is not an identifier of data ownership, merely that this user was logged in // when the conversion was requested. As such, as the owning component should declare the source file, // and we can't make sense of 'usermodified', we will not be reporting anything here. Also note that // the clean up task will ensure that whenever a source file is delete, its conversions also are. return new \core_privacy\local\request\contextlist(); } /** * Get the list of users who have data within a 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) { } /** * 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) { } /** * Delete all data for all users in the specified context. * * @param context $context The specific context to delete data for. */ public static function delete_data_for_all_users_in_context(context $context) { } /** * 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) { } /** * 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) { } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/auth/webservice/classes/privacy/provider.php000064400000002774152266546550027467 0ustar00. /** * Privacy Subsystem implementation for auth_webservice. * * @package auth_webservice * @copyright 2018 Carlos Escobedo * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace auth_webservice\privacy; defined('MOODLE_INTERNAL') || die(); /** * Privacy Subsystem for auth_webservice implementing null_provider. * * @copyright 2018 Carlos Escobedo * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\null_provider { /** * Get the language string identifier with the component's language * file to explain why this plugin stores no data. * * @return string */ public static function get_reason(): string { return 'privacy:metadata'; } }home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/dataformat/ods/classes/privacy/provider.php000064400000002760152266607740027271 0ustar00. /** * Privacy provider implementation for dataformat_ods. * * @package dataformat_ods * @copyright 2018 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace dataformat_ods\privacy; defined('MOODLE_INTERNAL') || die(); /** * Privacy provider implementation for dataformat_ods. * * @copyright 2018 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\null_provider { /** * Get the language string identifier with the component's language * file to explain why this plugin stores no data. * * @return string */ public static function get_reason(): string { return 'privacy:metadata'; } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/dataformat/csv/classes/privacy/provider.php000064400000002760152266607750027300 0ustar00. /** * Privacy provider implementation for dataformat_csv. * * @package dataformat_csv * @copyright 2018 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace dataformat_csv\privacy; defined('MOODLE_INTERNAL') || die(); /** * Privacy provider implementation for dataformat_csv. * * @copyright 2018 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\null_provider { /** * Get the language string identifier with the component's language * file to explain why this plugin stores no data. * * @return string */ public static function get_reason(): string { return 'privacy:metadata'; } } smkb8269/public_html/lms.smk-almanshuriyah.sch.id/blocks/private_files/classes/privacy/provider.php000064400000002774152266610030030403 0ustar00home. /** * Privacy Subsystem implementation for block_private_files. * * @package block_private_files * @copyright 2018 Zig Tan * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace block_private_files\privacy; defined('MOODLE_INTERNAL') || die(); /** * Privacy Subsystem for block_private_files implementing null_provider. * * @copyright 2018 Zig Tan * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\null_provider { /** * Get the language string identifier with the component's language * file to explain why this plugin stores no data. * * @return string */ public static function get_reason(): string { return 'privacy:metadata'; } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/favourites/classes/privacy/provider.php000064400000025360152266610640026543 0ustar00. /** * Privacy class for requesting user data for the favourites subsystem. * * @package core_favourites * @copyright 2018 Jake Dallimore * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core_favourites\privacy; defined('MOODLE_INTERNAL') || die(); use \core_privacy\local\metadata\collection; use \core_privacy\local\request\context; use \core_privacy\local\request\approved_contextlist; use \core_privacy\local\request\transform; /** * Privacy class for requesting user data. * * @copyright 2018 Jake Dallimore * @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\subsystem\plugin_provider, \core_privacy\local\request\shared_userlist_provider { /** * Returns metadata about this system. * * @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 { return $collection->add_database_table('favourite', [ 'userid' => 'privacy:metadata:favourite:userid', 'component' => 'privacy:metadata:favourite:component', 'itemtype' => 'privacy:metadata:favourite:itemtype', 'itemid' => 'privacy:metadata:favourite:itemid', 'ordering' => 'privacy:metadata:favourite:ordering', 'timecreated' => 'privacy:metadata:favourite:timecreated', 'timemodified' => 'privacy:metadata:favourite:timemodified', ], 'privacy:metadata:favourite'); } /** * Provide a list of contexts which have favourites for the user, in the respective area (component/itemtype combination). * * This method is to be called by consumers of the favourites subsystem (plugins), in their get_contexts_for_userid() method, * to add the contexts for items which may have been favourited, but would normally not be reported as having user data by the * plugin responsible for them. * * Consider an example: Favourite courses. * Favourite courses will be handled by the core_course subsystem and courses can be favourited at site context. * * Now normally, the course provider method get_contexts_for_userid() would report the context of any courses the user is in. * Then, we'd export data for those contexts. This won't include courses the user has favourited, but is not a member of. * * To report the full list, the course provider needs to be made aware of the contexts of any courses the user may have marked * as favourites. Course will need to ask th favourites subsystem for this - a call to add_contexts_for_userid($userid). * * Once called, if a course has been marked as a favourite, at site context, then we'd return the site context. During export, * the consumer (course), just looks at all contexts and decides whether to export favourite courses for each one. * * @param \core_privacy\local\request\contextlist $contextlist * @param int $userid The id of the user in scope. * @param string $component the frankenstyle component name. * @param string $itemtype the type of the favourited items. */ public static function add_contexts_for_userid(\core_privacy\local\request\contextlist $contextlist, int $userid, string $component, ?string $itemtype = null) { $sql = "SELECT contextid FROM {favourite} f WHERE userid = :userid AND component = :component"; $params = ['userid' => $userid, 'component' => $component]; if (!is_null($itemtype)) { $sql .= " AND itemtype = :itemtype"; $params['itemtype'] = $itemtype; } $contextlist->add_from_sql($sql, $params); } /** * Add users to a userlist who have favourites within the specified context. * * @param \core_privacy\local\request\userlist $userlist The userlist to add the users to. * @param string $itemtype the type of the favourited items. * @return void */ public static function add_userids_for_context(\core_privacy\local\request\userlist $userlist, ?string $itemtype = null) { if (empty($userlist)) { return; } $params = [ 'contextid' => $userlist->get_context()->id, 'component' => $userlist->get_component() ]; $sql = "SELECT userid FROM {favourite} WHERE contextid = :contextid AND component = :component"; if (!is_null($itemtype)) { $sql .= " AND itemtype = :itemtype"; $params['itemtype'] = $itemtype; } $userlist->add_from_sql('userid', $sql, $params); } /** * Get favourites data for the specified user in the specified component, item type and item ID. * * @param int $userid The id of the user in scope. * @param \context $context The context to which data is scoped. * @param string $component The favourite's component name. * @param string $itemtype The favourite's item type. * @param int $itemid The favourite's item ID. * @return array|null */ public static function get_favourites_info_for_user(int $userid, \context $context, string $component, string $itemtype, int $itemid) { global $DB; $params = [ 'userid' => $userid, 'component' => $component, 'itemtype' => $itemtype, 'itemid' => $itemid, 'contextid' => $context->id ]; if (!$favourited = $DB->get_record('favourite', $params)) { return; } return [ 'starred' => transform::yesno(true), 'ordering' => $favourited->ordering, 'timecreated' => transform::datetime($favourited->timecreated), 'timemodified' => transform::datetime($favourited->timemodified) ]; } /** * Delete all favourites for all users in the specified contexts, and component area. * * @param \context $context The context to which deletion is scoped. * @param string $component The favourite's component name. * @param string $itemtype The favourite's itemtype. * @param int $itemid Optional itemid associated with component. * @throws \dml_exception if any errors are encountered during deletion. */ public static function delete_favourites_for_all_users(\context $context, string $component, string $itemtype, int $itemid = 0) { global $DB; $params = [ 'component' => $component, 'itemtype' => $itemtype, 'contextid' => $context->id ]; $select = "component = :component AND itemtype =:itemtype AND contextid = :contextid"; if (!empty($itemid)) { $select .= " AND itemid = :itemid"; $params['itemid'] = $itemid; } $DB->delete_records_select('favourite', $select, $params); } /** * Delete all favourites for the specified users in the specified context, component area and item type. * * @param \core_privacy\local\request\approved_userlist $userlist The approved contexts and user information * to delete information for. * @param string $itemtype The favourite's itemtype. * @param int $itemid Optional itemid associated with component. * @throws \dml_exception if any errors are encountered during deletion. */ public static function delete_favourites_for_userlist(\core_privacy\local\request\approved_userlist $userlist, string $itemtype, int $itemid = 0) { global $DB; $userids = $userlist->get_userids(); if (empty($userids)) { return; } $context = $userlist->get_context(); list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); $params = [ 'component' => $userlist->get_component(), 'itemtype' => $itemtype, 'contextid' => $context->id ]; $params += $userparams; $select = "component = :component AND itemtype = :itemtype AND contextid = :contextid AND userid $usersql"; if (!empty($itemid)) { $select .= " AND itemid = :itemid"; $params['itemid'] = $itemid; } $DB->delete_records_select('favourite', $select, $params); } /** * Delete all favourites for the specified user, in the specified contexts. * * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. * @param string $component The favourite's component name. * @param string $itemtype The favourite's itemtype. * @param int $itemid Optional itemid associated with component. * @throws \coding_exception * @throws \dml_exception */ public static function delete_favourites_for_user(approved_contextlist $contextlist, string $component, string $itemtype, int $itemid = 0) { global $DB; $userid = $contextlist->get_user()->id; list($insql, $inparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); $params = [ 'userid' => $userid, 'component' => $component, 'itemtype' => $itemtype, ]; $params += $inparams; $select = "userid = :userid AND component = :component AND itemtype =:itemtype AND contextid $insql"; if (!empty($itemid)) { $select .= " AND itemid = :itemid"; $params['itemid'] = $itemid; } $DB->delete_records_select('favourite', $select, $params); } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/auth/ldap/classes/privacy/provider.php000064400000002744152266615640026243 0ustar00. /** * Privacy Subsystem implementation for auth_ldap. * * @package auth_ldap * @copyright 2018 Carlos Escobedo * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace auth_ldap\privacy; defined('MOODLE_INTERNAL') || die(); /** * Privacy Subsystem for auth_ldap implementing null_provider. * * @copyright 2018 Carlos Escobedo * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\null_provider { /** * Get the language string identifier with the component's language * file to explain why this plugin stores no data. * * @return string */ public static function get_reason(): string { return 'privacy:metadata'; } }home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/analytics/classes/privacy/provider.php000064400000056543152266616270026357 0ustar00. /** * Privacy Subsystem implementation for core_analytics. * * @package core_analytics * @copyright 2018 David Monllaó * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core_analytics\privacy; use core_privacy\local\request\transform; use core_privacy\local\request\writer; use core_privacy\local\metadata\collection; use core_privacy\local\request\approved_contextlist; use core_privacy\local\request\approved_userlist; use core_privacy\local\request\context; use core_privacy\local\request\contextlist; use core_privacy\local\request\userlist; defined('MOODLE_INTERNAL') || die(); /** * Privacy Subsystem for core_analytics implementing metadata and plugin providers. * * @copyright 2018 David Monllaó * @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\plugin\provider { /** * Returns meta data about this system. * * @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_database_table( 'analytics_indicator_calc', [ 'starttime' => 'privacy:metadata:analytics:indicatorcalc:starttime', 'endtime' => 'privacy:metadata:analytics:indicatorcalc:endtime', 'contextid' => 'privacy:metadata:analytics:indicatorcalc:contextid', 'sampleorigin' => 'privacy:metadata:analytics:indicatorcalc:sampleorigin', 'sampleid' => 'privacy:metadata:analytics:indicatorcalc:sampleid', 'indicator' => 'privacy:metadata:analytics:indicatorcalc:indicator', 'value' => 'privacy:metadata:analytics:indicatorcalc:value', 'timecreated' => 'privacy:metadata:analytics:indicatorcalc:timecreated', ], 'privacy:metadata:analytics:indicatorcalc' ); $collection->add_database_table( 'analytics_predictions', [ 'modelid' => 'privacy:metadata:analytics:predictions:modelid', 'contextid' => 'privacy:metadata:analytics:predictions:contextid', 'sampleid' => 'privacy:metadata:analytics:predictions:sampleid', 'rangeindex' => 'privacy:metadata:analytics:predictions:rangeindex', 'prediction' => 'privacy:metadata:analytics:predictions:prediction', 'predictionscore' => 'privacy:metadata:analytics:predictions:predictionscore', 'calculations' => 'privacy:metadata:analytics:predictions:calculations', 'timecreated' => 'privacy:metadata:analytics:predictions:timecreated', 'timestart' => 'privacy:metadata:analytics:predictions:timestart', 'timeend' => 'privacy:metadata:analytics:predictions:timeend', ], 'privacy:metadata:analytics:predictions' ); $collection->add_database_table( 'analytics_prediction_actions', [ 'predictionid' => 'privacy:metadata:analytics:predictionactions:predictionid', 'userid' => 'privacy:metadata:analytics:predictionactions:userid', 'actionname' => 'privacy:metadata:analytics:predictionactions:actionname', 'timecreated' => 'privacy:metadata:analytics:predictionactions:timecreated', ], 'privacy:metadata:analytics:predictionactions' ); // Regarding this block, we are unable to export or purge this data, as // it would damage the analytics data across the whole site. $collection->add_database_table( 'analytics_models', [ 'usermodified' => 'privacy:metadata:analytics:analyticsmodels:usermodified', ], 'privacy:metadata:analytics:analyticsmodels' ); // Regarding this block, we are unable to export or purge this data, as // it would damage the analytics log data across the whole site. $collection->add_database_table( 'analytics_models_log', [ 'usermodified' => 'privacy:metadata:analytics:analyticsmodelslog:usermodified', ], 'privacy:metadata:analytics:analyticsmodelslog' ); 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 { global $DB; $contextlist = new \core_privacy\local\request\contextlist(); $models = self::get_models_with_user_data(); foreach ($models as $modelid => $model) { $analyser = $model->get_analyser(['notimesplitting' => true]); // Analytics predictions. $joinusersql = $analyser->join_sample_user('ap'); $sql = "SELECT DISTINCT ap.contextid FROM {analytics_predictions} ap {$joinusersql} WHERE u.id = :userid AND ap.modelid = :modelid"; $contextlist->add_from_sql($sql, ['userid' => $userid, 'modelid' => $modelid]); // Indicator calculations. $joinusersql = $analyser->join_sample_user('aic'); $sql = "SELECT DISTINCT aic.contextid FROM {analytics_indicator_calc} aic {$joinusersql} WHERE u.id = :userid AND aic.sampleorigin = :analysersamplesorigin"; $contextlist->add_from_sql($sql, ['userid' => $userid, 'analysersamplesorigin' => $analyser->get_samples_origin()]); } // We can leave this out of the loop as there is no analyser-dependent stuff. list($sql, $params) = self::analytics_prediction_actions_user_sql($userid, array_keys($models)); $sql = "SELECT DISTINCT ap.contextid" . $sql; $contextlist->add_from_sql($sql, $params); return $contextlist; } /** * Get the list of users who have data within a 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) { global $DB; $context = $userlist->get_context(); $models = self::get_models_with_user_data(); foreach ($models as $modelid => $model) { $analyser = $model->get_analyser(['notimesplitting' => true]); // Analytics predictions. $params = [ 'contextid' => $context->id, 'modelid' => $modelid, ]; $joinusersql = $analyser->join_sample_user('ap'); $sql = "SELECT u.id AS userid FROM {analytics_predictions} ap {$joinusersql} WHERE ap.contextid = :contextid AND ap.modelid = :modelid"; $userlist->add_from_sql('userid', $sql, $params); // Indicator calculations. $params = [ 'contextid' => $context->id, 'analysersamplesorigin' => $analyser->get_samples_origin(), ]; $joinusersql = $analyser->join_sample_user('aic'); $sql = "SELECT u.id AS userid FROM {analytics_indicator_calc} aic {$joinusersql} WHERE aic.contextid = :contextid AND aic.sampleorigin = :analysersamplesorigin"; $userlist->add_from_sql('userid', $sql, $params); } // We can leave this out of the loop as there is no analyser-dependent stuff. list($sql, $params) = self::analytics_prediction_actions_context_sql($context->id, array_keys($models)); $sql = "SELECT apa.userid" . $sql; $userlist->add_from_sql('userid', $sql, $params); } /** * 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) { global $DB; $userid = intval($contextlist->get_user()->id); $models = self::get_models_with_user_data(); $modelids = array_keys($models); list ($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); $rootpath = [get_string('analytics', 'analytics')]; $ctxfields = \context_helper::get_preload_record_columns_sql('ctx'); foreach ($models as $modelid => $model) { $analyser = $model->get_analyser(['notimesplitting' => true]); // Analytics predictions. $joinusersql = $analyser->join_sample_user('ap'); $sql = "SELECT ap.*, $ctxfields FROM {analytics_predictions} ap JOIN {context} ctx ON ctx.id = ap.contextid {$joinusersql} WHERE u.id = :userid AND ap.modelid = :modelid AND ap.contextid {$contextsql}"; $params = ['userid' => $userid, 'modelid' => $modelid] + $contextparams; $predictions = $DB->get_recordset_sql($sql, $params); foreach ($predictions as $prediction) { \context_helper::preload_from_record($prediction); $context = \context::instance_by_id($prediction->contextid); $path = $rootpath; $path[] = get_string('privacy:metadata:analytics:predictions', 'analytics'); $path[] = $prediction->id; $data = (object)[ 'target' => $model->get_target()->get_name()->out(), 'context' => $context->get_context_name(true, true), 'prediction' => $model->get_target()->get_display_value($prediction->prediction), 'timestart' => transform::datetime($prediction->timestart), 'timeend' => transform::datetime($prediction->timeend), 'timecreated' => transform::datetime($prediction->timecreated), ]; writer::with_context($context)->export_data($path, $data); } $predictions->close(); // Indicator calculations. $joinusersql = $analyser->join_sample_user('aic'); $sql = "SELECT aic.*, $ctxfields FROM {analytics_indicator_calc} aic JOIN {context} ctx ON ctx.id = aic.contextid {$joinusersql} WHERE u.id = :userid AND aic.sampleorigin = :analysersamplesorigin AND aic.contextid {$contextsql}"; $params = ['userid' => $userid, 'analysersamplesorigin' => $analyser->get_samples_origin()] + $contextparams; $indicatorcalculations = $DB->get_recordset_sql($sql, $params); foreach ($indicatorcalculations as $calculation) { \context_helper::preload_from_record($calculation); $context = \context::instance_by_id($calculation->contextid); $path = $rootpath; $path[] = get_string('privacy:metadata:analytics:indicatorcalc', 'analytics'); $path[] = $calculation->id; $indicator = \core_analytics\manager::get_indicator($calculation->indicator); $data = (object)[ 'indicator' => $indicator::get_name()->out(), 'context' => $context->get_context_name(true, true), 'calculation' => $indicator->get_display_value($calculation->value), 'starttime' => transform::datetime($calculation->starttime), 'endtime' => transform::datetime($calculation->endtime), 'timecreated' => transform::datetime($calculation->timecreated), ]; writer::with_context($context)->export_data($path, $data); } $indicatorcalculations->close(); } // Analytics predictions. // Provided contexts are ignored as we export all user-related stuff. list($sql, $params) = self::analytics_prediction_actions_user_sql($userid, $modelids, $contextsql); $sql = "SELECT apa.*, ap.modelid, ap.contextid, $ctxfields" . $sql; $predictionactions = $DB->get_recordset_sql($sql, $params + $contextparams); foreach ($predictionactions as $predictionaction) { \context_helper::preload_from_record($predictionaction); $context = \context::instance_by_id($predictionaction->contextid); $path = $rootpath; $path[] = get_string('privacy:metadata:analytics:predictionactions', 'analytics'); $path[] = $predictionaction->id; $data = (object)[ 'target' => $models[$predictionaction->modelid]->get_target()->get_name()->out(), 'context' => $context->get_context_name(true, true), 'action' => $predictionaction->actionname, 'timecreated' => transform::datetime($predictionaction->timecreated), ]; writer::with_context($context)->export_data($path, $data); } $predictionactions->close(); } /** * Delete all data for all users in the specified context. * * @param context $context The specific context to delete data for. */ public static function delete_data_for_all_users_in_context(\context $context) { global $DB; $models = self::get_models_with_user_data(); $modelids = array_keys($models); foreach ($models as $modelid => $model) { $idssql = "SELECT ap.id FROM {analytics_predictions} ap WHERE ap.contextid = :contextid AND ap.modelid = :modelid"; $idsparams = ['contextid' => $context->id, 'modelid' => $modelid]; $DB->delete_records_select('analytics_prediction_actions', "predictionid IN ($idssql)", $idsparams); $DB->delete_records_select('analytics_predictions', "contextid = :contextid AND modelid = :modelid", $idsparams); } // We delete them all this table is just a cache and we don't know which model filled it. $DB->delete_records('analytics_indicator_calc', ['contextid' => $context->id]); } /** * 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) { global $DB; $userid = intval($contextlist->get_user()->id); $models = self::get_models_with_user_data(); $modelids = array_keys($models); list ($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); // Analytics prediction actions. list($sql, $apaparams) = self::analytics_prediction_actions_user_sql($userid, $modelids, $contextsql); $sql = "SELECT apa.id " . $sql; $predictionactionids = $DB->get_fieldset_sql($sql, $apaparams + $contextparams); if ($predictionactionids) { list ($predictionactionidssql, $params) = $DB->get_in_or_equal($predictionactionids); $DB->delete_records_select('analytics_prediction_actions', "id {$predictionactionidssql}", $params); } foreach ($models as $modelid => $model) { $analyser = $model->get_analyser(['notimesplitting' => true]); // Analytics predictions. $joinusersql = $analyser->join_sample_user('ap'); $sql = "SELECT DISTINCT ap.id FROM {analytics_predictions} ap {$joinusersql} WHERE u.id = :userid AND ap.modelid = :modelid AND ap.contextid {$contextsql}"; $predictionids = $DB->get_fieldset_sql($sql, ['userid' => $userid, 'modelid' => $modelid] + $contextparams); if ($predictionids) { list($predictionidssql, $params) = $DB->get_in_or_equal($predictionids, SQL_PARAMS_NAMED); $DB->delete_records_select('analytics_predictions', "id $predictionidssql", $params); } // Indicator calculations. $joinusersql = $analyser->join_sample_user('aic'); $sql = "SELECT DISTINCT aic.id FROM {analytics_indicator_calc} aic {$joinusersql} WHERE u.id = :userid AND aic.sampleorigin = :analysersamplesorigin AND aic.contextid {$contextsql}"; $params = ['userid' => $userid, 'analysersamplesorigin' => $analyser->get_samples_origin()] + $contextparams; $indicatorcalcids = $DB->get_fieldset_sql($sql, $params); if ($indicatorcalcids) { list ($indicatorcalcidssql, $params) = $DB->get_in_or_equal($indicatorcalcids, SQL_PARAMS_NAMED); $DB->delete_records_select('analytics_indicator_calc', "id $indicatorcalcidssql", $params); } } } /** * 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) { global $DB; $context = $userlist->get_context(); $models = self::get_models_with_user_data(); $modelids = array_keys($models); list($usersinsql, $baseparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED); // Analytics prediction actions. list($sql, $apaparams) = self::analytics_prediction_actions_context_sql($context->id, $modelids, $usersinsql); $sql = "SELECT apa.id" . $sql; $predictionactionids = $DB->get_fieldset_sql($sql, $baseparams + $apaparams); if ($predictionactionids) { list ($predictionactionidssql, $params) = $DB->get_in_or_equal($predictionactionids); $DB->delete_records_select('analytics_prediction_actions', "id {$predictionactionidssql}", $params); } $baseparams['contextid'] = $context->id; foreach ($models as $modelid => $model) { $analyser = $model->get_analyser(['notimesplitting' => true]); // Analytics predictions. $joinusersql = $analyser->join_sample_user('ap'); $sql = "SELECT DISTINCT ap.id FROM {analytics_predictions} ap {$joinusersql} WHERE ap.contextid = :contextid AND ap.modelid = :modelid AND u.id {$usersinsql}"; $params = $baseparams; $params['modelid'] = $modelid; $predictionids = $DB->get_fieldset_sql($sql, $params); if ($predictionids) { list($predictionidssql, $params) = $DB->get_in_or_equal($predictionids, SQL_PARAMS_NAMED); $DB->delete_records_select('analytics_predictions', "id {$predictionidssql}", $params); } // Indicator calculations. $joinusersql = $analyser->join_sample_user('aic'); $sql = "SELECT DISTINCT aic.id FROM {analytics_indicator_calc} aic {$joinusersql} WHERE aic.contextid = :contextid AND aic.sampleorigin = :analysersamplesorigin AND u.id {$usersinsql}"; $params = $baseparams; $params['analysersamplesorigin'] = $analyser->get_samples_origin(); $indicatorcalcids = $DB->get_fieldset_sql($sql, $params); if ($indicatorcalcids) { list ($indicatorcalcidssql, $params) = $DB->get_in_or_equal($indicatorcalcids, SQL_PARAMS_NAMED); $DB->delete_records_select('analytics_indicator_calc', "id $indicatorcalcidssql", $params); } } } /** * Returns a list of models with user data. * * @return \core_analytics\model[] */ private static function get_models_with_user_data() { $models = \core_analytics\manager::get_all_models(); foreach ($models as $modelid => $model) { $analyser = $model->get_analyser(['notimesplitting' => true]); if (!$analyser->processes_user_data()) { unset($models[$modelid]); } } return $models; } /** * Returns the sql query to query analytics_prediction_actions table by user ID. * * @param int $userid The user ID of the analytics prediction. * @param int[] $modelids Model IDs to include in the SQL. * @param string $contextsql Optional "in or equal" SQL to also query by context ID(s). * @return array sql string in [0] and params in [1]. */ private static function analytics_prediction_actions_user_sql($userid, $modelids, $contextsql = false) { global $DB; list($insql, $params) = $DB->get_in_or_equal($modelids, SQL_PARAMS_NAMED); $sql = " FROM {analytics_predictions} ap JOIN {context} ctx ON ctx.id = ap.contextid JOIN {analytics_prediction_actions} apa ON apa.predictionid = ap.id JOIN {analytics_models} am ON ap.modelid = am.id WHERE apa.userid = :userid AND ap.modelid {$insql}"; $params['userid'] = $userid; if ($contextsql) { $sql .= " AND ap.contextid $contextsql"; } return [$sql, $params]; } /** * Returns the sql query to query analytics_prediction_actions table by context ID. * * @param int $contextid The context ID of the analytics prediction. * @param int[] $modelids Model IDs to include in the SQL. * @param string $usersql Optional "in or equal" SQL to also query by user ID(s). * @return array sql string in [0] and params in [1]. */ private static function analytics_prediction_actions_context_sql($contextid, $modelids, $usersql = false) { global $DB; list($insql, $params) = $DB->get_in_or_equal($modelids, SQL_PARAMS_NAMED); $sql = " FROM {analytics_predictions} ap JOIN {analytics_prediction_actions} apa ON apa.predictionid = ap.id WHERE ap.contextid = :contextid AND ap.modelid {$insql}"; $params['contextid'] = $contextid; if ($usersql) { $sql .= " AND apa.userid {$usersql}"; } return [$sql, $params]; } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/competency/classes/privacy/provider.php000064400000302545152266616300026524 0ustar00. namespace core_competency\privacy; use context; use context_course; use context_helper; use context_module; use context_system; use context_user; use moodle_recordset; use core_competency\api; use core_competency\competency; use core_competency\competency_framework; use core_competency\course_competency; use core_competency\course_competency_settings; use core_competency\course_module_competency; use core_competency\evidence; use core_competency\plan; use core_competency\plan_competency; use core_competency\related_competency; use core_competency\template; use core_competency\template_cohort; use core_competency\template_competency; use core_competency\user_competency; use core_competency\user_competency_course; use core_competency\user_competency_plan; use core_competency\user_evidence; use core_competency\user_evidence_competency; use core_competency\external\performance_helper; use core_privacy\local\metadata\collection; use core_privacy\local\request\approved_userlist; use core_privacy\local\request\contextlist; use core_privacy\local\request\approved_contextlist; use core_privacy\local\request\transform; use core_privacy\local\request\userlist; use core_privacy\local\request\writer; /** * Data provider class. * * @package core_competency * @copyright 2018 Frédéric Massart * @author Frédéric Massart * @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 { /** * Returns metadata. * * @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 { // Tables not related to users aside from the editing information. $collection->add_database_table('competency', [ 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency'); $collection->add_database_table('competency_coursecompsetting', [ 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_coursecompsetting'); $collection->add_database_table('competency_framework', [ 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_framework'); $collection->add_database_table('competency_coursecomp', [ 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_coursecomp'); $collection->add_database_table('competency_template', [ 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_template'); $collection->add_database_table('competency_templatecomp', [ 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_templatecomp'); $collection->add_database_table('competency_templatecohort', [ 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_templatecohort'); $collection->add_database_table('competency_relatedcomp', [ 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_relatedcomp'); $collection->add_database_table('competency_modulecomp', [ 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_modulecomp'); // Tables containing user data. $collection->add_database_table('competency_plan', [ 'name' => 'privacy:metadata:plan:name', 'description' => 'privacy:metadata:plan:description', 'userid' => 'privacy:metadata:plan:userid', 'status' => 'privacy:metadata:plan:status', 'duedate' => 'privacy:metadata:plan:duedate', 'reviewerid' => 'privacy:metadata:plan:reviewerid', 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_plan'); $collection->add_database_table('competency_usercomp', [ 'userid' => 'privacy:metadata:usercomp:userid', 'status' => 'privacy:metadata:usercomp:status', 'reviewerid' => 'privacy:metadata:usercomp:reviewerid', 'proficiency' => 'privacy:metadata:usercomp:proficiency', 'grade' => 'privacy:metadata:usercomp:grade', 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_usercomp'); $collection->add_database_table('competency_usercompcourse', [ 'userid' => 'privacy:metadata:usercomp:userid', 'proficiency' => 'privacy:metadata:usercomp:proficiency', 'grade' => 'privacy:metadata:usercomp:grade', 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_usercompcourse'); $collection->add_database_table('competency_usercompplan', [ 'userid' => 'privacy:metadata:usercomp:userid', 'proficiency' => 'privacy:metadata:usercomp:proficiency', 'grade' => 'privacy:metadata:usercomp:grade', 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_usercompplan'); $collection->add_database_table('competency_plancomp', [ 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_plancomp'); $collection->add_database_table('competency_evidence', [ 'action' => 'privacy:metadata:evidence:action', 'actionuserid' => 'privacy:metadata:evidence:actionuserid', 'descidentifier' => 'privacy:metadata:evidence:descidentifier', 'desccomponent' => 'privacy:metadata:evidence:desccomponent', 'desca' => 'privacy:metadata:evidence:desca', 'url' => 'privacy:metadata:evidence:url', 'grade' => 'privacy:metadata:evidence:grade', 'note' => 'privacy:metadata:evidence:note', 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_evidence'); $collection->add_database_table('competency_userevidence', [ 'name' => 'privacy:metadata:userevidence:name', 'description' => 'privacy:metadata:userevidence:description', 'url' => 'privacy:metadata:userevidence:url', 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_userevidence'); $collection->add_database_table('competency_userevidencecomp', [ 'timecreated' => 'privacy:metadata:timecreated', 'timemodified' => 'privacy:metadata:timemodified', 'usermodified' => 'privacy:metadata:usermodified', ], 'privacy:metadata:competency_userevidencecomp'); // Comments can be left on learning plans and competencies. $collection->link_subsystem('core_comment', 'privacy:metadata:core_comments'); 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 { global $DB; $contextlist = new \core_privacy\local\request\contextlist(); // Find the contexts of the frameworks, and related data, modified by the user. $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . competency_framework::TABLE . "} cf ON cf.contextid = ctx.id WHERE cf.usermodified = :userid1"; $params = ['userid1' => $userid]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . competency_framework::TABLE . "} cf ON cf.contextid = ctx.id JOIN {" . competency::TABLE . "} c ON c.competencyframeworkid = cf.id WHERE c.usermodified = :userid2"; $params = ['userid2' => $userid]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . competency_framework::TABLE . "} cf ON cf.contextid = ctx.id JOIN {" . competency::TABLE . "} c ON c.competencyframeworkid = cf.id JOIN {" . related_competency::TABLE . "} cr ON cr.competencyid = c.id WHERE cr.usermodified = :userid3"; $params = ['userid3' => $userid]; $contextlist->add_from_sql($sql, $params); // Find the contexts of the templates, and related data, modified by the user. $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . template::TABLE . "} tpl ON tpl.contextid = ctx.id WHERE tpl.usermodified = :userid1"; $params = ['userid1' => $userid]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . template::TABLE . "} tpl ON tpl.contextid = ctx.id JOIN {" . template_cohort::TABLE . "} tch ON tch.templateid = tpl.id AND tch.usermodified = :userid2"; $params = ['userid2' => $userid]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . template::TABLE . "} tpl ON tpl.contextid = ctx.id JOIN {" . template_competency::TABLE . "} tc ON tc.templateid = tpl.id AND tc.usermodified = :userid3"; $params = ['userid3' => $userid]; $contextlist->add_from_sql($sql, $params); // Find the possible course contexts. $sql = " SELECT DISTINCT ctx.id FROM {" . course_competency::TABLE . "} cc JOIN {context} ctx ON ctx.instanceid = cc.courseid AND ctx.contextlevel = :courselevel WHERE cc.usermodified = :userid"; $params = ['courselevel' => CONTEXT_COURSE, 'userid' => $userid]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {" . course_competency_settings::TABLE . "} ccs JOIN {context} ctx ON ctx.instanceid = ccs.courseid AND ctx.contextlevel = :courselevel WHERE ccs.usermodified = :userid"; $params = ['courselevel' => CONTEXT_COURSE, 'userid' => $userid]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {" . user_competency_course::TABLE . "} ucc JOIN {context} ctx ON ctx.instanceid = ucc.courseid AND ctx.contextlevel = :courselevel WHERE ucc.usermodified = :userid"; $params = ['courselevel' => CONTEXT_COURSE, 'userid' => $userid]; $contextlist->add_from_sql($sql, $params); // Find the possible module contexts. $sql = " SELECT DISTINCT ctx.id FROM {" . course_module_competency::TABLE . "} cmc JOIN {context} ctx ON ctx.instanceid = cmc.cmid AND ctx.contextlevel = :modulelevel WHERE cmc.usermodified = :userid"; $params = ['modulelevel' => CONTEXT_MODULE, 'userid' => $userid]; $contextlist->add_from_sql($sql, $params); // Add user contexts through usermodified/reviewing of plan related data. $sql = " SELECT DISTINCT ctx.id FROM {" . plan::TABLE . "} p JOIN {context} ctx ON ctx.instanceid = p.userid AND ctx.contextlevel = :userlevel WHERE p.usermodified = :userid1"; $params = [ 'userlevel' => CONTEXT_USER, 'userid1' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {" . plan::TABLE . "} p JOIN {context} ctx ON ctx.instanceid = p.userid AND ctx.contextlevel = :userlevel WHERE p.reviewerid = :userid2"; $params = [ 'userlevel' => CONTEXT_USER, 'userid2' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {" . plan::TABLE . "} p JOIN {context} ctx ON ctx.instanceid = p.userid AND ctx.contextlevel = :userlevel JOIN {" . plan_competency::TABLE . "} pc ON pc.planid = p.id AND pc.usermodified = :userid3"; $params = [ 'userlevel' => CONTEXT_USER, 'userid3' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {" . plan::TABLE . "} p JOIN {context} ctx ON ctx.instanceid = p.userid AND ctx.contextlevel = :userlevel JOIN {" . user_competency_plan::TABLE . "} upc ON upc.planid = p.id AND upc.usermodified = :userid4"; $params = [ 'userlevel' => CONTEXT_USER, 'userid4' => $userid, ]; $contextlist->add_from_sql($sql, $params); // Add user contexts through usermodified/reviewing of competency data. $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . user_competency::TABLE . "} uc ON uc.userid = ctx.instanceid AND ctx.contextlevel = :userlevel1 WHERE uc.usermodified = :userid1"; $params = [ 'userlevel1' => CONTEXT_USER, 'userid1' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . user_competency::TABLE . "} uc ON uc.userid = ctx.instanceid AND ctx.contextlevel = :userlevel1 WHERE uc.reviewerid = :userid2"; $params = [ 'userlevel1' => CONTEXT_USER, 'userid2' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . user_competency::TABLE . "} uc ON uc.userid = ctx.instanceid AND ctx.contextlevel = :userlevel1 JOIN {" . evidence::TABLE . "} e ON e.usercompetencyid = uc.id AND e.usermodified = :userid3"; $params = [ 'userlevel1' => CONTEXT_USER, 'userid3' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . user_competency::TABLE . "} uc ON uc.userid = ctx.instanceid AND ctx.contextlevel = :userlevel1 JOIN {" . evidence::TABLE . "} e ON e.usercompetencyid = uc.id AND e.actionuserid = :userid4"; $params = [ 'userlevel1' => CONTEXT_USER, 'userid4' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . user_evidence::TABLE . "} ue ON ue.userid = ctx.instanceid AND ctx.contextlevel = :userlevel2 AND ue.usermodified = :userid5"; $params = [ 'userlevel2' => CONTEXT_USER, 'userid5' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . user_evidence::TABLE . "} ue ON ue.userid = ctx.instanceid AND ctx.contextlevel = :userlevel2 JOIN {" . user_evidence_competency::TABLE . "} uec ON uec.userevidenceid = ue.id AND uec.usermodified = :userid6"; $params = [ 'userlevel2' => CONTEXT_USER, 'userid6' => $userid, ]; $contextlist->add_from_sql($sql, $params); // Now, the easy part, we fetch the user context for user plans and competencies. // We also fetch the course context for the state of competencies for the user in courses. $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . plan::TABLE . "} p ON p.userid = ctx.instanceid AND ctx.contextlevel = :userlevel1 WHERE p.userid = :userid1"; $params = [ 'userlevel1' => CONTEXT_USER, 'userid1' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . user_competency::TABLE . "} uc ON uc.userid = ctx.instanceid AND ctx.contextlevel = :userlevel2 AND uc.userid = :userid2"; $params = [ 'userlevel2' => CONTEXT_USER, 'userid2' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . user_evidence::TABLE . "} ue ON ue.userid = ctx.instanceid AND ctx.contextlevel = :userlevel3 AND ue.userid = :userid3"; $params = [ 'userlevel3' => CONTEXT_USER, 'userid3' => $userid, ]; $contextlist->add_from_sql($sql, $params); $sql = " SELECT DISTINCT ctx.id FROM {context} ctx JOIN {" . user_competency_course::TABLE . "} ucc ON ucc.courseid = ctx.instanceid AND ctx.contextlevel = :courselevel AND ucc.userid = :userid4"; $params = [ 'courselevel' => CONTEXT_COURSE, 'userid4' => $userid, ]; $contextlist->add_from_sql($sql, $params); // Include the user contexts in which the user commented. $sql = " SELECT ctx.id FROM {context} ctx JOIN {comments} c ON c.contextid = ctx.id WHERE c.component = :component AND c.commentarea IN (:planarea, :usercomparea) AND c.userid = :userid"; $params = [ 'component' => 'competency', // Must not be core_competency. 'planarea' => 'plan', 'usercomparea' => 'user_competency', 'userid' => $userid ]; $contextlist->add_from_sql($sql, $params); return $contextlist; } /** * Get the list of users who have data within a 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(); $params = ['contextid' => $context->id]; // Add users who have modified the frameworks and related data in the context. $sql = " SELECT cf.usermodified FROM {" . competency_framework::TABLE . "} cf WHERE cf.contextid = :contextid"; $userlist->add_from_sql('usermodified', $sql, $params); $sql = " SELECT c.usermodified FROM {" . competency_framework::TABLE . "} cf JOIN {" . competency::TABLE . "} c ON c.competencyframeworkid = cf.id WHERE cf.contextid = :contextid"; $userlist->add_from_sql('usermodified', $sql, $params); $sql = " SELECT cr.usermodified FROM {" . competency_framework::TABLE . "} cf JOIN {" . competency::TABLE . "} c ON c.competencyframeworkid = cf.id JOIN {" . related_competency::TABLE . "} cr ON cr.competencyid = c.id WHERE cf.contextid = :contextid"; $userlist->add_from_sql('usermodified', $sql, $params); // Add users who have modified the templates and related data in the context. $sql = " SELECT tpl.usermodified FROM {" . template::TABLE . "} tpl WHERE tpl.contextid = :contextid"; $userlist->add_from_sql('usermodified', $sql, $params); $sql = " SELECT tch.usermodified FROM {" . template::TABLE . "} tpl JOIN {" . template_cohort::TABLE . "} tch ON tch.templateid = tpl.id WHERE tpl.contextid = :contextid"; $userlist->add_from_sql('usermodified', $sql, $params); $sql = " SELECT tc.usermodified FROM {" . template::TABLE . "} tpl JOIN {" . template_competency::TABLE . "} tc ON tc.templateid = tpl.id WHERE tpl.contextid = :contextid"; $userlist->add_from_sql('usermodified', $sql, $params); // Add users if userlist is in course context. if (is_a($context, \context_course::class)) { $params = ['courseid' => $context->instanceid]; $sql = " SELECT cc.usermodified FROM {" . course_competency::TABLE . "} cc WHERE cc.courseid = :courseid"; $userlist->add_from_sql('usermodified', $sql, $params); $sql = " SELECT ccs.usermodified FROM {" . course_competency_settings::TABLE . "} ccs WHERE ccs.courseid = :courseid"; $userlist->add_from_sql('usermodified', $sql, $params); $sql = " SELECT ucc.userid, ucc.usermodified FROM {" . user_competency_course::TABLE . "} ucc WHERE ucc.courseid = :courseid"; $userlist->add_from_sql('userid', $sql, $params); $userlist->add_from_sql('usermodified', $sql, $params); } else if (is_a($context, \context_module::class)) { // Add users if userlist is in module context. $params = ['moduleid' => $context->instanceid]; $sql = " SELECT cmc.usermodified FROM {" . course_module_competency::TABLE . "} cmc WHERE cmc.cmid = :moduleid"; $userlist->add_from_sql('usermodified', $sql, $params); } else if (is_a($context, \context_user::class)) { $params = ['userid' => $context->instanceid]; // Add users through plan related data. $sql = " SELECT p.userid, p.usermodified, p.reviewerid FROM {" . plan::TABLE . "} p WHERE p.userid = :userid"; $userlist->add_from_sql('userid', $sql, $params); $userlist->add_from_sql('usermodified', $sql, $params); $userlist->add_from_sql('reviewerid', $sql, $params); $sql = " SELECT pc.usermodified FROM {" . plan::TABLE . "} p JOIN {" . plan_competency::TABLE . "} pc ON pc.planid = p.id WHERE p.userid = :userid"; $userlist->add_from_sql('usermodified', $sql, $params); $sql = " SELECT upc.usermodified FROM {" . user_competency_plan::TABLE . "} upc WHERE upc.userid = :userid"; $userlist->add_from_sql('usermodified', $sql, $params); // Add users through competency data. $sql = " SELECT uc.userid, uc.usermodified, uc.reviewerid FROM {" . user_competency::TABLE . "} uc WHERE uc.userid = :userid"; $userlist->add_from_sql('userid', $sql, $params); $userlist->add_from_sql('usermodified', $sql, $params); $userlist->add_from_sql('reviewerid', $sql, $params); $sql = " SELECT e.usermodified, e.actionuserid FROM {" . user_competency::TABLE . "} uc JOIN {" . evidence::TABLE . "} e ON e.usercompetencyid = uc.id WHERE uc.userid = :userid"; $userlist->add_from_sql('usermodified', $sql, $params); $userlist->add_from_sql('actionuserid', $sql, $params); // Add users through evidence data. $sql = " SELECT ue.userid, ue.usermodified FROM {" . user_evidence::TABLE . "} ue WHERE ue.userid = :userid"; $userlist->add_from_sql('userid', $sql, $params); $userlist->add_from_sql('usermodified', $sql, $params); $sql = " SELECT ue.usermodified FROM {" . user_evidence::TABLE . "} ue JOIN {" . user_evidence_competency::TABLE . "} uec ON uec.userevidenceid = ue.id WHERE ue.userid = :userid"; $userlist->add_from_sql('usermodified', $sql, $params); } // Add users who commented in the context. // Note: Comment component must be competency and not core_competency. \core_comment\privacy\provider::get_users_in_context_from_sql( $userlist, 'com', 'competency', 'plan', $context->id); \core_comment\privacy\provider::get_users_in_context_from_sql( $userlist, 'com', 'competency', 'user_competency', $context->id); } /** * Export all user data for the specified user, in the specified contexts. * * We skip the enabled check for competencies, as there could be historical data. This avoids exceptions thrown from * the {@see api::require_enabled} method, which is called at various points during export via the competency API * * @param approved_contextlist $contextlist The approved contexts to export information for. */ public static function export_user_data(approved_contextlist $contextlist) { // Export even if competencies are not currently enabled. api::skip_enabled(); $user = $contextlist->get_user(); $userid = $user->id; // Re-arrange the contexts by context level. $groupedcontexts = array_reduce($contextlist->get_contexts(), function($carry, $context) { $contextlevel = $context->contextlevel; if (!in_array($contextlevel, [CONTEXT_USER, CONTEXT_COURSE, CONTEXT_MODULE, CONTEXT_SYSTEM, CONTEXT_COURSECAT])) { return $carry; } $carry[$contextlevel][] = $context; return $carry; }, [ CONTEXT_COURSE => [], CONTEXT_COURSECAT => [], CONTEXT_MODULE => [], CONTEXT_SYSTEM => [], CONTEXT_USER => [], ]); // Process module contexts. static::export_user_data_in_module_contexts($userid, $groupedcontexts[CONTEXT_MODULE]); // Process course contexts. static::export_user_data_in_course_contexts($userid, $groupedcontexts[CONTEXT_COURSE]); // Process course categories context. static::export_user_data_in_category_contexts($userid, $groupedcontexts[CONTEXT_COURSECAT]); // Process system context. if (!empty($groupedcontexts[CONTEXT_SYSTEM])) { static::export_user_data_in_system_context($userid); } // Process user contexts. static::export_user_data_in_user_contexts($userid, $groupedcontexts[CONTEXT_USER]); } /** * Delete all data for all users in the specified context. * * @param context $context The specific context to delete data for. */ public static function delete_data_for_all_users_in_context(context $context) { global $DB; switch ($context->contextlevel) { case CONTEXT_USER: $userid = $context->instanceid; static::delete_user_evidence_of_prior_learning($userid); static::delete_user_plans($userid); static::delete_user_competencies($userid); break; case CONTEXT_COURSE: static::delete_user_competencies_in_course($context->instanceid); break; } } /** * Delete all user data for the specified user, in the specified contexts. * * Here we only delete the private data of user, not whether they modified, are reviewing, * or are associated with the record on at a second level. Only data directly linked to the * user will be affected. * * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. */ public static function delete_data_for_user(approved_contextlist $contextlist) { $user = $contextlist->get_user(); $userid = $user->id; foreach ($contextlist as $context) { switch ($context->contextlevel) { case CONTEXT_USER: if ($context->instanceid == $userid) { // Only delete the user's information when they requested their context to be deleted. We // do not take any action on other user's contexts because we don't own the data there. static::delete_user_evidence_of_prior_learning($userid); static::delete_user_plans($userid); static::delete_user_competencies($userid); } break; case CONTEXT_COURSE: static::delete_user_competencies_in_course($context->instanceid, [$userid]); break; } } } /** * Delete multiple users within a single context. * * Here we only delete the private data of users, not whether they modified, are reviewing, * or are associated with the record on at a second level. Only data directly linked to the * user will be affected. * * @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(); $userids = $userlist->get_userids(); switch ($context->contextlevel) { case CONTEXT_USER: // Only delete the user's information when their context is being deleted. // We do not take any action on other user's contexts because we don't own the data there. if (in_array($context->instanceid, $userids)) { static::delete_user_evidence_of_prior_learning($context->instanceid); static::delete_user_plans($context->instanceid); static::delete_user_competencies($context->instanceid); } break; case CONTEXT_COURSE: static::delete_user_competencies_in_course($context->instanceid, $userids); break; } } /** * Delete evidence of prior learning. * * @param int $userid The user ID. * @return void */ protected static function delete_user_evidence_of_prior_learning($userid) { global $DB; $usercontext = context_user::instance($userid); $ueids = $DB->get_fieldset_select(user_evidence::TABLE, 'id', 'userid = :userid', ['userid' => $userid]); if (empty($ueids)) { return; } list($insql, $inparams) = $DB->get_in_or_equal($ueids, SQL_PARAMS_NAMED); // Delete competencies associated with user evidence. $DB->delete_records_select(user_evidence_competency::TABLE, "userevidenceid $insql", $inparams); // Delete the user evidence. $DB->delete_records_select(user_evidence::TABLE, "id $insql", $inparams); // Delete the user evidence files. $fs = get_file_storage(); $fs->delete_area_files($usercontext->id, 'core_competency', 'userevidence'); } /** * User plans. * * @param int $userid The user ID. * @return void */ protected static function delete_user_plans($userid) { global $DB; $usercontext = context_user::instance($userid); // Remove all the comments made on plans. \core_comment\privacy\provider::delete_comments_for_all_users($usercontext, 'competency', 'plan'); // Find the user plan IDs. $planids = $DB->get_fieldset_select(plan::TABLE, 'id', 'userid = :userid', ['userid' => $userid]); if (empty($planids)) { return; } list($insql, $inparams) = $DB->get_in_or_equal($planids, SQL_PARAMS_NAMED); // Delete all the competencies proficiency in the plans. $DB->delete_records_select(user_competency_plan::TABLE, "planid $insql", $inparams); // Delete all the competencies in the plans. $DB->delete_records_select(plan_competency::TABLE, "planid $insql", $inparams); // Delete all the plans. $DB->delete_records_select(plan::TABLE, "id $insql", $inparams); } /** * Delete user competency data. * * @param int $userid The user ID. * @return void */ protected static function delete_user_competencies($userid) { global $DB; $usercontext = context_user::instance($userid); // Remove all the comments made on user competencies. \core_comment\privacy\provider::delete_comments_for_all_users($usercontext, 'competency', 'user_competency'); // Find the user competency IDs. $ucids = $DB->get_fieldset_select(user_competency::TABLE, 'id', 'userid = :userid', ['userid' => $userid]); if (empty($ucids)) { return; } list($insql, $inparams) = $DB->get_in_or_equal($ucids, SQL_PARAMS_NAMED); // Delete all the evidence associated with competencies. $DB->delete_records_select(evidence::TABLE, "usercompetencyid $insql", $inparams); // Delete all the record of competency. $DB->delete_records_select(user_competency::TABLE, "id $insql", $inparams); } /** * Delete the record of competencies for user(s) in a course. * * @param int $courseid The course ID. * @param int[] $userids The user IDs, if deleting for specific user(s). * @return void */ protected static function delete_user_competencies_in_course($courseid, $userids = []) { global $DB; $params = ['courseid' => $courseid]; $where = "courseid = :courseid"; if (!empty($userids)) { list($insql, $inparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); $params = $params + $inparams; $where .= " AND userid {$insql}"; } $DB->delete_records_select(user_competency_course::TABLE, $where, $params); } /** * Export the user data in user context. * * @param int $userid The user ID. * @param array $contexts The contexts. * @return void */ protected static function export_user_data_in_user_contexts($userid, array $contexts) { global $DB; $mycontext = context_user::instance($userid); $contextids = array_map(function($context) { return $context->id; }, $contexts); $exportowncontext = in_array($mycontext->id, $contextids); $othercontexts = array_filter($contextids, function($contextid) use ($mycontext) { return $contextid != $mycontext->id; }); if ($exportowncontext) { static::export_user_data_learning_plans($mycontext); static::export_user_data_competencies($mycontext); static::export_user_data_user_evidence($mycontext); } foreach ($othercontexts as $contextid) { static::export_user_data_learning_plans_related_to_me($userid, context::instance_by_id($contextid)); static::export_user_data_competencies_related_to_me($userid, context::instance_by_id($contextid)); static::export_user_data_user_evidence_related_to_me($userid, context::instance_by_id($contextid)); } } /** * Export the user data in systen context. * * @param int $userid The user ID. * @return void */ protected static function export_user_data_in_system_context($userid) { static::export_user_data_frameworks_in_context($userid, context_system::instance()); static::export_user_data_templates_in_context($userid, context_system::instance()); } /** * Export the user data in category contexts. * * @param int $userid The user ID. * @param array $contexts The contexts. * @return void */ protected static function export_user_data_in_category_contexts($userid, array $contexts) { $contexts = array_filter($contexts, function($context) { return $context->contextlevel == CONTEXT_COURSECAT; }); if (empty($contexts)) { return; } foreach ($contexts as $context) { static::export_user_data_frameworks_in_context($userid, $context); static::export_user_data_templates_in_context($userid, $context); } } /** * Export the user data in course contexts. * * @param int $userid The user whose data we're exporting. * @param array $contexts A list of contexts. * @return void */ protected static function export_user_data_in_course_contexts($userid, array $contexts) { global $DB; $contexts = array_filter($contexts, function($context) { return $context->contextlevel == CONTEXT_COURSE; }); if (empty($contexts)) { return; } $helper = new performance_helper(); $path = [get_string('competencies', 'core_competency')]; $courseids = array_map(function($context) { return $context->instanceid; }, $contexts); // Fetch all the records of competency proficiency in the course. $ffields = competency_framework::get_sql_fields('f', 'f_'); $compfields = competency::get_sql_fields('c', 'c_'); $uccfields = user_competency_course::get_sql_fields('ucc', 'ucc_'); $ctxfields = context_helper::get_preload_record_columns_sql('ctx'); list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); $sql = " SELECT $ffields, $compfields, $uccfields, $ctxfields FROM {" . user_competency_course::TABLE . "} ucc JOIN {" . competency::TABLE . "} c ON c.id = ucc.competencyid JOIN {" . competency_framework::TABLE . "} f ON f.id = c.competencyframeworkid JOIN {context} ctx ON ctx.id = f.contextid WHERE ucc.userid = :userid AND ucc.courseid $insql ORDER BY ucc.courseid, c.id"; $params = array_merge($inparams, ['userid' => $userid]); // Export data. $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'ucc_courseid', [], function($carry, $record) use ($helper) { context_helper::preload_from_record($record); $framework = new competency_framework(null, competency_framework::extract_record($record, 'f_')); $competency = new competency(null, competency::extract_record($record, 'c_')); $ucc = new user_competency_course(null, user_competency_course::extract_record($record, 'ucc_')); $helper->ingest_framework($framework); $carry[] = array_merge(static::transform_competency_brief($competency), [ 'rating' => [ 'rating' => static::transform_competency_grade($competency, $ucc->get('grade'), $helper), 'proficient' => static::transform_proficiency($ucc->get('proficiency')), 'timecreated' => transform::datetime($ucc->get('timecreated')), 'timemodified' => transform::datetime($ucc->get('timemodified')), ] ]); return $carry; }, function($courseid, $data) use ($path) { $context = context_course::instance($courseid); writer::with_context($context)->export_data($path, (object) ['ratings' => $data]); }); // Export usermodified data. static::export_user_data_in_course_contexts_associations($userid, $courseids, $path); static::export_user_data_in_course_contexts_settings($userid, $courseids, $path); static::export_user_data_in_course_contexts_rated_by_me($userid, $courseids, $path, $helper); } /** * Export the ratings given in a course. * * @param int $userid The user ID. * @param array $courseids The course IDs. * @param array $path The root path. * @param performance_helper $helper The performance helper. * @return void */ protected static function export_user_data_in_course_contexts_rated_by_me($userid, $courseids, $path, performance_helper $helper) { global $DB; // Fetch all the records of competency proficiency in the course. $ffields = competency_framework::get_sql_fields('f', 'f_'); $compfields = competency::get_sql_fields('c', 'c_'); $uccfields = user_competency_course::get_sql_fields('ucc', 'ucc_'); $ctxfields = context_helper::get_preload_record_columns_sql('ctx'); list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); $sql = " SELECT $ffields, $compfields, $uccfields, $ctxfields FROM {" . user_competency_course::TABLE . "} ucc JOIN {" . competency::TABLE . "} c ON c.id = ucc.competencyid JOIN {" . competency_framework::TABLE . "} f ON f.id = c.competencyframeworkid JOIN {context} ctx ON ctx.id = f.contextid WHERE ucc.usermodified = :userid AND ucc.courseid $insql ORDER BY ucc.courseid, ucc.id"; $params = array_merge($inparams, ['userid' => $userid]); // Export the data. static::recordset_loop_and_export($DB->get_recordset_sql($sql, $params), 'ucc_courseid', [], function($carry, $record) use ($helper) { context_helper::preload_from_record($record); $framework = new competency_framework(null, competency_framework::extract_record($record, 'f_')); $competency = new competency(null, competency::extract_record($record, 'c_')); $ucc = new user_competency_course(null, user_competency_course::extract_record($record, 'ucc_')); $helper->ingest_framework($framework); $carry[] = array_merge(static::transform_competency_brief($competency), [ 'rating' => [ 'userid' => transform::user($ucc->get('userid')), 'rating' => static::transform_competency_grade($competency, $ucc->get('grade'), $helper), 'proficient' => static::transform_proficiency($ucc->get('proficiency')), 'timemodified' => transform::datetime($ucc->get('timemodified')), ] ]); return $carry; }, function($courseid, $data) use ($path) { $context = context_course::instance($courseid); writer::with_context($context)->export_related_data($path, 'rated_by_me', (object) [ 'ratings' => $data ]); } ); } /** * Export user data in course contexts related to linked competencies. * * @param int $userid The user ID. * @param array $courseids The course IDs. * @param array $path The root path to export at. * @return void */ protected static function export_user_data_in_course_contexts_associations($userid, $courseids, $path) { global $DB; // Fetch all the courses with associations we created or modified. $compfields = competency::get_sql_fields('c', 'c_'); $ccfields = course_competency::get_sql_fields('cc', 'cc_'); $ctxfields = context_helper::get_preload_record_columns_sql('ctx'); list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); $sql = " SELECT $compfields, $ccfields, $ctxfields FROM {" . course_competency::TABLE . "} cc JOIN {" . competency::TABLE . "} c ON c.id = cc.competencyid JOIN {" . competency_framework::TABLE . "} f ON f.id = c.competencyframeworkid JOIN {context} ctx ON ctx.id = f.contextid WHERE cc.usermodified = :userid AND cc.courseid $insql ORDER BY cc.courseid, c.id"; $params = array_merge($inparams, ['userid' => $userid]); $recordset = $DB->get_recordset_sql($sql, $params); // Export the data. static::recordset_loop_and_export($recordset, 'cc_courseid', [], function($carry, $record) { context_helper::preload_from_record($record); $competency = new competency(null, competency::extract_record($record, 'c_')); $cc = new course_competency(null, course_competency::extract_record($record, 'cc_')); $carry[] = array_merge(static::transform_competency_brief($competency), [ 'timemodified' => transform::datetime($cc->get('timemodified')), 'created_or_modified_by_you' => transform::yesno(true) ]); return $carry; }, function($courseid, $data) use ($path, $userid, $DB) { $context = context_course::instance($courseid); writer::with_context($context)->export_related_data($path, 'associations', (object) ['competencies' => $data]); }); } /** * Export user data in course contexts related to course settings. * * @param int $userid The user ID. * @param array $courseids The course IDs. * @param array $path The root path to export at. * @return void */ protected static function export_user_data_in_course_contexts_settings($userid, $courseids, $path) { global $DB; // Fetch all the courses with associations we created or modified. $ccsfields = course_competency_settings::get_sql_fields('ccs', 'ccs_'); list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); $sql = " SELECT $ccsfields FROM {" . course_competency_settings::TABLE . "} ccs WHERE ccs.usermodified = :userid AND ccs.courseid $insql ORDER BY ccs.courseid"; $params = array_merge($inparams, ['userid' => $userid]); $recordset = $DB->get_recordset_sql($sql, $params); // Export the data. static::recordset_loop_and_export($recordset, 'ccs_courseid', [], function($carry, $record) { $ccs = new course_competency_settings(null, course_competency_settings::extract_record($record, 'ccs_')); return [ 'timemodified' => transform::datetime($ccs->get('timemodified')), 'created_or_modified_by_you' => transform::yesno(true) ]; }, function($courseid, $data) use ($path, $userid, $DB) { $context = context_course::instance($courseid); writer::with_context($context)->export_related_data($path, 'settings', (object) $data); }); } /** * Export the user data in module contexts. * * @param int $userid The user whose data we're exporting. * @param array $contexts A list of contexts. * @return void */ protected static function export_user_data_in_module_contexts($userid, array $contexts) { global $DB; $contexts = array_filter($contexts, function($context) { return $context->contextlevel == CONTEXT_MODULE; }); if (empty($contexts)) { return; } $path = [get_string('competencies', 'core_competency')]; $cmids = array_map(function($context) { return $context->instanceid; }, $contexts); // Fetch all the modules with associations we created or modified. $compfields = competency::get_sql_fields('c', 'c_'); $cmcfields = course_module_competency::get_sql_fields('cmc', 'cmc_'); $ctxfields = context_helper::get_preload_record_columns_sql('ctx'); list($insql, $inparams) = $DB->get_in_or_equal($cmids, SQL_PARAMS_NAMED); $sql = " SELECT $compfields, $cmcfields, $ctxfields FROM {" . course_module_competency::TABLE . "} cmc JOIN {" . competency::TABLE . "} c ON c.id = cmc.competencyid JOIN {" . competency_framework::TABLE . "} f ON f.id = c.competencyframeworkid JOIN {context} ctx ON ctx.id = f.contextid WHERE cmc.usermodified = :userid AND cmc.cmid $insql ORDER BY cmc.cmid"; $params = array_merge($inparams, ['userid' => $userid]); // Export the data. $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'cmc_cmid', [], function($carry, $record) { context_helper::preload_from_record($record); $competency = new competency(null, competency::extract_record($record, 'c_')); $cmc = new course_module_competency(null, course_module_competency::extract_record($record, 'cmc_')); $carry[] = array_merge(static::transform_competency_brief($competency), [ 'timecreated' => transform::datetime($cmc->get('timecreated')), 'timemodified' => transform::datetime($cmc->get('timemodified')), 'created_or_modified_by_you' => transform::yesno(true) ]); return $carry; }, function($cmid, $data) use ($path) { $context = context_module::instance($cmid); writer::with_context($context)->export_data($path, (object) ['associations' => $data]); }); } /** * Export a user's competencies. * * @param context_user $context The context of the user requesting the export. * @return void */ protected static function export_user_data_competencies(context_user $context) { global $DB; $userid = $context->instanceid; $path = [get_string('competencies', 'core_competency'), get_string('competencies', 'core_competency')]; $helper = new performance_helper(); $cfields = competency::get_sql_fields('c', 'c_'); $ucfields = user_competency::get_sql_fields('uc', 'uc_'); $efields = evidence::get_sql_fields('e', 'e_'); $makecomppath = function($competencyid, $data) use ($path) { return array_merge($path, [$data['name'] . ' (' . $competencyid . ')']); }; $sql = " SELECT $cfields, $ucfields, $efields FROM {" . user_competency::TABLE . "} uc JOIN {" . competency::TABLE . "} c ON c.id = uc.competencyid LEFT JOIN {" . evidence::TABLE . "} e ON uc.id = e.usercompetencyid WHERE uc.userid = :userid ORDER BY c.id, e.timecreated DESC, e.id DESC"; $params = ['userid' => $userid]; $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'c_id', null, function($carry, $record) use ($context, $userid, $helper, $makecomppath) { $competency = new competency(null, competency::extract_record($record, 'c_')); if ($carry === null) { $uc = new user_competency(null, user_competency::extract_record($record, 'uc_')); $carry = array_merge(static::transform_competency_brief($competency), [ 'rating' => static::transform_user_competency($userid, $uc, $competency, $helper), 'evidence' => [] ]); \core_comment\privacy\provider::export_comments($context, 'competency', 'user_competency', $uc->get('id'), $makecomppath($competency->get('id'), $carry), false); } // There is an evidence in this record. if (!empty($record->e_id)) { $evidence = new evidence(null, evidence::extract_record($record, 'e_')); $carry['evidence'][] = static::transform_evidence($userid, $evidence, $competency, $helper); } return $carry; }, function($competencyid, $data) use ($makecomppath, $context) { writer::with_context($context)->export_data($makecomppath($competencyid, $data), (object) $data); }); } /** * Export a user's learning plans. * * @param context_user $context The context of the user requesting the export. * @return void */ protected static function export_user_data_learning_plans(context_user $context) { global $DB; $userid = $context->instanceid; $path = [get_string('competencies', 'core_competency'), get_string('privacy:path:plans', 'core_competency')]; $helper = new performance_helper(); $pfields = plan::get_sql_fields('p', 'p_'); $pcfields = plan_competency::get_sql_fields('pc', 'pc_'); $cfields = competency::get_sql_fields('c', 'c_'); $ucfields = user_competency::get_sql_fields('uc', 'uc_'); $ucpfields = user_competency_plan::get_sql_fields('ucp', 'ucp_'); // The user's learning plans. $sql = " SELECT $pfields, $pcfields, $cfields, $ucfields, $ucpfields FROM {" . plan::TABLE . "} p LEFT JOIN {" . plan_competency::TABLE . "} pc ON p.id = pc.planid AND p.templateid IS NULL AND p.status != :complete1 LEFT JOIN {" . template_competency::TABLE . "} tc ON tc.templateid = p.templateid AND p.templateid IS NOT NULL AND p.status != :complete2 LEFT JOIN {" . user_competency_plan::TABLE . "} ucp ON ucp.planid = p.id AND p.status = :complete3 LEFT JOIN {" . competency::TABLE . "} c ON c.id = pc.competencyid OR c.id = tc.competencyid OR c.id = ucp.competencyid LEFT JOIN {" . user_competency::TABLE . "} uc ON uc.userid = p.userid AND (uc.competencyid = pc.competencyid OR uc.competencyid = tc.competencyid) WHERE p.userid = :userid ORDER BY p.id, c.id"; $params = [ 'userid' => $userid, 'complete1' => plan::STATUS_COMPLETE, 'complete2' => plan::STATUS_COMPLETE, 'complete3' => plan::STATUS_COMPLETE, ]; $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'p_id', null, function($carry, $record) use ($userid, $helper, $context) { $iscomplete = $record->p_status == plan::STATUS_COMPLETE; if ($carry === null) { $plan = new plan(null, plan::extract_record($record, 'p_')); $options = ['context' => $context]; $carry = [ 'name' => format_string($plan->get('name'), true, $options), 'description' => format_text($plan->get('description'), $plan->get('descriptionformat'), $options), 'status' => $plan->get_statusname(), 'duedate' => $plan->get('duedate') ? transform::datetime($plan->get('duedate')) : '-', 'reviewerid' => $plan->get('reviewerid') ? transform::user($plan->get('reviewerid')) : '-', 'timecreated' => transform::datetime($plan->get('timecreated')), 'timemodified' => transform::datetime($plan->get('timemodified')), 'competencies' => [], ]; } // The plan is empty. if (empty($record->c_id)) { return $carry; } $competency = new competency(null, competency::extract_record($record, 'c_')); $rating = null; if ($iscomplete) { // When the plan is complete, we should always found the user_competency_plan. $ucp = new user_competency_plan(null, user_competency_plan::extract_record($record, 'ucp_')); $rating = static::transform_user_competency($userid, $ucp, $competency, $helper); } else if (!empty($record->uc_id)) { // When the plan is complete, there are still records of user_competency but we do not // export them here, we export them as part of the competencies structure. The reason why // we try to get the user_competency when the plan is not complete is to give the most accurate // representation of the plan as possible. $uc = new user_competency(null, user_competency::extract_record($record, 'uc_')); $rating = static::transform_user_competency($userid, $uc, $competency, $helper); } $carry['competencies'][] = array_merge(static::transform_competency_brief($competency), ['rating' => $rating]); return $carry; }, function($planid, $data) use ($context, $path) { $planpath = array_merge($path, [$data['name'] . ' (' . $planid . ')']); \core_comment\privacy\provider::export_comments($context, 'competency', 'plan', $planid, $planpath, false); writer::with_context($context)->export_data($planpath, (object) $data); }); } /** * Export a user's data related to learning plans. * * @param int $userid The user ID we're exporting for. * @param context_user $context The context of the user in which we're gathering data. * @return void */ protected static function export_user_data_learning_plans_related_to_me($userid, context_user $context) { global $DB; $path = [ get_string('competencies', 'core_competency'), get_string('privacy:path:relatedtome', 'core_competency'), get_string('privacy:path:plans', 'core_competency'), ]; $plans = []; $helper = new performance_helper(); $pfields = plan::get_sql_fields('p', 'p_'); $pcfields = plan_competency::get_sql_fields('pc', 'pc_'); $cfields = competency::get_sql_fields('c', 'c_'); $ucpfields = user_competency_plan::get_sql_fields('ucp', 'ucp_'); // Function to initialise a plan record. $initplan = function($record) use ($context, $userid, &$plans) { $plan = new plan(null, plan::extract_record($record, 'p_')); $options = ['context' => $context]; $plans[$plan->get('id')] = [ 'name' => format_string($plan->get('name'), true, $options), 'reviewer_is_you' => transform::yesno($plan->get('reviewerid') == $userid), 'timecreated' => transform::datetime($plan->get('timecreated')), 'timemodified' => transform::datetime($plan->get('timemodified')), 'created_or_modified_by_you' => transform::yesno($plan->get('usermodified') == $userid), 'competencies' => [], ]; }; $initcompetency = function($record, $planid) use (&$plans) { $competency = new competency(null, competency::extract_record($record, 'c_')); $plans[$planid]['competencies'][$competency->get('id')] = static::transform_competency_brief($competency); }; // Look for associations that were created. $sql = " SELECT $pfields, $pcfields, $cfields FROM {" . plan_competency::TABLE . "} pc JOIN {" . plan::TABLE . "} p ON p.id = pc.planid JOIN {" . competency::TABLE . "} c ON c.id = pc.competencyid WHERE p.userid = :targetuserid AND pc.usermodified = :userid ORDER BY p.id, c.id"; $params = [ 'targetuserid' => $context->instanceid, 'userid' => $userid, ]; $recordset = $DB->get_recordset_sql($sql, $params); foreach ($recordset as $record) { $planid = $record->p_id; if (!isset($plans[$planid])) { $initplan($record); } $initcompetency($record, $planid); $pc = new plan_competency(null, plan_competency::extract_record($record, 'pc_')); $plans[$planid]['competencies'][$pc->get('competencyid')] = array_merge( $plans[$planid]['competencies'][$pc->get('competencyid')], [ 'timemodified' => $pc->get('timemodified') ? transform::datetime($pc->get('timemodified')) : '-', 'timecreated' => $pc->get('timecreated') ? transform::datetime($pc->get('timecreated')) : '-', 'created_or_modified_by_you' => transform::yesno($pc->get('usermodified') == $userid), ] ); } $recordset->close(); // Look for final grades that were given. $sql = " SELECT $pfields, $ucpfields, $cfields FROM {" . user_competency_plan::TABLE . "} ucp JOIN {" . plan::TABLE . "} p ON p.id = ucp.planid JOIN {" . competency::TABLE . "} c ON c.id = ucp.competencyid WHERE p.userid = :targetuserid AND ucp.usermodified = :userid ORDER BY p.id, c.id"; $params = [ 'targetuserid' => $context->instanceid, 'userid' => $userid, ]; $recordset = $DB->get_recordset_sql($sql, $params); foreach ($recordset as $record) { $planid = $record->p_id; $competencyid = $record->c_id; if (!isset($plans[$planid])) { $initplan($record); } if (!isset($plans[$planid]['competencies'][$competencyid])) { $initcompetency($record, $planid); } $competency = new competency(null, competency::extract_record($record, 'c_')); $ucp = new user_competency_plan(null, user_competency_plan::extract_record($record, 'ucp_')); $plans[$planid]['competencies'][$competencyid]['rating'] = static::transform_user_competency($userid, $ucp, $competency, $helper); } $recordset->close(); // Find the plans that were modified or reviewed. $insql = " > 0"; $inparams = []; if (!empty($plans)) { list($insql, $inparams) = $DB->get_in_or_equal(array_keys($plans), SQL_PARAMS_NAMED, 'param', false); } $sql = " SELECT $pfields FROM {" . plan::TABLE . "} p LEFT JOIN {comments} c ON c.contextid = :contextid AND c.commentarea = :planarea AND c.component = :competency AND c.itemid = p.id WHERE p.userid = :targetuserid AND (p.usermodified = :userid1 OR p.reviewerid = :userid2 OR c.userid = :userid3) AND p.id $insql ORDER BY p.id"; $params = array_merge($inparams, [ 'targetuserid' => $context->instanceid, 'userid1' => $userid, 'userid2' => $userid, 'userid3' => $userid, 'contextid' => $context->id, 'planarea' => 'plan', 'competency' => 'competency' ]); $recordset = $DB->get_recordset_sql($sql, $params); foreach ($recordset as $record) { $planid = $record->p_id; if (!isset($plans[$planid])) { $initplan($record); } } $recordset->close(); // Export each plan on its own. foreach ($plans as $planid => $plan) { $planpath = array_merge($path, ["{$plan['name']} ({$planid})"]); $plan['competencies'] = array_values($plan['competencies']); // Drop the keys. writer::with_context($context)->export_data($planpath, (object) $plan); \core_comment\privacy\provider::export_comments($context, 'competency', 'plan', $planid, $planpath, true); } } /** * Export a user's data related to competencies. * * @param int $userid The user ID we're exporting for. * @param context_user $context The context of the user in which we're gathering data. * @return void */ protected static function export_user_data_competencies_related_to_me($userid, context_user $context) { global $DB; $path = [ get_string('competencies', 'core_competency'), get_string('privacy:path:relatedtome', 'core_competency'), get_string('competencies', 'core_competency'), ]; $competencies = []; $helper = new performance_helper(); $cfields = competency::get_sql_fields('c', 'c_'); $ucfields = user_competency::get_sql_fields('uc', 'uc_'); $efields = evidence::get_sql_fields('e', 'e_'); $initcompetency = function($record) use (&$competencies) { $competency = new competency(null, competency::extract_record($record, 'c_')); $competencies[$competency->get('id')] = array_merge(static::transform_competency_brief($competency), [ 'evidence' => [] ]); }; $initusercomp = function($competency, $record) use (&$competencies, $userid, $helper) { $competencyid = $competency->get('id'); $uc = new user_competency(null, user_competency::extract_record($record, 'uc_')); $competencies[$competencyid]['uc_id'] = $uc->get('id'); $competencies[$competencyid]['rating'] = static::transform_user_competency($userid, $uc, $competency, $helper); }; // Look for evidence. $sql = " SELECT $efields, $ucfields, $cfields FROM {" . evidence::TABLE . "} e JOIN {" . user_competency::TABLE . "} uc ON uc.id = e.usercompetencyid JOIN {" . competency::TABLE . "} c ON c.id = uc.competencyid WHERE uc.userid = :targetuserid AND (e.usermodified = :userid1 OR e.actionuserid = :userid2) ORDER BY c.id, e.id"; $params = [ 'targetuserid' => $context->instanceid, 'userid1' => $userid, 'userid2' => $userid, ]; $recordset = $DB->get_recordset_sql($sql, $params); foreach ($recordset as $record) { $competencyid = $record->c_id; $competency = new competency(null, competency::extract_record($record, 'c_')); if (!isset($competencies[$competencyid])) { $initcompetency($record); } if (!array_key_exists('rating', $competencies[$competencyid])) { $competencies[$competencyid]['rating'] = null; if ($record->uc_reviewerid == $userid || $record->uc_usermodified == $userid) { $initusercomp($competency, $record); } } $evidence = new evidence(null, evidence::extract_record($record, 'e_')); $competencies[$competencyid]['evidence'][] = static::transform_evidence($userid, $evidence, $competency, $helper); } $recordset->close(); // Look for user competency we modified and didn't catch. $insql = ' > 0'; $inparams = []; if (!empty($competencies)) { list($insql, $inparams) = $DB->get_in_or_equal(array_keys($competencies), SQL_PARAMS_NAMED, 'param', false); } $sql = " SELECT $ucfields, $cfields FROM {" . user_competency::TABLE . "} uc JOIN {" . competency::TABLE . "} c ON c.id = uc.competencyid LEFT JOIN {comments} cmt ON cmt.contextid = :contextid AND cmt.commentarea = :ucarea AND cmt.component = :competency AND cmt.itemid = uc.id WHERE uc.userid = :targetuserid AND (uc.usermodified = :userid1 OR uc.reviewerid = :userid2 OR cmt.userid = :userid3) AND uc.competencyid $insql ORDER BY c.id, uc.id"; $params = array_merge($inparams, [ 'targetuserid' => $context->instanceid, 'userid1' => $userid, 'userid2' => $userid, 'userid3' => $userid, 'contextid' => $context->id, 'ucarea' => 'user_competency', 'competency' => 'competency', ]); $recordset = $DB->get_recordset_sql($sql, $params); foreach ($recordset as $record) { $competency = new competency(null, competency::extract_record($record, 'c_')); if (!isset($competencies[$competency->get('id')])) { $initcompetency($record); $initusercomp($competency, $record); } } $recordset->close(); // Export each competency on its own. foreach ($competencies as $competencyid => $competency) { $comppath = array_merge($path, ["{$competency['name']} ({$competencyid})"]); $ucid = isset($competency['uc_id']) ? $competency['uc_id'] : null; unset($competency['uc_id']); // Send to writer. writer::with_context($context)->export_data($comppath, (object) $competency); if ($ucid) { \core_comment\privacy\provider::export_comments($context, 'competency', 'user_competency', $ucid, $comppath, true); } } } /** * Export a user's data related to evidence of prior learning. * * @param int $userid The user ID we're exporting for. * @param context_user $context The context of the user in which we're gathering data. * @return void */ protected static function export_user_data_user_evidence_related_to_me($userid, context_user $context) { global $DB; $path = [ get_string('competencies', 'core_competency'), get_string('privacy:path:relatedtome', 'core_competency'), get_string('privacy:path:userevidence', 'core_competency'), ]; $evidence = []; $helper = new performance_helper(); $cfields = competency::get_sql_fields('c', 'c_'); $uecfields = user_evidence_competency::get_sql_fields('uec', 'uec_'); $uefields = user_evidence::get_sql_fields('ue', 'ue_'); $initevidence = function($record) use (&$evidence, $userid) { $ue = new user_evidence(null, user_evidence::extract_record($record, 'ue_')); $evidence[$ue->get('id')] = static::transform_user_evidence($userid, $ue); }; // Look for evidence. $sql = " SELECT $uefields, $uecfields, $cfields FROM {" . user_evidence_competency::TABLE . "} uec JOIN {" . user_evidence::TABLE . "} ue ON ue.id = uec.userevidenceid JOIN {" . competency::TABLE . "} c ON c.id = uec.competencyid WHERE ue.userid = :targetuserid AND uec.usermodified = :userid ORDER BY ue.id, c.id"; $params = [ 'targetuserid' => $context->instanceid, 'userid' => $userid, ]; $recordset = $DB->get_recordset_sql($sql, $params); foreach ($recordset as $record) { $ueid = $record->ue_id; if (!isset($evidence[$ueid])) { $initevidence($record); } $competency = new competency(null, competency::extract_record($record, 'c_')); $uec = new user_evidence_competency(null, user_evidence_competency::extract_record($record, 'uec_')); $evidence[$ueid]['competencies'][] = array_merge(static::transform_competency_brief($competency), [ 'timemodified' => $uec->get('timemodified') ? transform::datetime($uec->get('timemodified')) : '-', 'timecreated' => $uec->get('timecreated') ? transform::datetime($uec->get('timecreated')) : '-', 'created_or_modified_by_you' => transform::yesno($uec->get('usermodified')) ]); } $recordset->close(); // Look for user evidence we modified or reviewed and didn't catch. $insql = ' > 0'; $inparams = []; if (!empty($evidence)) { list($insql, $inparams) = $DB->get_in_or_equal(array_keys($evidence), SQL_PARAMS_NAMED, 'param', false); } $sql = " SELECT $uefields FROM {" . user_evidence::TABLE . "} ue WHERE ue.userid = :targetuserid AND ue.usermodified = :userid AND ue.id $insql ORDER BY ue.id"; $params = array_merge($inparams, [ 'targetuserid' => $context->instanceid, 'userid' => $userid, ]); $recordset = $DB->get_recordset_sql($sql, $params); foreach ($recordset as $record) { $initevidence($record); } $recordset->close(); // Export files, then content. foreach ($evidence as $ueid => $data) { $uepath = array_merge($path, ["{$data['name']} ({$ueid})"]); writer::with_context($context)->export_area_files($uepath, 'core_competency', 'userevidence', $ueid); writer::with_context($context)->export_data($uepath, (object) $data); } } /** * Export the evidence of prior learning of a user. * * @param context_user $context The context of the user we're exporting for. * @return void */ protected static function export_user_data_user_evidence(context_user $context) { global $DB; $userid = $context->instanceid; $path = [get_string('competencies', 'core_competency'), get_string('privacy:path:userevidence', 'core_competency')]; $uefields = user_evidence::get_sql_fields('ue', 'ue_'); $cfields = competency::get_sql_fields('c', 'c_'); $sql = " SELECT $uefields, $cfields FROM {" . user_evidence::TABLE . "} ue LEFT JOIN {" . user_evidence_competency::TABLE . "} uec ON uec.userevidenceid = ue.id LEFT JOIN {" . competency::TABLE . "} c ON c.id = uec.competencyid WHERE ue.userid = :userid ORDER BY ue.id"; $params = ['userid' => $userid]; $recordset = $DB->get_recordset_sql($sql, $params); static::recordset_loop_and_export($recordset, 'ue_id', null, function($carry, $record) use ($userid, $context){ if ($carry === null) { $ue = new user_evidence(null, user_evidence::extract_record($record, 'ue_')); $carry = static::transform_user_evidence($userid, $ue); } if (!empty($record->c_id)) { $competency = new competency(null, competency::extract_record($record, 'c_')); $carry['competencies'][] = static::transform_competency_brief($competency); } return $carry; }, function($ueid, $data) use ($context, $path) { $finalpath = array_merge($path, [$data['name'] . ' (' . $ueid . ')']); writer::with_context($context)->export_area_files($finalpath, 'core_competency', 'userevidence', $ueid); writer::with_context($context)->export_data($finalpath, (object) $data); }); } /** * Export the user data related to frameworks in context. * * @param int $userid The user ID. * @param context $context The context. * @return void */ protected static function export_user_data_frameworks_in_context($userid, context $context) { global $DB; $ffields = competency_framework::get_sql_fields('f', 'f_'); $cfields = competency::get_sql_fields('c', 'c_'); $c2fields = competency::get_sql_fields('c2', 'c2_'); $rcfields = related_competency::get_sql_fields('rc', 'rc_'); $frameworks = []; $initframework = function($record) use (&$frameworks, $userid) { $framework = new competency_framework(null, competency_framework::extract_record($record, 'f_')); $frameworks[$framework->get('id')] = array_merge(static::transform_framework_brief($framework), [ 'timemodified' => transform::datetime($framework->get('timemodified')), 'created_or_modified_by_you' => transform::yesno($framework->get('usermodified') == $userid), 'competencies' => [] ]); }; $initcompetency = function($record, $prefix) use (&$frameworks, $userid) { $competency = new competency(null, competency::extract_record($record, $prefix)); $frameworks[$competency->get('competencyframeworkid')]['competencies'][$competency->get('id')] = array_merge( static::transform_competency_brief($competency), [ 'timemodified' => transform::datetime($competency->get('timemodified')), 'created_or_modified_by_you' => transform::yesno($competency->get('usermodified') == $userid), 'related_competencies' => [] ] ); }; // Start by looking for related competencies. $sql = " SELECT $ffields, $cfields, $c2fields, $rcfields FROM {" . related_competency::TABLE . "} rc JOIN {" . competency::TABLE . "} c ON c.id = rc.competencyid JOIN {" . competency::TABLE . "} c2 ON c2.id = rc.relatedcompetencyid JOIN {" . competency_framework::TABLE . "} f ON f.id = c.competencyframeworkid WHERE rc.usermodified = :userid AND f.contextid = :contextid ORDER BY rc.id, c.id"; $params = ['userid' => $userid, 'contextid' => $context->id]; $recordset = $DB->get_recordset_sql($sql, $params); foreach ($recordset as $record) { $frameworkid = $record->f_id; $comp1id = $record->c_id; $comp2id = $record->c2_id; if (!isset($frameworks[$frameworkid])) { $initframework($record); } foreach (['c_', 'c2_'] as $key) { $competencyid = $record->{$key . 'id'}; if (!isset($frameworks[$frameworkid]['competencies'][$competencyid])) { $initcompetency($record, $key); } } $relcomp = new related_competency(null, related_competency::extract_record($record, 'rc_')); foreach (['c_' => 'c2_', 'c2_' => 'c_'] as $key => $relatedkey) { $competencyid = $record->{$key . 'id'}; $competency = new competency(null, competency::extract_record($record, $relatedkey)); $frameworks[$frameworkid]['competencies'][$competencyid]['related_competencies'][] = [ 'name' => $competency->get('shortname'), 'idnumber' => $competency->get('idnumber'), 'timemodified' => transform::datetime($relcomp->get('timemodified')), 'created_or_modified_by_you' => transform::yesno($relcomp->get('usermodified') == $userid), ]; } } $recordset->close(); // Now look for competencies, but skip the ones we've already seen. $competencyids = array_reduce($frameworks, function($carry, $framework) { return array_merge($carry, array_keys($framework['competencies'])); }, []); $insql = ' IS NOT NULL'; $inparams = []; if (!empty($competencyids)) { list($insql, $inparams) = $DB->get_in_or_equal($competencyids, SQL_PARAMS_NAMED, 'param', false); } $sql = " SELECT $ffields, $cfields FROM {" . competency::TABLE . "} c JOIN {" . competency_framework::TABLE . "} f ON f.id = c.competencyframeworkid WHERE c.usermodified = :userid AND f.contextid = :contextid AND c.id $insql ORDER BY c.id"; $params = array_merge($inparams, ['userid' => $userid, 'contextid' => $context->id]); $recordset = $DB->get_recordset_sql($sql, $params); foreach ($recordset as $record) { $frameworkid = $record->f_id; if (!isset($frameworks[$frameworkid])) { $initframework($record); } $initcompetency($record, 'c_'); } $recordset->close(); // Now look for frameworks, but skip the ones we've already seen. $frameworkids = array_keys($frameworks); $insql = ' IS NOT NULL'; $inparams = []; if (!empty($frameworkids)) { list($insql, $inparams) = $DB->get_in_or_equal($frameworkids, SQL_PARAMS_NAMED, 'param', false); } $sql = " SELECT $ffields FROM {" . competency_framework::TABLE . "} f WHERE f.usermodified = :userid AND f.contextid = :contextid AND f.id $insql ORDER BY f.id"; $params = array_merge($inparams, ['userid' => $userid, 'contextid' => $context->id]); $recordset = $DB->get_recordset_sql($sql, $params); foreach ($recordset as $record) { context_helper::preload_from_record($record); $initframework($record); } $recordset->close(); // Export all the things! writer::with_context($context)->export_related_data( [get_string('competencies', 'core_competency')], 'frameworks', (object) [ // Drop the temporary IDs. 'frameworks' => array_reduce($frameworks, function($carry, $item) { $item['competencies'] = array_values($item['competencies']); $carry[] = $item; return $carry; }, []) ] ); } /** * Export the user data related to templates in contexts. * * @param int $userid The user ID. * @param context $context The context. * @return void */ protected static function export_user_data_templates_in_context($userid, context $context) { global $DB; $tfields = template::get_sql_fields('t', 't_'); $cfields = competency::get_sql_fields('c', 'c_'); $tcfields = template_competency::get_sql_fields('tc', 'tc_'); $tchfields = template_cohort::get_sql_fields('tch', 'tch_'); $templates = []; $inittemplate = function($record) use (&$templates, $userid) { $template = new template(null, template::extract_record($record, 't_')); $templates[$template->get('id')] = array_merge(static::transform_template_brief($template), [ 'timemodified' => transform::datetime($template->get('timemodified')), 'created_or_modified_by_you' => transform::yesno($template->get('usermodified') == $userid), 'competencies' => [], 'cohorts' => [] ]); }; // Find the template competencies. $sql = " SELECT $tfields, $cfields, $tcfields FROM {" . template_competency::TABLE . "} tc JOIN {" . template::TABLE . "} t ON t.id = tc.templateid JOIN {" . competency::TABLE . "} c ON c.id = tc.competencyid WHERE t.contextid = :contextid AND tc.usermodified = :userid ORDER BY t.id, tc.id"; $params = ['userid' => $userid, 'contextid' => $context->id]; $recordset = $DB->get_recordset_sql($sql, $params); foreach ($recordset as $record) { $templateid = $record->t_id; if (!isset($templates[$templateid])) { $inittemplate($record); } $tplcomp = new template_competency(null, template_competency::extract_record($record, 'tc_')); $competency = new competency(null, competency::extract_record($record, 'c_')); $templates[$templateid]['competencies'][] = array_merge( static::transform_competency_brief($competency), [ 'timemodified' => transform::datetime($tplcomp->get('timemodified')), 'created_or_modified_by_you' => transform::yesno($tplcomp->get('usermodified') == $userid) ] ); } $recordset->close(); // Find the template cohorts. $sql = " SELECT $tfields, $tchfields, c.name AS cohortname FROM {" . template_cohort::TABLE . "} tch JOIN {" . template::TABLE . "} t ON t.id = tch.templateid JOIN {cohort} c ON c.id = tch.cohortid WHERE t.contextid = :contextid AND tch.usermodified = :userid ORDER BY t.id, tch.id"; $params = ['userid' => $userid, 'contextid' => $context->id]; $recordset = $DB->get_recordset_sql($sql, $params); foreach ($recordset as $record) { $templateid = $record->t_id; if (!isset($templates[$templateid])) { $inittemplate($record); } $tplcohort = new template_cohort(null, template_cohort::extract_record($record, 'tch_')); $templates[$templateid]['cohorts'][] = [ 'name' => $record->cohortname, 'timemodified' => transform::datetime($tplcohort->get('timemodified')), 'created_or_modified_by_you' => transform::yesno($tplcohort->get('usermodified') == $userid) ]; } $recordset->close(); // Find the modified templates which we haven't been found yet. $templateids = array_keys($templates); $insql = "IS NOT NULL"; $inparams = []; if (!empty($templateids)) { list($insql, $inparams) = $DB->get_in_or_equal($templateids, SQL_PARAMS_NAMED, 'param', false); } $sql = " SELECT $tfields FROM {" . template::TABLE . "} t WHERE t.contextid = :contextid AND t.usermodified = :userid AND t.id $insql ORDER BY t.id"; $params = array_merge($inparams, ['userid' => $userid, 'contextid' => $context->id]); $recordset = $DB->get_recordset_sql($sql, $params); foreach ($recordset as $record) { $inittemplate($record); } $recordset->close(); // Export all the things! writer::with_context($context)->export_related_data([get_string('competencies', 'core_competency')], 'templates', (object) ['templates' => array_values($templates)]); } /** * Transform a competency into a brief description. * * @param competency $competency The competency. * @return array */ protected static function transform_competency_brief(competency $competency) { global $OUTPUT; $exporter = new \core_competency\external\competency_exporter($competency, ['context' => $competency->get_context()]); $data = $exporter->export($OUTPUT); return [ 'idnumber' => $data->idnumber, 'name' => $data->shortname, 'description' => $data->description ]; } /** * Transform a competency rating. * * @param competency $competency The competency. * @param int $grade The grade. * @param performance_helper $helper The performance helper. * @return string */ protected static function transform_competency_grade(competency $competency, $grade, performance_helper $helper) { if ($grade === null) { return '-'; } $scale = $helper->get_scale_from_competency($competency); return $scale->scale_items[$grade - 1]; } /** * Transform an evidence. * * @param int $userid The user ID we are exporting for. * @param evidence $evidence The evidence. * @param competency $competency The associated competency. * @param performance_helper $helper The performance helper. * @return array */ protected static function transform_evidence($userid, evidence $evidence, competency $competency, performance_helper $helper) { $action = $evidence->get('action'); $actiontxt = '?'; if ($action == evidence::ACTION_LOG) { $actiontxt = get_string('privacy:evidence:action:log', 'core_competency'); } else if ($action == evidence::ACTION_COMPLETE) { $actiontxt = get_string('privacy:evidence:action:complete', 'core_competency'); } else if ($action == evidence::ACTION_OVERRIDE) { $actiontxt = get_string('privacy:evidence:action:override', 'core_competency'); } $actionuserid = $evidence->get('actionuserid'); return [ 'action' => $actiontxt, 'actionuserid' => $actionuserid ? transform::user($actionuserid) : '-', 'acting_user_is_you' => transform::yesno($userid == $actionuserid), 'description' => (string) $evidence->get_description(), 'url' => $evidence->get('url'), 'grade' => static::transform_competency_grade($competency, $evidence->get('grade'), $helper), 'note' => $evidence->get('note'), 'timecreated' => transform::datetime($evidence->get('timecreated')), 'timemodified' => transform::datetime($evidence->get('timemodified')), 'created_or_modified_by_you' => transform::yesno($userid == $evidence->get('usermodified')) ]; } /** * Transform a framework into a brief description. * * @param competency_framework $framework The framework. * @return array */ protected static function transform_framework_brief(competency_framework $framework) { global $OUTPUT; $exporter = new \core_competency\external\competency_framework_exporter($framework); $data = $exporter->export($OUTPUT); return [ 'name' => $data->shortname, 'idnumber' => $data->idnumber, 'description' => $data->description ]; } /** * Transform a template into a brief description. * * @param template $template The Template. * @return array */ protected static function transform_template_brief(template $template) { global $OUTPUT; $exporter = new \core_competency\external\template_exporter($template); $data = $exporter->export($OUTPUT); return [ 'name' => $data->shortname, 'description' => $data->description ]; } /** * Transform proficiency. * * @param null|bool $proficiency The proficiency. * @return string */ protected static function transform_proficiency($proficiency) { return $proficiency !== null ? transform::yesno($proficiency) : '-'; } /** * Transform user competency. * * @param int $userid The user ID we are exporting for. * @param user_competency|user_competency_plan|user_competency_course $uc The user competency. * @param competency $competency The associated competency. * @param performance_helper $helper The performance helper. * @return array */ protected static function transform_user_competency($userid, $uc, competency $competency, performance_helper $helper) { $data = [ 'proficient' => static::transform_proficiency($uc->get('proficiency')), 'rating' => static::transform_competency_grade($competency, $uc->get('grade'), $helper), 'timemodified' => $uc->get('timemodified') ? transform::datetime($uc->get('timemodified')) : '-', 'timecreated' => $uc->get('timecreated') ? transform::datetime($uc->get('timecreated')) : '-', 'created_or_modified_by_you' => transform::yesno($uc->get('usermodified') == $userid), ]; if ($uc instanceof user_competency) { $reviewer = $uc->get('reviewerid'); $data['status'] = (string) user_competency::get_status_name($uc->get('status')); $data['reviewerid'] = $reviewer ? transform::user($reviewer) : '-'; $data['reviewer_is_you'] = transform::yesno($reviewer == $userid); } return $data; } /** * Transform a user evidence. * * @param int $userid The user we are exporting for. * @param user_evidence $ue The evidence of prior learning. * @return array */ protected static function transform_user_evidence($userid, user_evidence $ue) { $options = ['context' => $ue->get_context()]; return [ 'name' => format_string($ue->get('name'), true, $options), 'description' => format_text($ue->get('description'), $ue->get('descriptionformat'), $options), 'url' => $ue->get('url'), 'timecreated' => $ue->get('timecreated') ? transform::datetime($ue->get('timecreated')) : '-', 'timemodified' => $ue->get('timemodified') ? transform::datetime($ue->get('timemodified')) : '-', 'created_or_modified_by_you' => transform::yesno($ue->get('usermodified') == $userid), 'competencies' => [] ]; } /** * Loop and export from a recordset. * * @param moodle_recordset $recordset The recordset. * @param string $splitkey The record key to determine when to export. * @param mixed $initial The initial data to reduce from. * @param callable $reducer The function to return the dataset, receives current dataset, and the current record. * @param callable $export The function to export the dataset, receives the last value from $splitkey and the dataset. * @return void */ protected static function recordset_loop_and_export(moodle_recordset $recordset, $splitkey, $initial, callable $reducer, callable $export) { $data = $initial; $lastid = null; foreach ($recordset as $record) { if ($lastid && $record->{$splitkey} != $lastid) { $export($lastid, $data); $data = $initial; } $data = $reducer($data, $record); $lastid = $record->{$splitkey}; } $recordset->close(); if (!empty($lastid)) { $export($lastid, $data); } } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/cache/classes/privacy/provider.php000064400000004553152266625100025415 0ustar00. /** * Privacy Subsystem implementation for core_cache. * * @package core_cache * @copyright 2018 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core_cache\privacy; use core_privacy\local\metadata\collection; /** * Privacy Subsystem implementation for core_cache. * * @copyright 2018 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements // Caches store data. \core_privacy\local\metadata\provider, // The cache subsystem stores data on behalf of other components. \core_privacy\local\request\subsystem\plugin_provider, \core_privacy\local\request\shared_userlist_provider { /** * Returns meta data about this system. * * Note, although this plugin does store user data, it is not able to * identify it, and that user data is typically very short lived. * * Therefore it is not realistically possible to export any of this * data as it is only identifiable by the plugin storing it, and that * plugin should already be exporting the data as part of it's own * implementation. * * @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 { // Data is stored in cache stores. $collection->add_plugintype_link('cachestore', [], 'privacy:metadata:cachestore'); // Cache locks do not store any personal user data. return $collection; } } smkb8269/public_html/lms.smk-almanshuriyah.sch.id/blocks/tag_flickr/classes/privacy/provider.php000064400000007713152266632060027661 0ustar00home. /** * Privacy Subsystem implementation for block_tag_flickr. * * @package block_tag_flickr * @copyright 2018 Zig Tan * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace block_tag_flickr\privacy; use core_privacy\local\metadata\collection; use core_privacy\local\request\approved_contextlist; use core_privacy\local\request\context; use core_privacy\local\request\contextlist; use core_privacy\local\request\userlist; use core_privacy\local\request\approved_userlist; defined('MOODLE_INTERNAL') || die(); /** * Privacy Subsystem for block_tag_flickr implementing metadata and plugin provider. * * @copyright 2018 Zig Tan * @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\plugin\provider { /** * Returns meta data about this system. * * @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_external_location_link( 'flickr.com', [ 'tags' => 'privacy:metadata:block_tag_flickr:tags' ], 'privacy:metadata:block_tag_flickr' ); 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 { return new contextlist(); } /** * Get the list of users who have data within a 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) { } /** * 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) { } /** * Delete all data for all users in the specified context. * * @param \context $context The specific context to delete data for. */ public static function delete_data_for_all_users_in_context(\context $context) { } /** * 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) { } /** * 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) { } } smkb8269/public_html/lms.smk-almanshuriyah.sch.id/repository/wikimedia/classes/privacy/provider.php000064400000012744152266652020030457 0ustar00home. /** * Privacy Subsystem implementation for repository_wikimedia. * * @package repository_wikimedia * @copyright 2018 Zig Tan * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace repository_wikimedia\privacy; use core_privacy\local\metadata\collection; use core_privacy\local\request\approved_contextlist; use core_privacy\local\request\approved_userlist; use core_privacy\local\request\context; use core_privacy\local\request\contextlist; use core_privacy\local\request\userlist; use core_privacy\local\request\writer; defined('MOODLE_INTERNAL') || die(); /** * Privacy Subsystem for repository_wikimedia implementing metadata and plugin providers. * * @copyright 2018 Zig Tan * @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\plugin\provider, \core_privacy\local\request\user_preference_provider { /** * Returns meta data about this system. * * @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_external_location_link( 'wikimedia.org', [ 'search_text' => 'privacy:metadata:repository_wikimedia:search_text' ], 'privacy:metadata:repository_wikimedia' ); $collection->add_user_preference( 'repository_wikimedia_maxwidth', 'privacy:metadata:repository_wikimedia:preference:maxwidth' ); $collection->add_user_preference( 'repository_wikimedia_maxheight', 'privacy:metadata:repository_wikimedia:preference:maxheight' ); 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 { return new contextlist(); } /** * Get the list of users who have data within a 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) { } /** * 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) { } /** * Delete all data for all users in the specified context. * * @param context $context The specific context to delete data for. */ public static function delete_data_for_all_users_in_context(\context $context) { } /** * 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) { } /** * 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) { } /** * Export all user preferences for the plugin. * * @param int $userid The userid of the user whose data is to be exported. */ public static function export_user_preferences(int $userid) { $maxwidth = get_user_preferences('repository_wikimedia_maxwidth', null, $userid); if ($maxwidth !== null) { writer::export_user_preference( 'repository_wikimedia', 'repository_wikimedia_maxwidth', $maxwidth, get_string('privacy:metadata:repository_wikimedia:preference:maxwidth', 'repository_wikimedia') ); } $maxheight = get_user_preferences('repository_wikimedia_maxheight', null, $userid); if ($maxheight !== null) { writer::export_user_preference( 'repository_wikimedia', 'repository_wikimedia_maxheight', $maxheight, get_string('privacy:metadata:repository_wikimedia:preference:maxheight', 'repository_wikimedia') ); } } } public_html/lms.smk-almanshuriyah.sch.id/repository/flickr_public/classes/privacy/provider.php000064400000010411152266660270031317 0ustar00home/smkb8269. /** * Privacy Subsystem implementation for repository_flickr_public. * * @package repository_flickr_public * @copyright 2018 Zig Tan * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace repository_flickr_public\privacy; use core_privacy\local\metadata\collection; use core_privacy\local\request\approved_contextlist; use core_privacy\local\request\approved_userlist; use core_privacy\local\request\context; use core_privacy\local\request\contextlist; use core_privacy\local\request\userlist; defined('MOODLE_INTERNAL') || die(); /** * Privacy Subsystem for repository_flickr_public implementing metadata and plugin providers. * * @copyright 2018 Zig Tan * @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\plugin\provider { /** * Returns meta data about this system. * * @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_external_location_link( 'flickr.com', [ 'author' => 'privacy:metadata:repository_flickr_public:author', 'email_address' => 'privacy:metadata:repository_flickr_public:email_address', 'text' => 'privacy:metadata:repository_flickr_public:text', 'user_id' => 'privacy:metadata:repository_flickr_public:user_id' ], 'privacy:metadata:repository_flickr_public' ); 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 { return new contextlist(); } /** * Get the list of users who have data within a 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) { } /** * 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) { } /** * Delete all data for all users in the specified context. * * @param context $context The specific context to delete data for. */ public static function delete_data_for_all_users_in_context(\context $context) { } /** * 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) { } /** * 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) { } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/calendar/classes/privacy/provider.php000064400000074633152266731720026137 0ustar00. /** * Privacy class for requesting user data. * * @package core_calendar * @copyright 2018 Zig Tan * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core_calendar\privacy; defined('MOODLE_INTERNAL') || die(); use \core_privacy\local\metadata\collection; use \core_privacy\local\request\approved_contextlist; use \core_privacy\local\request\context; use \core_privacy\local\request\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 Subsystem for core_calendar implementing metadata, plugin, and user_preference providers. * * @package core_calendar * @copyright 2018 Zig Tan * @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\plugin\provider, \core_privacy\local\request\core_userlist_provider, \core_privacy\local\request\user_preference_provider { /** * Provides meta data that is stored about a user with core_calendar. * * @param collection $collection A collection of meta data items to be added to. * @return collection Returns the collection of metadata. */ public static function get_metadata(collection $collection): collection { // The calendar 'event' table contains user data. $collection->add_database_table( 'event', [ 'name' => 'privacy:metadata:calendar:event:name', 'description' => 'privacy:metadata:calendar:event:description', 'eventtype' => 'privacy:metadata:calendar:event:eventtype', 'timestart' => 'privacy:metadata:calendar:event:timestart', 'timeduration' => 'privacy:metadata:calendar:event:timeduration', ], 'privacy:metadata:calendar:event' ); // The calendar 'event_subscriptions' table contains user data. $collection->add_database_table( 'event_subscriptions', [ 'name' => 'privacy:metadata:calendar:event_subscriptions:name', 'url' => 'privacy:metadata:calendar:event_subscriptions:url', 'eventtype' => 'privacy:metadata:calendar:event_subscriptions:eventtype', ], 'privacy:metadata:calendar:event_subscriptions' ); // The calendar user preference setting 'calendar_savedflt'. $collection->add_user_preference( 'calendar_savedflt', 'privacy:metadata:calendar:preferences:calendar_savedflt' ); return $collection; } /** * Get the list of contexts that contain calendar 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 { $contextlist = new contextlist(); // Calendar Events can exist at Site, Course Category, Course, Course Group, User, or Course Modules contexts. $params = [ 'sitecontext' => CONTEXT_SYSTEM, 'categorycontext' => CONTEXT_COURSECAT, 'coursecontext' => CONTEXT_COURSE, 'groupcontext' => CONTEXT_COURSE, 'usercontext' => CONTEXT_USER, 'cuserid' => $userid, 'modulecontext' => CONTEXT_MODULE, 'muserid' => $userid ]; // Get contexts of Calendar Events for the owner. $sql = "SELECT ctx.id FROM {context} ctx JOIN {event} e ON (e.eventtype = 'site' AND ctx.contextlevel = :sitecontext) OR (e.categoryid = ctx.instanceid AND e.eventtype = 'category' AND ctx.contextlevel = :categorycontext) OR (e.courseid = ctx.instanceid AND e.eventtype = 'course' AND ctx.contextlevel = :coursecontext) OR (e.courseid = ctx.instanceid AND e.eventtype = 'group' AND ctx.contextlevel = :groupcontext) OR (e.userid = ctx.instanceid AND e.eventtype = 'user' AND ctx.contextlevel = :usercontext) WHERE e.userid = :cuserid"; $contextlist->add_from_sql($sql, $params); $sql = "SELECT ctx.id FROM {context} ctx JOIN {course_modules} cm ON cm.id = ctx.instanceid AND ctx.contextlevel = :modulecontext JOIN {modules} m ON m.id = cm.module JOIN {event} e ON e.modulename = m.name AND e.courseid = cm.course AND e.instance = cm.instance WHERE e.userid = :muserid"; $contextlist->add_from_sql($sql, $params); // Calendar Subscriptions can exist at Site, Course Category, Course, Course Group, or User contexts. $params = [ 'sitecontext' => CONTEXT_SYSTEM, 'categorycontext' => CONTEXT_COURSECAT, 'coursecontext' => CONTEXT_COURSE, 'groupcontext' => CONTEXT_COURSE, 'usercontext' => CONTEXT_USER, 'userid' => $userid ]; // Get contexts for Calendar Subscriptions for the owner. $sql = "SELECT ctx.id FROM {context} ctx JOIN {event_subscriptions} s ON (s.eventtype = 'site' AND ctx.contextlevel = :sitecontext) OR (s.categoryid = ctx.instanceid AND s.eventtype = 'category' AND ctx.contextlevel = :categorycontext) OR (s.courseid = ctx.instanceid AND s.eventtype = 'course' AND ctx.contextlevel = :coursecontext) OR (s.courseid = ctx.instanceid AND s.eventtype = 'group' AND ctx.contextlevel = :groupcontext) OR (s.userid = ctx.instanceid AND s.eventtype = 'user' AND ctx.contextlevel = :usercontext) WHERE s.userid = :userid"; $contextlist->add_from_sql($sql, $params); // Return combined contextlist for Calendar Events & Calendar Subscriptions. 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) { global $DB; $context = $userlist->get_context(); // Calendar Events can exist at Site (CONTEXT_SYSTEM), Course Category (CONTEXT_COURSECAT), // Course and Course Group (CONTEXT_COURSE), User (CONTEXT_USER), or Course Modules (CONTEXT_MODULE) contexts. if ($context->contextlevel == CONTEXT_MODULE) { $params = ['cmid' => $context->instanceid]; $sql = "SELECT e.userid FROM {course_modules} cm JOIN {modules} m ON m.id = cm.module JOIN {event} e ON e.modulename = m.name AND e.courseid = cm.course AND e.instance = cm.instance WHERE cm.id = :cmid"; $userlist->add_from_sql('userid', $sql, $params); } else if ($context->contextlevel == CONTEXT_SYSTEM) { // Get contexts of Calendar Events for the owner. $sql = "SELECT userid FROM {event} WHERE eventtype = 'site'"; $userlist->add_from_sql('userid', $sql, []); // Get contexts for Calendar Subscriptions for the owner. $sql = "SELECT userid FROM {event_subscriptions} WHERE eventtype = 'site'"; $userlist->add_from_sql('userid', $sql, []); } else if (in_array($context->contextlevel, [CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_USER])) { $eventfields = [ CONTEXT_COURSECAT => 'categoryid', CONTEXT_COURSE => 'courseid', CONTEXT_USER => 'userid' ]; $eventfield = $eventfields[$context->contextlevel]; $eventtypes = [ CONTEXT_COURSECAT => 'category', CONTEXT_COURSE => ['course' , 'group'], CONTEXT_USER => 'user' ]; list($eventtypesql, $eventtypeparams) = $DB->get_in_or_equal($eventtypes[$context->contextlevel], SQL_PARAMS_NAMED); $params = $eventtypeparams + ['instanceid' => $context->instanceid]; // Get contexts of Calendar Events for the owner. $sql = "SELECT userid FROM {event} WHERE eventtype $eventtypesql AND $eventfield = :instanceid"; $userlist->add_from_sql('userid', $sql, $params); // Get contexts for Calendar Subscriptions for the owner. $sql = "SELECT userid FROM {event_subscriptions} WHERE eventtype $eventtypesql AND $eventfield = :instanceid"; $userlist->add_from_sql('userid', $sql, $params); } } /** * 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 (empty($contextlist)) { return; } self::export_user_calendar_event_data($contextlist); self::export_user_calendar_subscription_data($contextlist); } /** * Export all user preferences for the plugin. * * @param int $userid The userid of the user whose data is to be exported. */ public static function export_user_preferences(int $userid) { $calendarsavedflt = get_user_preferences('calendar_savedflt', null, $userid); if (null !== $calendarsavedflt) { writer::export_user_preference( 'core_calendar', 'calendarsavedflt', $calendarsavedflt, get_string('privacy:metadata:calendar:preferences:calendar_savedflt', 'core_calendar') ); } } /** * Delete all Calendar Event and Calendar Subscription data for all users in the specified context. * * @param \context $context Transform the specific context to delete data for. */ public static function delete_data_for_all_users_in_context(\context $context) { // Delete all Calendar Events in the specified context in batches. if ($eventids = array_keys(self::get_calendar_event_ids_by_context($context))) { self::delete_batch_records('event', 'id', $eventids); } // Delete all Calendar Subscriptions in the specified context in batches. if ($subscriptionids = array_keys(self::get_calendar_subscription_ids_by_context($context))) { self::delete_batch_records('event_subscriptions', 'id', $subscriptionids); } } /** * 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(); $userids = $userlist->get_userids(); $allowedcontexts = [ CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_MODULE, CONTEXT_USER ]; if (!in_array($context->contextlevel, $allowedcontexts)) { return; } if (empty($userids)) { return; } // Delete all Calendar Events in the specified context in batches. if ($eventids = array_keys(self::get_calendar_event_ids_by_context($context, $userids))) { self::delete_batch_records('event', 'id', $eventids); } // Delete all Calendar Subscriptions in the specified context in batches. if ($subscriptionids = array_keys(self::get_calendar_subscription_ids_by_context($context, $userids))) { self::delete_batch_records('event_subscriptions', 'id', $subscriptionids); } } /** * 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 (empty($contextlist)) { return; } // Delete all Calendar Events for the owner and specified contexts in batches. $eventdetails = self::get_calendar_event_details_by_contextlist($contextlist); $eventids = []; foreach ($eventdetails as $eventdetail) { $eventids[] = $eventdetail->eventid; } $eventdetails->close(); self::delete_batch_records('event', 'id', $eventids); // Delete all Calendar Subscriptions for the owner and specified contexts in batches. $subscriptiondetails = self::get_calendar_subscription_details_by_contextlist($contextlist); $subscriptionids = []; foreach ($subscriptiondetails as $subscriptiondetail) { $subscriptionids[] = $subscriptiondetail->subscriptionid; } $subscriptiondetails->close(); self::delete_batch_records('event_subscriptions', 'id', $subscriptionids); } /** * Helper function to export Calendar Events data by a User's contextlist. * * @param approved_contextlist $contextlist * @throws \coding_exception */ protected static function export_user_calendar_event_data(approved_contextlist $contextlist) { // Calendar Events can exist at Site, Course Category, Course, Course Group, User, or Course Modules contexts. $eventdetails = self::get_calendar_event_details_by_contextlist($contextlist); // Multiple Calendar Events of the same eventtype and time can exist for a context, so collate them for export. $eventrecords = []; foreach ($eventdetails as $eventdetail) { // Create an array key based on the contextid, eventtype, and time. $key = $eventdetail->contextid . $eventdetail->eventtype . $eventdetail->timestart; if (array_key_exists($key, $eventrecords) === false) { $eventrecords[$key] = [ $eventdetail ]; } else { $eventrecords[$key] = array_merge($eventrecords[$key], [$eventdetail]); } } $eventdetails->close(); // Export Calendar Event data. foreach ($eventrecords as $eventrecord) { $index = (count($eventrecord) > 1) ? 1 : 0; foreach ($eventrecord as $event) { // Export the events using the structure Calendar/Events/{datetime}/{eventtype}-event.json. $subcontexts = [ get_string('calendar', 'calendar'), get_string('events', 'calendar'), date('c', $event->timestart) ]; $name = $event->eventtype . '-event'; // Use name {eventtype}-event-{index}.json if multiple eventtypes and time exists at the same context. if ($index != 0) { $name .= '-' . $index; $index++; } $eventdetails = (object) [ 'name' => $event->name, 'description' => $event->description, 'location' => $event->location, 'eventtype' => $event->eventtype, 'timestart' => transform::datetime($event->timestart), 'timeduration' => $event->timeduration ]; $context = \context::instance_by_id($event->contextid); writer::with_context($context)->export_related_data($subcontexts, $name, $eventdetails); } } } /** * Helper function to export Calendar Subscriptions data by a User's contextlist. * * @param approved_contextlist $contextlist * @throws \coding_exception */ protected static function export_user_calendar_subscription_data(approved_contextlist $contextlist) { // Calendar Subscriptions can exist at Site, Course Category, Course, Course Group, or User contexts. $subscriptiondetails = self::get_calendar_subscription_details_by_contextlist($contextlist); // Multiple Calendar Subscriptions of the same eventtype can exist for a context, so collate them for export. $subscriptionrecords = []; foreach ($subscriptiondetails as $subscriptiondetail) { // Create an array key based on the contextid and eventtype. $key = $subscriptiondetail->contextid . $subscriptiondetail->eventtype; if (array_key_exists($key, $subscriptionrecords) === false) { $subscriptionrecords[$key] = [ $subscriptiondetail ]; } else { $subscriptionrecords[$key] = array_merge($subscriptionrecords[$key], [$subscriptiondetail]); } } $subscriptiondetails->close(); // Export Calendar Subscription data. foreach ($subscriptionrecords as $subscriptionrecord) { $index = (count($subscriptionrecord) > 1) ? 1 : 0; foreach ($subscriptionrecord as $subscription) { // Export the events using the structure Calendar/Subscriptions/{eventtype}-subscription.json. $subcontexts = [ get_string('calendar', 'calendar'), get_string('subscriptions', 'calendar') ]; $name = $subscription->eventtype . '-subscription'; // Use name {eventtype}-subscription-{index}.json if multiple eventtypes exists at the same context. if ($index != 0) { $name .= '-' . $index; $index++; } $context = \context::instance_by_id($subscription->contextid); writer::with_context($context)->export_related_data($subcontexts, $name, $subscription); } } } /** * Helper function to return all Calendar Event id results for a specified context and optionally * included user list. * * @param \context $context * @param array $userids * @return array|null * @throws \dml_exception */ protected static function get_calendar_event_ids_by_context(\context $context, $userids = array()) { global $DB; // Calendar Events can exist at Site (CONTEXT_SYSTEM), Course Category (CONTEXT_COURSECAT), // Course and Course Group (CONTEXT_COURSE), User (CONTEXT_USER), or Course Modules (CONTEXT_MODULE) contexts. if (!in_array($context->contextlevel, [CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_USER, CONTEXT_MODULE])) { return []; } $whereusersql = ''; $userparams = array(); if (!empty($userids)) { list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); $whereusersql = "AND e.userid {$usersql}"; } if ($context->contextlevel == CONTEXT_MODULE) { // Course Module events. $params = ['cmid' => $context->instanceid]; // Get Calendar Events for the specified Course Module context. $sql = "SELECT DISTINCT e.id AS eventid FROM {course_modules} cm JOIN {modules} m ON m.id = cm.module JOIN {event} e ON e.modulename = m.name AND e.courseid = cm.course AND e.instance = cm.instance WHERE cm.id = :cmid $whereusersql"; } else if ($context->contextlevel == CONTEXT_SYSTEM) { // Site events. $params = []; $sql = "SELECT DISTINCT e.id AS eventid FROM {event} e WHERE e.eventtype = 'site' $whereusersql"; } else { // The rest. $eventfields = [ CONTEXT_COURSECAT => 'categoryid', CONTEXT_COURSE => 'courseid', CONTEXT_USER => 'userid' ]; $eventfield = $eventfields[$context->contextlevel]; $eventtypes = [ CONTEXT_COURSECAT => 'category', CONTEXT_COURSE => ['course' , 'group'], CONTEXT_USER => 'user' ]; list($eventtypesql, $eventtypeparams) = $DB->get_in_or_equal($eventtypes[$context->contextlevel], SQL_PARAMS_NAMED); $params = $eventtypeparams + ['instanceid' => $context->instanceid]; // Get Calendar Events for the specified Moodle context. $sql = "SELECT DISTINCT e.id AS eventid FROM {event} e WHERE e.eventtype $eventtypesql AND e.{$eventfield} = :instanceid $whereusersql"; } $params += $userparams; return $DB->get_records_sql($sql, $params); } /** * Helper function to return all Calendar Subscription id results for a specified context and optionally * included user list. * * @param \context $context * @param array $userids * @return array * @throws \dml_exception */ protected static function get_calendar_subscription_ids_by_context(\context $context, $userids = array()) { global $DB; // Calendar Subscriptions can exist at Site (CONTEXT_SYSTEM), Course Category (CONTEXT_COURSECAT), // Course and Course Group (CONTEXT_COURSE), or User (CONTEXT_USER) contexts. if (!in_array($context->contextlevel, [CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_USER])) { return []; } $whereusersql = ''; $userparams = array(); if (!empty($userids)) { list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); $whereusersql = "AND s.userid {$usersql}"; } if ($context->contextlevel == CONTEXT_SYSTEM) { $params = []; // Get Calendar Subscriptions for the system context. $sql = "SELECT DISTINCT s.id AS subscriptionid FROM {event_subscriptions} s WHERE s.eventtype = 'site' $whereusersql"; } else { $eventfields = [ CONTEXT_COURSECAT => 'categoryid', CONTEXT_COURSE => 'courseid', CONTEXT_USER => 'userid' ]; $eventfield = $eventfields[$context->contextlevel]; $eventtypes = [ CONTEXT_COURSECAT => 'category', CONTEXT_COURSE => ['course' , 'group'], CONTEXT_USER => 'user' ]; list($eventtypesql, $eventtypeparams) = $DB->get_in_or_equal($eventtypes[$context->contextlevel], SQL_PARAMS_NAMED); $params = $eventtypeparams + ['instanceid' => $context->instanceid]; // Get Calendar Subscriptions for the specified context. $sql = "SELECT DISTINCT s.id AS subscriptionid FROM {event_subscriptions} s WHERE s.eventtype $eventtypesql AND s.{$eventfield} = :instanceid $whereusersql"; } $params += $userparams; return $DB->get_records_sql($sql, $params); } /** * Helper function to return the Calendar Events for a given user and context list. * * @param approved_contextlist $contextlist * @return \moodle_recordset * @throws \coding_exception * @throws \dml_exception */ protected static function get_calendar_event_details_by_contextlist(approved_contextlist $contextlist) { global $DB; $userid = $contextlist->get_user()->id; list($contextsql1, $contextparams1) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); list($contextsql2, $contextparams2) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); // Calendar Events can exist at Site, Course Category, Course, Course Group, User, or Course Modules contexts. $params = [ 'sitecontext' => CONTEXT_SYSTEM, 'categorycontext' => CONTEXT_COURSECAT, 'coursecontext' => CONTEXT_COURSE, 'groupcontext' => CONTEXT_COURSE, 'usercontext' => CONTEXT_USER, 'cuserid' => $userid, 'modulecontext' => CONTEXT_MODULE, 'muserid' => $userid ]; $params += $contextparams1; $params += $contextparams2; // Get Calendar Events details for the approved contexts and the owner. $sql = "SELECT ctxid as contextid, details.id as eventid, details.name as name, details.description as description, details.location as location, details.eventtype as eventtype, details.timestart as timestart, details.timeduration as timeduration FROM ( SELECT e.id AS id, ctx.id AS ctxid FROM {context} ctx INNER JOIN {event} e ON (e.eventtype = 'site' AND ctx.contextlevel = :sitecontext) OR (e.categoryid = ctx.instanceid AND e.eventtype = 'category' AND ctx.contextlevel = :categorycontext) OR (e.courseid = ctx.instanceid AND e.eventtype = 'course' AND ctx.contextlevel = :coursecontext) OR (e.courseid = ctx.instanceid AND e.eventtype = 'group' AND ctx.contextlevel = :groupcontext) OR (e.userid = ctx.instanceid AND e.eventtype = 'user' AND ctx.contextlevel = :usercontext) WHERE e.userid = :cuserid AND ctx.id {$contextsql1} UNION SELECT e.id AS id, ctx.id AS ctxid FROM {context} ctx INNER JOIN {course_modules} cm ON cm.id = ctx.instanceid AND ctx.contextlevel = :modulecontext INNER JOIN {modules} m ON m.id = cm.module INNER JOIN {event} e ON e.modulename = m.name AND e.courseid = cm.course AND e.instance = cm.instance WHERE e.userid = :muserid AND ctx.id {$contextsql2} ) ids JOIN {event} details ON details.id = ids.id ORDER BY ids.id"; return $DB->get_recordset_sql($sql, $params); } /** * Helper function to return the Calendar Subscriptions for a given user and context list. * * @param approved_contextlist $contextlist * @return \moodle_recordset * @throws \coding_exception * @throws \dml_exception */ protected static function get_calendar_subscription_details_by_contextlist(approved_contextlist $contextlist) { global $DB; $user = $contextlist->get_user(); list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); $params = [ 'sitecontext' => CONTEXT_SYSTEM, 'categorycontext' => CONTEXT_COURSECAT, 'coursecontext' => CONTEXT_COURSE, 'groupcontext' => CONTEXT_COURSE, 'usercontext' => CONTEXT_USER, 'userid' => $user->id ]; $params += $contextparams; // Get Calendar Subscriptions for the approved contexts and the owner. $sql = "SELECT DISTINCT c.id as contextid, s.id as subscriptionid, s.name as name, s.url as url, s.eventtype as eventtype FROM {context} c INNER JOIN {event_subscriptions} s ON (s.eventtype = 'site' AND c.contextlevel = :sitecontext) OR (s.categoryid = c.instanceid AND s.eventtype = 'category' AND c.contextlevel = :categorycontext) OR (s.courseid = c.instanceid AND s.eventtype = 'course' AND c.contextlevel = :coursecontext) OR (s.courseid = c.instanceid AND s.eventtype = 'group' AND c.contextlevel = :groupcontext) OR (s.userid = c.instanceid AND s.eventtype = 'user' AND c.contextlevel = :usercontext) WHERE s.userid = :userid AND c.id {$contextsql}"; return $DB->get_recordset_sql($sql, $params); } /** * Helper function to delete records in batches in order to minimise amount of deletion queries. * * @param string $tablename The table name to delete from. * @param string $field The table column field name to delete records by. * @param array $values The table column field values to delete records by. * @throws \dml_exception */ protected static function delete_batch_records($tablename, $field, $values) { global $DB; // Batch deletion with an upper limit of 2000 records to minimise the number of deletion queries. $batchrecords = array_chunk($values, 2000); foreach ($batchrecords as $batchrecord) { $DB->delete_records_list($tablename, $field, $batchrecord); } } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/blocks/classes/privacy/provider.php000064400000024200152266736660025634 0ustar00. /** * Data provider. * * @package core_block * @copyright 2018 Frédéric Massart * @author Frédéric Massart * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core_block\privacy; defined('MOODLE_INTERNAL') || die(); use context; use context_block; use core_privacy\local\metadata\collection; use core_privacy\local\request\approved_contextlist; use core_privacy\local\request\transform; use core_privacy\local\request\writer; /** * Data provider class. * * @package core_block * @copyright 2018 Frédéric Massart * @author Frédéric Massart * @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\subsystem\provider, \core_privacy\local\request\user_preference_provider, \core_privacy\local\request\core_userlist_provider { /** * Returns metadata. * * @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_user_preference('blockIDhidden', 'privacy:metadata:userpref:hiddenblock'); $collection->add_user_preference('docked_block_instance_ID', 'privacy:metadata:userpref:dockedinstance'); 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): \core_privacy\local\request\contextlist { global $DB; $contextlist = new \core_privacy\local\request\contextlist(); // Fetch the block instance IDs. $likehidden = $DB->sql_like('name', ':hidden', false, false); $likedocked = $DB->sql_like('name', ':docked', false, false); $sql = "userid = :userid AND ($likehidden OR $likedocked)"; $params = [ 'userid' => $userid, 'hidden' => 'block%hidden', 'docked' => 'docked_block_instance_%', ]; $prefs = $DB->get_fieldset_select('user_preferences', 'name', $sql, $params); $instanceids = array_unique(array_map(function($prefname) { if (preg_match('/^block(\d+)hidden$/', $prefname, $matches)) { return $matches[1]; } else if (preg_match('/^docked_block_instance_(\d+)$/', $prefname, $matches)) { return $matches[1]; } return 0; }, $prefs)); // Find the context of the instances. if (!empty($instanceids)) { list($insql, $inparams) = $DB->get_in_or_equal($instanceids, SQL_PARAMS_NAMED); $sql = " SELECT ctx.id FROM {context} ctx WHERE ctx.instanceid $insql AND ctx.contextlevel = :blocklevel"; $params = array_merge($inparams, ['blocklevel' => CONTEXT_BLOCK]); $contextlist->add_from_sql($sql, $params); } return $contextlist; } /** * Get the list of users who have data within a context. * * @param \core_privacy\local\request\userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. */ public static function get_users_in_context(\core_privacy\local\request\userlist $userlist) { global $DB; $context = $userlist->get_context(); if ($context->contextlevel != CONTEXT_BLOCK) { return; } $params = ['docked' => 'docked_block_instance_' . $context->instanceid, 'hidden' => 'block' . $context->instanceid . 'hidden']; $sql = "SELECT userid FROM {user_preferences} WHERE name = :hidden OR name = :docked"; $userlist->add_from_sql('userid', $sql, $params); } /** * 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) { global $DB; $userid = $contextlist->get_user()->id; // Extract the block instance IDs. $instanceids = array_reduce($contextlist->get_contexts(), function($carry, $context) { if ($context->contextlevel == CONTEXT_BLOCK) { $carry[] = $context->instanceid; } return $carry; }, []); if (empty($instanceids)) { return; } // Query the blocks and their preferences. list($insql, $inparams) = $DB->get_in_or_equal($instanceids, SQL_PARAMS_NAMED); $hiddenkey = $DB->sql_concat("'block'", 'bi.id', "'hidden'"); $dockedkey = $DB->sql_concat("'docked_block_instance_'", 'bi.id'); $sql = " SELECT bi.id, h.value AS prefhidden, d.value AS prefdocked FROM {block_instances} bi LEFT JOIN {user_preferences} h ON h.userid = :userid1 AND h.name = $hiddenkey LEFT JOIN {user_preferences} d ON d.userid = :userid2 AND d.name = $dockedkey WHERE bi.id $insql AND (h.id IS NOT NULL OR d.id IS NOT NULL)"; $params = array_merge($inparams, [ 'userid1' => $userid, 'userid2' => $userid, ]); // Export all the things. $dockedstr = get_string('privacy:request:blockisdocked', 'core_block'); $hiddenstr = get_string('privacy:request:blockishidden', 'core_block'); $recordset = $DB->get_recordset_sql($sql, $params); foreach ($recordset as $record) { $context = context_block::instance($record->id); if ($record->prefdocked !== null) { writer::with_context($context)->export_user_preference( 'core_block', 'block_is_docked', transform::yesno($record->prefdocked), $dockedstr ); } if ($record->prefhidden !== null) { writer::with_context($context)->export_user_preference( 'core_block', 'block_is_hidden', transform::yesno($record->prefhidden), $hiddenstr ); } } $recordset->close(); } /** * Export all user preferences for the plugin. * * @param int $userid The userid of the user whose data is to be exported. */ public static function export_user_preferences(int $userid) { // Our preferences aren't site-wide so they are exported in export_user_data. } /** * Delete all data for all users in the specified context. * * @param context $context The specific context to delete data for. */ public static function delete_data_for_all_users_in_context(context $context) { global $DB; if ($context->contextlevel != CONTEXT_BLOCK) { return; } // Delete the user preferences. $instanceid = $context->instanceid; $DB->delete_records_list('user_preferences', 'name', [ "block{$instanceid}hidden", "docked_block_instance_{$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) { global $DB; $userid = $contextlist->get_user()->id; $prefnames = array_reduce($contextlist->get_contexts(), function($carry, $context) { if ($context->contextlevel == CONTEXT_BLOCK) { $carry[] = "block{$context->instanceid}hidden"; $carry[] = "docked_block_instance_{$context->instanceid}"; } return $carry; }, []); if (empty($prefnames)) { return; } list($insql, $inparams) = $DB->get_in_or_equal($prefnames, SQL_PARAMS_NAMED); $sql = "userid = :userid AND name $insql"; $params = array_merge($inparams, ['userid' => $userid]); $DB->delete_records_select('user_preferences', $sql, $params); } /** * Delete multiple users within a single context. * * @param \core_privacy\local\request\approved_userlist $userlist The approved context and user information to delete * information for. */ public static function delete_data_for_users(\core_privacy\local\request\approved_userlist $userlist) { global $DB; $context = $userlist->get_context(); if ($context->contextlevel != CONTEXT_BLOCK) { return; } list($insql, $params) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED); $params['hidden'] = 'block' . $context->instanceid . 'hidden'; $params['docked'] = 'docked_block_instance_' . $context->instanceid; $DB->delete_records_select('user_preferences', "(name = :hidden OR name = :docked) AND userid $insql", $params); } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/course/classes/privacy/provider.php000064400000031377152266737500025666 0ustar00. /** * Privacy class for requesting user data. * * @package core_course * @copyright 2018 Adrian Greeve * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core_course\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\approved_userlist; use \core_privacy\local\request\transform; use \core_privacy\local\request\userlist; use \core_privacy\local\request\writer; /** * Privacy class for requesting user data. * * @copyright 2018 Adrian Greeve * @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\context_aware_provider, \core_privacy\local\request\core_userlist_provider, \core_privacy\local\request\plugin\provider, \core_privacy\local\request\user_preference_provider { /** * Returns meta data about this system. * * @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_completion', [], 'privacy:metadata:completionsummary'); $collection->add_subsystem_link('core_favourites', [], 'privacy:metadata:favouritessummary'); $collection->add_subsystem_link('core_favourites', [], 'privacy:metadata:activityfavouritessummary'); $collection->add_user_preference('coursecat_management_perpage', 'privacy:perpage'); 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 { list($join, $where, $params) = \core_completion\privacy\provider::get_course_completion_join_sql($userid, 'cc', 'c.id'); $sql = "SELECT ctx.id FROM {context} ctx JOIN {course} c ON ctx.instanceid = c.id AND ctx.contextlevel = :contextcourse {$join} WHERE {$where}"; $params['contextcourse'] = CONTEXT_COURSE; $contextlist = new contextlist(); $contextlist->add_from_sql($sql, $params); \core_favourites\privacy\provider::add_contexts_for_userid($contextlist, $userid, 'core_course', 'courses'); return $contextlist; } /** * Get the list of users who have data within a 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_course) { return; } \core_completion\privacy\provider::add_course_completion_users_to_userlist($userlist); \core_favourites\privacy\provider::add_userids_for_context($userlist, 'courses'); } /** * 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) { global $DB; // Get the course. list($select, $params) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); $params['contextcourse'] = CONTEXT_COURSE; $sql = "SELECT c.* FROM {course} c JOIN {context} ctx ON c.id = ctx.instanceid AND ctx.contextlevel = :contextcourse WHERE ctx.id $select"; $courses = $DB->get_recordset_sql($sql, $params); foreach ($courses as $course) { $coursecompletion = \core_completion\privacy\provider::get_course_completion_info($contextlist->get_user(), $course); writer::with_context(\context_course::instance($course->id))->export_data( [get_string('privacy:completionpath', 'course')], (object) $coursecompletion); // Get user's favourites information for the particular course. $coursefavourite = \core_favourites\privacy\provider::get_favourites_info_for_user($contextlist->get_user()->id, \context_course::instance($course->id), 'core_course', 'courses', $course->id); if ($coursefavourite) { // If the course has been favourited by the user, include it in the export. writer::with_context(\context_course::instance($course->id))->export_data( [get_string('privacy:favouritespath', 'course')], (object) $coursefavourite); } } $courses->close(); } /** * Give the component a chance to include any contextual information deemed relevant to any child contexts which are * exporting personal data. * * By giving the component access to the full list of contexts being exported across all components, it can determine whether a * descendant context is being exported, and decide whether to add relevant contextual information about itself. Having access * to the full list of contexts being exported is what makes this component a context aware provider. * * E.g. * If, during the core export process, a course module is included in the contextlist_collection but the course containing the * module is not (perhaps there's no longer a user enrolment), then the course should include general contextual information in * the export so we know basic details about which course the module belongs to. This method allows the course to make that * decision, based on the existence of any decendant module contexts in the collection. * * @param \core_privacy\local\request\contextlist_collection $contextlistcollection */ public static function export_context_data(\core_privacy\local\request\contextlist_collection $contextlistcollection) { global $DB; $coursecontextids = $DB->get_records_menu('context', ['contextlevel' => CONTEXT_COURSE], '', 'id, instanceid'); $courseids = []; foreach ($contextlistcollection as $component) { foreach ($component->get_contexts() as $context) { // All course contexts have been accounted for, so skip all checks. if (empty($coursecontextids)) { break; } // Only course, module, and block contexts are checked. if (in_array($context->contextlevel, [CONTEXT_USER, CONTEXT_SYSTEM, CONTEXT_COURSECAT])) { continue; } // If the context is a course, then we just add it without the need to check context path. if ($context->contextlevel == CONTEXT_COURSE) { $courseids[$context->id] = $context->instanceid; unset($coursecontextids[$context->id]); continue; } // Otherwise, we need to check all the course context paths, to see if this context is a descendant. foreach ($coursecontextids as $contextid => $instanceid) { if (stripos($context->path, '/' . $contextid . '/') !== false) { $courseids[$contextid] = $instanceid; unset($coursecontextids[$contextid]); } } } } if (empty($courseids)) { return; } // Export general data for these contexts. list($sql, $params) = $DB->get_in_or_equal($courseids); $sql = 'id ' . $sql; $coursedata = $DB->get_records_select('course', $sql, $params); foreach ($coursedata as $course) { $context = \context_course::instance($course->id); $courseformat = $course->format !== 'site' ? get_string('pluginname', 'format_' . $course->format) : get_string('site'); $data = (object) [ 'fullname' => format_string($course->fullname, true, ['context' => $context]), 'shortname' => $course->shortname, 'idnumber' => $course->idnumber, 'summary' => format_text( writer::with_context($context)->rewrite_pluginfile_urls( [], 'course', 'summary', 0, $course->summary ), $course->summaryformat, ['context' => $context]), 'format' => $courseformat, 'startdate' => transform::datetime($course->startdate), 'enddate' => transform::datetime($course->enddate) ]; writer::with_context($context) ->export_area_files([], 'course', 'summary', 0) ->export_area_files([], 'course', 'overviewfiles', 0) ->export_data([], $data); } } /** * Export all user preferences for the plugin. * * @param int $userid The userid of the user whose data is to be exported. */ public static function export_user_preferences(int $userid) { $perpage = get_user_preferences('coursecat_management_perpage', null, $userid); if (isset($perpage)) { writer::export_user_preference('core_course', 'coursecat_management_perpage', $perpage, get_string('privacy:perpage', 'course') ); } } /** * Delete all data for all users in the specified context. * * @param \context $context The specific context to delete data for. */ public static function delete_data_for_all_users_in_context(\context $context) { // Check what context we've been delivered. if (!$context instanceof \context_course) { return; } // Delete course completion data. \core_completion\privacy\provider::delete_completion(null, $context->instanceid); // Delete course favourite data. \core_favourites\privacy\provider::delete_favourites_for_all_users($context, 'core_course', 'courses'); } /** * 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) { foreach ($contextlist as $context) { // Check what context we've been delivered. if ($context instanceof \context_course) { // Delete course completion data. \core_completion\privacy\provider::delete_completion($contextlist->get_user(), $context->instanceid); // Delete course favourite data. \core_favourites\privacy\provider::delete_favourites_for_user($contextlist, 'core_course', 'courses'); } } } /** * 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(); // Check what context we've been delivered. if (!$context instanceof \context_course) { return; } // Delete course completion data. \core_completion\privacy\provider::delete_completion_by_approved_userlist($userlist, $context->instanceid); // Delete course favourite data. \core_favourites\privacy\provider::delete_favourites_for_userlist($userlist, 'courses'); } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/auth/manual/classes/privacy/provider.php000064400000005153152266757740026605 0ustar00. /** * Privacy Subsystem implementation for auth_manual. * * @package auth_manual * @copyright 2018 Carlos Escobedo * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace auth_manual\privacy; defined('MOODLE_INTERNAL') || die(); use \core_privacy\local\request\writer; use \core_privacy\local\metadata\collection; use \core_privacy\local\request\transform; /** * Privacy provider for the authentication manual. * * @copyright 2018 Carlos Escobedo * @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\user_preference_provider { /** * Returns meta data about this system. * * @param collection $collection The initialised item collection to add items to. * @return collection A listing of user data stored through this system. */ public static function get_metadata(collection $collection): collection { // There is a one user preference. $collection->add_user_preference('auth_manual_passwordupdatetime', 'privacy:metadata:preference:passwordupdatetime'); return $collection; } /** * Export all user preferences for the plugin. * * @param int $userid The userid of the user whose data is to be exported. */ public static function export_user_preferences(int $userid) { $lastpasswordupdatetime = get_user_preferences('auth_manual_passwordupdatetime', null, $userid); if ($lastpasswordupdatetime !== null) { $time = transform::datetime($lastpasswordupdatetime); writer::export_user_preference('auth_manual', 'auth_manual_passwordupdatetime', $time, get_string('privacy:metadata:preference:passwordupdatetime', 'auth_manual') ); } } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/theme/classic/classes/privacy/provider.php000064400000002660152266777650027114 0ustar00. /** * Privacy Subsystem implementation for theme_classic. * * @package theme_classic * @copyright 2018 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace theme_classic\privacy; defined('MOODLE_INTERNAL') || die(); /** * The classic theme does not store any data. * * @copyright 2018 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\null_provider { /** * Get the language string identifier with the component's language * file to explain why this plugin stores no data. * * @return string */ public static function get_reason(): string { return 'privacy:metadata'; } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/filter/classes/privacy/provider.php000064400000002413152267006040025625 0ustar00. namespace core_filters\privacy; /** * Privacy Subsystem for core_filters implementing null_provider. * * @package core_filters * @copyright 2018 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\null_provider { /** * Get the language string identifier with the component's language * file to explain why this plugin stores no data. * * @return string */ public static function get_reason(): string { return 'privacy:reason'; } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/mod/subsection/classes/privacy/provider.php000064400000002376152267126270027315 0ustar00. namespace mod_subsection\privacy; /** * Privacy API implementation for the Subsection plugin. * * @package mod_subsection * @category privacy * @copyright 2023 Amaia Anabitarte * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\null_provider { /** * Returns stringid of a text explaining that this plugin stores no personal data. * * @return string */ public static function get_reason(): string { return 'privacy:metadata'; } } home/smkb8269/public_html/lms.smk-almanshuriyah.sch.id/ai/classes/provider.php000064400000017036152267624230023272 0ustar00. namespace core_ai; use core_ai\form\action_settings_form; use Psr\Http\Message\RequestInterface; use Spatie\Cloneable\Cloneable; /** * Class provider. * * @package core_ai * @copyright 2024 Matt Porritt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ abstract class provider { use Cloneable; /** @var string $provider The provider used to make this instance */ public readonly string $provider; /** @var array The configuration for this instance. */ public readonly array $config; /** @var array The action specific settings for this instance. */ public readonly array $actionconfig; /** * Create a new provider. * * @param bool $enabled Whether the gateway is enabled * @param string $name The name of the provider config. * @param string $config The configuration for this instance. * @param string $actionconfig The action specific settings for this instance. * @param int|null $id The id of the provider in the database. */ public function __construct( /** @var bool Whether the gateway is enabled */ public readonly bool $enabled, /** @var string The name of the provider config. */ public string $name, string $config, string $actionconfig = '', /** @var null|int The ID of the provider in the database, or null if it has not been persisted yet. */ public readonly ?int $id = null, ) { $this->provider = strstr(get_class($this), '\\', true); $this->config = json_decode($config, true); if ($actionconfig == '') { $this->actionconfig = static::initialise_action_settings(); } else { $this->actionconfig = json_decode($actionconfig, true); } } /** * Get the actions that this provider supports. * * Returns an array of action class names. * * @return array An array of action class names. */ abstract public static function get_action_list(): array; /** * Initialise the action settings array. * * @return array The initialised action settings. */ public static function initialise_action_settings(): array { $actions = static::get_action_list(); $actionconfig = []; foreach ($actions as $action) { $actionconfig[$action] = [ 'enabled' => true, 'settings' => static::get_action_setting_defaults($action), ]; } return $actionconfig; } /** * Given an action class name, return an array of sub actions * that this provider supports. * * @param string $classname The action class name. * @return array An array of supported sub actions. */ public function get_sub_actions(string $classname): array { return []; } /** * Get the name of the provider. * * @return string The name of the provider. */ public function get_name(): string { return \core\component::get_component_from_classname(get_class($this)); } /** * Get any action settings for this provider. * * @param string $action The action class name. * @param array $customdata The customdata for the form. * @return action_settings_form|bool The settings form for this action or false in no settings. */ public static function get_action_settings( string $action, array $customdata = [], ): action_settings_form|bool { return false; } /** * Get the default settings for an action. * * @param string $action The action class name. * @return array The default settings for the action. */ public static function get_action_setting_defaults(string $action): array { return []; } /** * Check if the request is allowed by the rate limiter. * * @param aiactions\base $action The action to check. * @return array|bool True on success, array of error details on failure. */ public function is_request_allowed(aiactions\base $action): array|bool { $ratelimiter = \core\di::get(rate_limiter::class); $component = \core\component::get_component_from_classname(get_class($this)); // Check the user rate limit. if (isset($this->config['enableuserratelimit']) && $this->config['enableuserratelimit']) { if (!$ratelimiter->check_user_rate_limit( component: $component, ratelimit: $this->config['userratelimit'], userid: $action->get_configuration('userid') )) { return [ 'success' => false, 'errorcode' => 429, 'errormessage' => 'User rate limit exceeded', ]; } } // Check the global rate limit. if (isset($this->config['enableglobalratelimit']) && $this->config['enableglobalratelimit']) { if (!$ratelimiter->check_global_rate_limit( component: $component, ratelimit: $this->config['globalratelimit'] )) { return [ 'success' => false, 'errorcode' => 429, 'errormessage' => 'Global rate limit exceeded', ]; } } return true; } /** * Check if a provider has the minimal configuration to work. * * @return bool Return true if configured. */ public function is_provider_configured(): bool { return false; } /** * Update a request to add any headers required by the provider (if needed). * AI providers will need to override this method to add their own headers. * * @param RequestInterface $request * @return RequestInterface */ public function add_authentication_headers(RequestInterface $request): RequestInterface { return $request; } /** * Generate a user id. * * This is a hash of the site id and user id, * this means we can determine who made the request * but don't pass any personal data to the AI provider. * * @param string $userid The user id. * @return string The generated user id. */ public function generate_userid(string $userid): string { global $CFG; return hash('sha256', $CFG->siteidentifier . $userid); } /** * Convert this object to a stdClass, suitable for saving to the database. * * @return \stdClass */ public function to_record(): \stdClass { return (object) [ 'id' => $this->id, 'name' => $this->name, 'provider' => get_class($this), 'enabled' => $this->enabled, 'config' => json_encode($this->config), 'actionconfig' => json_encode($this->actionconfig), ]; } }