芝麻web文件管理V1.00
编辑当前文件:/home2/sdektunc/.trash/cepali/mod/data/classes/privacy/provider.php
. /** * Privacy Subsystem implementation for mod_data. * * @package mod_data * @copyright 2018 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace mod_data\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\contextlist; use core_privacy\local\request\helper; use core_privacy\local\request\transform; use core_privacy\local\request\userlist; use core_privacy\local\request\writer; use core_privacy\manager; defined('MOODLE_INTERNAL') || die(); /** * Implementation of the privacy subsystem plugin provider for the database activity module. * * @package mod_data * @copyright 2018 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements // This plugin stores personal data. \core_privacy\local\metadata\provider, // This plugin is capable of determining which users have data within it. \core_privacy\local\request\core_userlist_provider, // This plugin is a core_user_data_provider. \core_privacy\local\request\plugin\provider { /** * Return the fields which contain personal data. * * @param collection $collection a reference to the collection to use to store the metadata. * @return collection the updated collection of metadata items. */ public static function get_metadata(collection $collection) : collection { $collection->add_database_table( 'data_records', [ 'userid' => 'privacy:metadata:data_records:userid', 'groupid' => 'privacy:metadata:data_records:groupid', 'timecreated' => 'privacy:metadata:data_records:timecreated', 'timemodified' => 'privacy:metadata:data_records:timemodified', 'approved' => 'privacy:metadata:data_records:approved', ], 'privacy:metadata:data_records' ); $collection->add_database_table( 'data_content', [ 'fieldid' => 'privacy:metadata:data_content:fieldid', 'content' => 'privacy:metadata:data_content:content', 'content1' => 'privacy:metadata:data_content:content1', 'content2' => 'privacy:metadata:data_content:content2', 'content3' => 'privacy:metadata:data_content:content3', 'content4' => 'privacy:metadata:data_content:content4', ], 'privacy:metadata:data_content' ); // Link to subplugins. $collection->add_plugintype_link('datafield', [], 'privacy:metadata:datafieldnpluginsummary'); // Subsystems used. $collection->link_subsystem('core_comment', 'privacy:metadata:commentpurpose'); $collection->link_subsystem('core_files', 'privacy:metadata:filepurpose'); $collection->link_subsystem('core_tag', 'privacy:metadata:tagpurpose'); $collection->link_subsystem('core_rating', 'privacy:metadata:ratingpurpose'); return $collection; } /** * Get the list of contexts that contain user information for the specified user. * * @param int $userid the userid. * @return contextlist the list of contexts containing user info for the user. */ public static function get_contexts_for_userid(int $userid) : contextlist { $contextlist = new contextlist(); // Fetch all data records that the user rote. $sql = "SELECT c.id FROM {context} c JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel JOIN {modules} m ON m.id = cm.module AND m.name = :modname JOIN {data} d ON d.id = cm.instance JOIN {data_records} dr ON dr.dataid = d.id WHERE dr.userid = :userid"; $params = [ 'contextlevel' => CONTEXT_MODULE, 'modname' => 'data', 'userid' => $userid, ]; $contextlist->add_from_sql($sql, $params); // Fetch contexts where the user commented. $sql = "SELECT c.id FROM {context} c JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel JOIN {modules} m ON m.id = cm.module AND m.name = :modname JOIN {data} d ON d.id = cm.instance JOIN {data_records} dr ON dr.dataid = d.id JOIN {comments} com ON com.commentarea = :commentarea and com.itemid = dr.id WHERE com.userid = :userid"; $params = [ 'contextlevel' => CONTEXT_MODULE, 'modname' => 'data', 'commentarea' => 'database_entry', 'userid' => $userid, ]; $contextlist->add_from_sql($sql, $params); // Fetch all data records. $ratingquery = \core_rating\privacy\provider::get_sql_join('r', 'mod_data', 'entry', 'dr.id', $userid, true); $sql = "SELECT c.id FROM {context} c JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel JOIN {modules} m ON m.id = cm.module AND m.name = :modname JOIN {data} d ON d.id = cm.instance JOIN {data_records} dr ON dr.dataid = d.id {$ratingquery->join} WHERE {$ratingquery->userwhere}"; $params = [ 'contextlevel' => CONTEXT_MODULE, 'modname' => 'data', ] + $ratingquery->params; $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(); if (!is_a($context, \context_module::class)) { return; } // Find users with data records. $sql = "SELECT dr.userid FROM {context} c JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel JOIN {modules} m ON m.id = cm.module AND m.name = :modname JOIN {data} d ON d.id = cm.instance JOIN {data_records} dr ON dr.dataid = d.id WHERE c.id = :contextid"; $params = [ 'modname' => 'data', 'contextid' => $context->id, 'contextlevel' => CONTEXT_MODULE, ]; $userlist->add_from_sql('userid', $sql, $params); // Find users with comments. \core_comment\privacy\provider::get_users_in_context_from_sql($userlist, 'com', 'mod_data', 'database_entry', $context->id); // Find users with ratings. $sql = "SELECT dr.id FROM {context} c JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel JOIN {modules} m ON m.id = cm.module AND m.name = :modname JOIN {data} d ON d.id = cm.instance JOIN {data_records} dr ON dr.dataid = d.id WHERE c.id = :contextid"; $params = [ 'modname' => 'data', 'contextid' => $context->id, 'contextlevel' => CONTEXT_MODULE, ]; \core_rating\privacy\provider::get_users_in_context_from_sql($userlist, 'rat', 'mod_data', 'entry', $sql, $params); } /** * Creates an object from all fields in the $record where key starts with $prefix * * @param \stdClass $record * @param string $prefix * @param array $additionalfields * @return \stdClass */ protected static function extract_object_from_record($record, $prefix, $additionalfields = []) { $object = new \stdClass(); foreach ($record as $key => $value) { if (preg_match('/^'.preg_quote($prefix, '/').'(.*)/', $key, $matches)) { $object->{$matches[1]} = $value; } } if ($additionalfields) { foreach ($additionalfields as $key => $value) { $object->$key = $value; } } return $object; } /** * Export one field answer in a record in database activity module * * @param \context $context * @param \stdClass $recordobj record from DB table {data_records} * @param \stdClass $fieldobj record from DB table {data_fields} * @param \stdClass $contentobj record from DB table {data_content} */ protected static function export_data_content($context, $recordobj, $fieldobj, $contentobj) { $value = (object)[ 'field' => [ // Name and description are displayed in mod_data without applying format_string(). 'name' => $fieldobj->name, 'description' => $fieldobj->description, 'type' => $fieldobj->type, 'required' => transform::yesno($fieldobj->required), ], 'content' => $contentobj->content ]; foreach (['content1', 'content2', 'content3', 'content4'] as $key) { if ($contentobj->$key !== null) { $value->$key = $contentobj->$key; } } $classname = manager::get_provider_classname_for_component('datafield_' . $fieldobj->type); if (class_exists($classname) && is_subclass_of($classname, datafield_provider::class)) { component_class_callback($classname, 'export_data_content', [$context, $recordobj, $fieldobj, $contentobj, $value]); } else { // Data field plugin does not implement datafield_provider, just export default value. writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $value); } writer::with_context($context)->export_area_files([$recordobj->id, $contentobj->id], 'mod_data', 'content', $contentobj->id); } /** * SQL query that returns all fields from {data_content}, {data_fields} and {data_records} tables * * @return string */ protected static function sql_fields() { return 'd.id AS dataid, dc.id AS contentid, dc.fieldid, df.type AS fieldtype, df.name AS fieldname, df.description AS fielddescription, df.required AS fieldrequired, df.param1 AS fieldparam1, df.param2 AS fieldparam2, df.param3 AS fieldparam3, df.param4 AS fieldparam4, df.param5 AS fieldparam5, df.param6 AS fieldparam6, df.param7 AS fieldparam7, df.param8 AS fieldparam8, df.param9 AS fieldparam9, df.param10 AS fieldparam10, dc.content AS contentcontent, dc.content1 AS contentcontent1, dc.content2 AS contentcontent2, dc.content3 AS contentcontent3, dc.content4 AS contentcontent4, dc.recordid, dr.timecreated AS recordtimecreated, dr.timemodified AS recordtimemodified, dr.approved AS recordapproved, dr.groupid AS recordgroupid, dr.userid AS recorduserid'; } /** * Export personal data for the given approved_contextlist. User and context information is contained within the contextlist. * * @param approved_contextlist $contextlist a list of contexts approved for export. */ public static function export_user_data(approved_contextlist $contextlist) { global $DB; if (!$contextlist->count()) { return; } $user = $contextlist->get_user(); list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); $sql = "SELECT cm.id AS cmid, d.name AS dataname, cm.course AS courseid, " . self::sql_fields() . " FROM {context} ctx JOIN {course_modules} cm ON cm.id = ctx.instanceid JOIN {modules} m ON m.id = cm.module AND m.name = :modname JOIN {data} d ON d.id = cm.instance JOIN {data_records} dr ON dr.dataid = d.id JOIN {data_content} dc ON dc.recordid = dr.id JOIN {data_fields} df ON df.id = dc.fieldid WHERE ctx.id {$contextsql} AND ctx.contextlevel = :contextlevel AND dr.userid = :userid OR EXISTS (SELECT 1 FROM {comments} com WHERE com.commentarea=:commentarea AND com.itemid = dr.id AND com.userid = :userid1) OR EXISTS (SELECT 1 FROM {rating} r WHERE r.contextid = ctx.id AND r.itemid = dr.id AND r.component = :moddata AND r.ratingarea = :ratingarea AND r.userid = :userid2) ORDER BY cm.id, dr.id, dc.fieldid"; $rs = $DB->get_recordset_sql($sql, $contextparams + ['contextlevel' => CONTEXT_MODULE, 'modname' => 'data', 'userid' => $user->id, 'userid1' => $user->id, 'commentarea' => 'database_entry', 'userid2' => $user->id, 'ratingarea' => 'entry', 'moddata' => 'mod_data']); $context = null; $recordobj = null; foreach ($rs as $row) { if (!$context || $context->instanceid != $row->cmid) { // This row belongs to the different data module than the previous row. // Export the data for the previous module. self::export_data($context, $user); // Start new data module. $context = \context_module::instance($row->cmid); } if (!$recordobj || $row->recordid != $recordobj->id) { // Export previous data record. self::export_data_record($context, $user, $recordobj); // Prepare for exporting new data record. $recordobj = self::extract_object_from_record($row, 'record', ['dataid' => $row->dataid]); } $fieldobj = self::extract_object_from_record($row, 'field', ['dataid' => $row->dataid]); $contentobj = self::extract_object_from_record($row, 'content', ['fieldid' => $fieldobj->id, 'recordid' => $recordobj->id]); self::export_data_content($context, $recordobj, $fieldobj, $contentobj); } $rs->close(); self::export_data_record($context, $user, $recordobj); self::export_data($context, $user); } /** * Export one entry in the database activity module (one record in {data_records} table) * * @param \context $context * @param \stdClass $user * @param \stdClass $recordobj */ protected static function export_data_record($context, $user, $recordobj) { if (!$recordobj) { return; } $data = [ 'userid' => transform::user($user->id), 'groupid' => $recordobj->groupid, 'timecreated' => transform::datetime($recordobj->timecreated), 'timemodified' => transform::datetime($recordobj->timemodified), 'approved' => transform::yesno($recordobj->approved), ]; // Data about the record. writer::with_context($context)->export_data([$recordobj->id], (object)$data); // Related tags. \core_tag\privacy\provider::export_item_tags($user->id, $context, [$recordobj->id], 'mod_data', 'data_records', $recordobj->id); // Export comments. For records that were not made by this user export only this user's comments, for own records // export comments made by everybody. \core_comment\privacy\provider::export_comments($context, 'mod_data', 'database_entry', $recordobj->id, [$recordobj->id], $recordobj->userid != $user->id); // Export ratings. For records that were not made by this user export only this user's ratings, for own records // export ratings from everybody. \core_rating\privacy\provider::export_area_ratings($user->id, $context, [$recordobj->id], 'mod_data', 'entry', $recordobj->id, $recordobj->userid != $user->id); } /** * Export basic info about database activity module * * @param \context $context * @param \stdClass $user */ protected static function export_data($context, $user) { if (!$context) { return; } $contextdata = helper::get_context_data($context, $user); helper::export_context_files($context, $user); writer::with_context($context)->export_data([], $contextdata); } /** * Delete all data for all users in the specified context. * * @param \context $context the context to delete in. */ public static function delete_data_for_all_users_in_context(\context $context) { global $DB; if (!$context instanceof \context_module) { return; } $recordstobedeleted = []; $sql = "SELECT " . self::sql_fields() . " FROM {course_modules} cm JOIN {modules} m ON m.id = cm.module AND m.name = :modname JOIN {data} d ON d.id = cm.instance JOIN {data_records} dr ON dr.dataid = d.id LEFT JOIN {data_content} dc ON dc.recordid = dr.id LEFT JOIN {data_fields} df ON df.id = dc.fieldid WHERE cm.id = :cmid ORDER BY dr.id"; $rs = $DB->get_recordset_sql($sql, ['cmid' => $context->instanceid, 'modname' => 'data']); foreach ($rs as $row) { self::mark_data_content_for_deletion($context, $row); $recordstobedeleted[$row->recordid] = $row->recordid; } $rs->close(); self::delete_data_records($context, $recordstobedeleted); } /** * Delete all user data for the specified user, in the specified contexts. * * @param approved_contextlist $contextlist a list of contexts approved for deletion. */ public static function delete_data_for_user(approved_contextlist $contextlist) { global $DB; if (empty($contextlist->count())) { return; } $user = $contextlist->get_user(); $recordstobedeleted = []; foreach ($contextlist->get_contexts() as $context) { $sql = "SELECT " . self::sql_fields() . " FROM {context} ctx JOIN {course_modules} cm ON cm.id = ctx.instanceid JOIN {modules} m ON m.id = cm.module AND m.name = :modname JOIN {data} d ON d.id = cm.instance JOIN {data_records} dr ON dr.dataid = d.id AND dr.userid = :userid LEFT JOIN {data_content} dc ON dc.recordid = dr.id LEFT JOIN {data_fields} df ON df.id = dc.fieldid WHERE ctx.id = :ctxid AND ctx.contextlevel = :contextlevel ORDER BY dr.id"; $rs = $DB->get_recordset_sql($sql, ['ctxid' => $context->id, 'contextlevel' => CONTEXT_MODULE, 'modname' => 'data', 'userid' => $user->id]); foreach ($rs as $row) { self::mark_data_content_for_deletion($context, $row); $recordstobedeleted[$row->recordid] = $row->recordid; } $rs->close(); self::delete_data_records($context, $recordstobedeleted); } // Additionally remove comments this user made on other entries. \core_comment\privacy\provider::delete_comments_for_user($contextlist, 'mod_data', 'database_entry'); // We do not delete ratings made by this user on other records because it may change grades. } /** * 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(); $recordstobedeleted = []; list($userinsql, $userinparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED); $sql = "SELECT " . self::sql_fields() . " FROM {context} ctx JOIN {course_modules} cm ON cm.id = ctx.instanceid JOIN {modules} m ON m.id = cm.module AND m.name = :modname JOIN {data} d ON d.id = cm.instance JOIN {data_records} dr ON dr.dataid = d.id AND dr.userid {$userinsql} LEFT JOIN {data_content} dc ON dc.recordid = dr.id LEFT JOIN {data_fields} df ON df.id = dc.fieldid WHERE ctx.id = :ctxid AND ctx.contextlevel = :contextlevel ORDER BY dr.id"; $params = [ 'ctxid' => $context->id, 'contextlevel' => CONTEXT_MODULE, 'modname' => 'data', ]; $params += $userinparams; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $row) { self::mark_data_content_for_deletion($context, $row); $recordstobedeleted[$row->recordid] = $row->recordid; } $rs->close(); self::delete_data_records($context, $recordstobedeleted); // Additionally remove comments these users made on other entries. \core_comment\privacy\provider::delete_comments_for_users($userlist, 'mod_data', 'database_entry'); // We do not delete ratings made by users on other records because it may change grades. } /** * Marks a data_record/data_content for deletion * * Also invokes callback from datafield plugin in case it stores additional data that needs to be deleted * * @param \context $context * @param \stdClass $row result of SQL query - tables data_content, data_record, data_fields join together */ protected static function mark_data_content_for_deletion($context, $row) { $recordobj = self::extract_object_from_record($row, 'record', ['dataid' => $row->dataid]); if ($row->contentid && $row->fieldid) { $fieldobj = self::extract_object_from_record($row, 'field', ['dataid' => $row->dataid]); $contentobj = self::extract_object_from_record($row, 'content', ['fieldid' => $fieldobj->id, 'recordid' => $recordobj->id]); // Allow datafield plugin to implement their own deletion. $classname = manager::get_provider_classname_for_component('datafield_' . $fieldobj->type); if (class_exists($classname) && is_subclass_of($classname, datafield_provider::class)) { component_class_callback($classname, 'delete_data_content', [$context, $recordobj, $fieldobj, $contentobj]); } } } /** * Deletes records marked for deletion and all associated data * * Should be executed after all records were marked by {@link mark_data_content_for_deletion()} * * Deletes records from data_content and data_records tables, associated files, tags, comments and ratings. * * @param \context $context * @param array $recordstobedeleted list of ids of the data records that need to be deleted */ protected static function delete_data_records($context, $recordstobedeleted) { global $DB; if (empty($recordstobedeleted)) { return; } list($sql, $params) = $DB->get_in_or_equal($recordstobedeleted, SQL_PARAMS_NAMED); // Delete files. get_file_storage()->delete_area_files_select($context->id, 'mod_data', 'data_records', "IN (SELECT dc.id FROM {data_content} dc WHERE dc.recordid $sql)", $params); // Delete from data_content. $DB->delete_records_select('data_content', 'recordid ' . $sql, $params); // Delete from data_records. $DB->delete_records_select('data_records', 'id ' . $sql, $params); // Delete tags. \core_tag\privacy\provider::delete_item_tags_select($context, 'mod_data', 'data_records', $sql, $params); // Delete comments. \core_comment\privacy\provider::delete_comments_for_all_users_select($context, 'mod_data', 'database_entry', $sql, $params); // Delete ratings. \core_rating\privacy\provider::delete_ratings_select($context, 'mod_data', 'entry', $sql, $params); } }