MDL-61937 mod_data: implement privacy provider

This commit is contained in:
Marina Glancy
2018-05-04 11:22:15 +08:00
parent 1c4b87cb97
commit ab803d697c
28 changed files with 1228 additions and 24 deletions
@@ -0,0 +1,72 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains interface datafield_provider
*
* @package mod_data
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_data\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Interface datafield_provider, all datafield plugins need to implement it
*
* @package mod_data
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface datafield_provider extends \core_privacy\local\request\plugin\subplugin_provider {
/**
* Exports data about one record in {data_content} table.
*
* Datafield plugins providers should implement this method to:
* - preprocess references to files in the response (examples - textarea, picture, file)
* - make content more human-readable (example - replace values separators in multimenu, format date in date)
* - add more information about the field itself (example - list all options for menu, multimenu, radio)
*
* Sample implementation (from datafield_textarea):
*
* $defaultvalue->content = writer::with_context($context)
* ->rewrite_pluginfile_urls([$recordobj->id, $contentobj->id], 'mod_data', 'content', $contentobj->id,
* $defaultvalue->content);
* writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $defaultvalue);
*
* @param \context_module $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}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue);
/**
* Allows plugins to delete locally stored data.
*
* Usually datafield plugins do not store anything and this method will be empty.
*
* @param \context_module $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}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj);
}
+445
View File
@@ -0,0 +1,445 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* 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\contextlist;
use core_privacy\local\request\helper;
use core_privacy\local\request\transform;
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 a core_user_data_provider.
\core_privacy\local\request\plugin\provider {
/** @var array stores list of records marked for deletion */
protected static $deletedrecords = [];
/**
* 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 {
// Fetch all data records.
$sql = "SELECT c.id
FROM {context} c
INNER JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
INNER JOIN {data} d ON d.id = cm.instance
INNER JOIN {data_records} dr ON dr.dataid = d.id
LEFT JOIN {comments} com ON com.commentarea=:commentarea and com.itemid = dr.id
LEFT JOIN {rating} r ON r.contextid = c.id AND r.itemid = dr.id AND r.component = :moddata AND r.ratingarea = :ratingarea
WHERE dr.userid = :userid OR com.userid = :userid1 OR r.userid = :userid2";
$params = [
'modname' => 'data',
'contextlevel' => CONTEXT_MODULE,
'userid' => $userid,
'userid1' => $userid,
'userid2' => $userid,
'commentarea' => 'database_entry',
'moddata' => 'mod_data',
'ratingarea' => 'entry',
];
$contextlist = new contextlist();
$contextlist->add_from_sql($sql, $params);
return $contextlist;
}
/**
* 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], '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;
}
$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);
}
$rs->close();
self::delete_data_records($context);
}
/**
* 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();
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);
}
$rs->close();
self::delete_data_records($context);
}
// Additionally remove comments this user made on other entries.
\core_comment\privacy\provider::delete_comments_for_user($contextlist, 'mod_data', 'entry');
// We do not delete ratings made by this user 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]);
}
}
self::$deletedrecords[$recordobj->id] = $recordobj->id;
}
/**
* 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
*/
protected static function delete_data_records($context) {
global $DB;
if (empty(self::$deletedrecords)) {
return;
}
list($sql, $params) = $DB->get_in_or_equal(self::$deletedrecords, 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', 'entry', $sql, $params);
// Delete ratings.
\core_rating\privacy\provider::delete_ratings_select($context, 'mod_data', 'entry', $sql, $params);
self::$deletedrecords = [];
}
}
@@ -21,6 +21,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace datafield_checkbox\privacy;
use core_privacy\local\request\writer;
use mod_data\privacy\datafield_provider;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for datafield_checkbox implementing null_provider.
@@ -28,7 +31,8 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2018 Carlos Escobedo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
class provider implements \core_privacy\local\metadata\null_provider,
datafield_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
@@ -38,4 +42,31 @@ class provider implements \core_privacy\local\metadata\null_provider {
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Exports data about one record in {data_content} table.
*
* @param \context_module $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}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue) {
$defaultvalue->field['options'] = preg_split('/\s*\n\s*/', trim($fieldobj->param1), -1, PREG_SPLIT_NO_EMPTY);
$defaultvalue->content = explode('##', $defaultvalue->content);
writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $defaultvalue);
}
/**
* Allows plugins to delete locally stored data.
*
* @param \context_module $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}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj) {
}
}
@@ -26,4 +26,4 @@
$string['pluginname'] = 'Checkbox';
$string['fieldtypelabel'] = 'Checkbox field';
$string['privacy:metadata'] = 'The Checkbox field component does not store any personal data.';
$string['privacy:metadata'] = 'The Checkbox field component does not store any personal data itself, it uses tables defined in mod_data.';
@@ -21,6 +21,10 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace datafield_date\privacy;
use core_privacy\local\request\transform;
use core_privacy\local\request\writer;
use mod_data\privacy\datafield_provider;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for datafield_date implementing null_provider.
@@ -28,7 +32,8 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2018 Carlos Escobedo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
class provider implements \core_privacy\local\metadata\null_provider,
datafield_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
@@ -38,4 +43,30 @@ class provider implements \core_privacy\local\metadata\null_provider {
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Exports data about one record in {data_content} table.
*
* @param \context_module $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}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue) {
$defaultvalue->content = transform::date($defaultvalue->content);
writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $defaultvalue);
}
/**
* Allows plugins to delete locally stored data.
*
* @param \context_module $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}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj) {
}
}
@@ -26,4 +26,4 @@
$string['pluginname'] = 'Date';
$string['fieldtypelabel'] = 'Date field';
$string['privacy:metadata'] = 'The Date field component does not store any personal data.';
$string['privacy:metadata'] = 'The Date field component does not store any personal data, it uses tables defined in mod_data.';
@@ -21,6 +21,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace datafield_file\privacy;
use mod_data\privacy\datafield_provider;
use core_privacy\local\request\writer;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for datafield_file implementing null_provider.
@@ -28,7 +31,8 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2018 Carlos Escobedo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
class provider implements \core_privacy\local\metadata\null_provider,
datafield_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
@@ -38,4 +42,37 @@ class provider implements \core_privacy\local\metadata\null_provider {
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Exports data about one record in {data_content} table.
*
* @param \context_module $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}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue) {
if ($fieldobj->param3) {
$defaultvalue->field['maxbytes'] = $fieldobj->param3;
}
// Change file name to file path.
$defaultvalue->file = writer::with_context($context)
->rewrite_pluginfile_urls([$recordobj->id, $contentobj->id], 'mod_data', 'content', $contentobj->id,
'@@PLUGINFILE@@/' . $defaultvalue->content);
unset($defaultvalue->content);
writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $defaultvalue);
}
/**
* Allows plugins to delete locally stored data.
*
* @param \context_module $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}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj) {
}
}
@@ -26,4 +26,4 @@
$string['pluginname'] = 'File';
$string['fieldtypelabel'] = 'File field';
$string['privacy:metadata'] = 'The File field component does not store any personal data.';
$string['privacy:metadata'] = 'The File field component does not store any personal data, it uses tables defined in mod_data.';
@@ -21,6 +21,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace datafield_latlong\privacy;
use core_privacy\local\request\writer;
use mod_data\privacy\datafield_provider;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for datafield_latlong implementing null_provider.
@@ -28,7 +31,8 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2018 Carlos Escobedo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
class provider implements \core_privacy\local\metadata\null_provider,
datafield_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
@@ -38,4 +42,41 @@ class provider implements \core_privacy\local\metadata\null_provider {
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Exports data about one record in {data_content} table.
*
* @param \context_module $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}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue) {
$defaultvalue->field['services'] = explode(',', $fieldobj->param1);
if ($fieldobj->param2 > 0) {
$defaultvalue->field['label'] = 'Content of field '.$fieldobj->param2;
} else if ($fieldobj->param2 == -2) {
$defaultvalue->field['label'] = 'lattitude/longitude';
} else {
$defaultvalue->field['label'] = 'item #';
}
$defaultvalue->lattitude = $contentobj->content;
$defaultvalue->longitude = $contentobj->content1;
unset($defaultvalue->content);
unset($defaultvalue->content1);
writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $defaultvalue);
}
/**
* Allows plugins to delete locally stored data.
*
* @param \context_module $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}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj) {
}
}
@@ -26,4 +26,4 @@
$string['pluginname'] = 'Latlong';
$string['fieldtypelabel'] = 'Latitude/longitude field';
$string['privacy:metadata'] = 'The Latitude/longitude field component does not store any personal data.';
$string['privacy:metadata'] = 'The Latitude/longitude field component does not store any personal data, it uses tables defined in mod_data.';
@@ -21,6 +21,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace datafield_menu\privacy;
use core_privacy\local\request\writer;
use mod_data\privacy\datafield_provider;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for datafield_menu implementing null_provider.
@@ -28,7 +31,8 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2018 Carlos Escobedo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
class provider implements \core_privacy\local\metadata\null_provider,
datafield_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
@@ -38,4 +42,30 @@ class provider implements \core_privacy\local\metadata\null_provider {
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Exports data about one record in {data_content} table.
*
* @param \context_module $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}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue) {
$defaultvalue->field['options'] = preg_split('/\s*\n\s*/', trim($fieldobj->param1), -1, PREG_SPLIT_NO_EMPTY);
writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $defaultvalue);
}
/**
* Allows plugins to delete locally stored data.
*
* @param \context_module $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}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj) {
}
}
@@ -26,4 +26,4 @@
$string['pluginname'] = 'Menu';
$string['fieldtypelabel'] = 'Menu field';
$string['privacy:metadata'] = 'The Menu field component does not store any personal data.';
$string['privacy:metadata'] = 'The Menu field component does not store any personal data, it uses tables defined in mod_data.';
@@ -21,6 +21,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace datafield_multimenu\privacy;
use core_privacy\local\request\writer;
use mod_data\privacy\datafield_provider;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for datafield_multimenu implementing null_provider.
@@ -28,7 +31,8 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2018 Carlos Escobedo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
class provider implements \core_privacy\local\metadata\null_provider,
datafield_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
@@ -38,4 +42,34 @@ class provider implements \core_privacy\local\metadata\null_provider {
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Exports data about one record in {data_content} table.
*
* @param \context_module $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}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue) {
$defaultvalue->field['options'] = preg_split('/\s*\n\s*/', trim($fieldobj->param1), -1, PREG_SPLIT_NO_EMPTY);
for ($i = 1; $i <= 10; $i++) {
unset($defaultvalue->field['param' . $i]);
}
$defaultvalue->content = explode('##', $defaultvalue->content);
writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $defaultvalue);
}
/**
* Allows plugins to delete locally stored data.
*
* @param \context_module $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}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj) {
}
}
@@ -26,4 +26,4 @@
$string['pluginname'] = 'Multimenu';
$string['fieldtypelabel'] = 'Multiple-selection menu field';
$string['privacy:metadata'] = 'The Multiple-selection menu field component does not store any personal data.';
$string['privacy:metadata'] = 'The Multiple-selection menu field component does not store any personal data, it uses tables defined in mod_data.';
@@ -21,6 +21,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace datafield_number\privacy;
use core_privacy\local\request\writer;
use mod_data\privacy\datafield_provider;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for datafield_number implementing null_provider.
@@ -28,7 +31,8 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2018 Carlos Escobedo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
class provider implements \core_privacy\local\metadata\null_provider,
datafield_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
@@ -38,4 +42,32 @@ class provider implements \core_privacy\local\metadata\null_provider {
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Exports data about one record in {data_content} table.
*
* @param \context_module $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}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue) {
if (preg_match("/^\d+$/", trim($fieldobj->param1))) {
$defaultvalue->field['decimals'] = trim($fieldobj->param1);
}
writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $defaultvalue);
}
/**
* Allows plugins to delete locally stored data.
*
* @param \context_module $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}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj) {
}
}
@@ -26,4 +26,4 @@
$string['pluginname'] = 'Number';
$string['fieldtypelabel'] = 'Number field';
$string['privacy:metadata'] = 'The Number field component does not store any personal data.';
$string['privacy:metadata'] = 'The Number field component does not store any personal data, it uses tables defined in mod_data.';
@@ -21,6 +21,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace datafield_picture\privacy;
use core_privacy\local\request\writer;
use mod_data\privacy\datafield_provider;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for datafield_picture implementing null_provider.
@@ -28,7 +31,8 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2018 Carlos Escobedo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
class provider implements \core_privacy\local\metadata\null_provider,
datafield_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
@@ -38,4 +42,48 @@ class provider implements \core_privacy\local\metadata\null_provider {
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Exports data about one record in {data_content} table.
*
* @param \context_module $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}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue) {
if ($fieldobj->param1) {
$defaultvalue->field['width'] = $fieldobj->param1;
}
if ($fieldobj->param2) {
$defaultvalue->field['height'] = $fieldobj->param2;
}
if ($fieldobj->param3) {
$defaultvalue->field['maxbytes'] = $fieldobj->param3;
}
// Change file name to file path.
$defaultvalue->file = writer::with_context($context)
->rewrite_pluginfile_urls([$recordobj->id, $contentobj->id], 'mod_data', 'content', $contentobj->id,
'@@PLUGINFILE@@/' . $defaultvalue->content);
if (isset($defaultvalue->content1)) {
$defaultvalue->alttext = $defaultvalue->content1;
}
unset($defaultvalue->content);
unset($defaultvalue->content1);
writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $defaultvalue);
}
/**
* Allows plugins to delete locally stored data.
*
* @param \context_module $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}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj) {
}
}
@@ -26,4 +26,4 @@
$string['pluginname'] = 'Picture';
$string['fieldtypelabel'] = 'Picture field';
$string['privacy:metadata'] = 'The Picture field component does not store any personal data.';
$string['privacy:metadata'] = 'The Picture field component does not store any personal data, it uses tables defined in mod_data.';
@@ -21,6 +21,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace datafield_radiobutton\privacy;
use core_privacy\local\request\writer;
use mod_data\privacy\datafield_provider;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for datafield_radiobutton implementing null_provider.
@@ -28,7 +31,8 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2018 Carlos Escobedo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
class provider implements \core_privacy\local\metadata\null_provider,
datafield_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
@@ -38,4 +42,30 @@ class provider implements \core_privacy\local\metadata\null_provider {
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Exports data about one record in {data_content} table.
*
* @param \context_module $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}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue) {
$defaultvalue->field['options'] = preg_split('/\s*\n\s*/', trim($fieldobj->param1), -1, PREG_SPLIT_NO_EMPTY);
writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $defaultvalue);
}
/**
* Allows plugins to delete locally stored data.
*
* @param \context_module $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}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj) {
}
}
@@ -26,5 +26,5 @@
$string['pluginname'] = 'Radio button';
$string['fieldtypelabel'] = 'Radio button field';
$string['privacy:metadata'] = 'The Radio button field component does not store any personal data.';
$string['privacy:metadata'] = 'The Radio button field component does not store any personal data, it uses tables defined in mod_data.';
@@ -21,6 +21,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace datafield_text\privacy;
use core_privacy\local\request\writer;
use mod_data\privacy\datafield_provider;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for datafield_text implementing null_provider.
@@ -28,7 +31,8 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2018 Carlos Escobedo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
class provider implements \core_privacy\local\metadata\null_provider,
datafield_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
@@ -38,4 +42,29 @@ class provider implements \core_privacy\local\metadata\null_provider {
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Exports data about one record in {data_content} table.
*
* @param \context_module $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}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue) {
writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $defaultvalue);
}
/**
* Allows plugins to delete locally stored data.
*
* @param \context_module $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}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj) {
}
}
@@ -26,4 +26,4 @@
$string['pluginname'] = 'Text input';
$string['fieldtypelabel'] = 'Text field';
$string['privacy:metadata'] = 'The Text field component does not store any personal data.';
$string['privacy:metadata'] = 'The Text field component does not store any personal data, it uses tables defined in mod_data.';
@@ -21,6 +21,11 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace datafield_textarea\privacy;
use core_privacy\local\request\transform;
use core_privacy\local\request\writer;
use mod_data\privacy\datafield_provider;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for datafield_textarea implementing null_provider.
@@ -28,7 +33,8 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2018 Carlos Escobedo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
class provider implements \core_privacy\local\metadata\null_provider,
datafield_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
@@ -38,4 +44,42 @@ class provider implements \core_privacy\local\metadata\null_provider {
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Exports data about one record in {data_content} table.
*
* @param \context_module $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}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue) {
$subcontext = [$recordobj->id, $contentobj->id];
$defaultvalue->content = writer::with_context($context)
->rewrite_pluginfile_urls($subcontext, 'mod_data', 'content', $contentobj->id,
$defaultvalue->content);
$defaultvalue->contentformat = $defaultvalue->content1;
unset($defaultvalue->content1);
$defaultvalue->field['autolink'] = transform::yesno($fieldobj->param1);
$defaultvalue->field['rows'] = $fieldobj->param3;
$defaultvalue->field['cols'] = $fieldobj->param2;
if ($fieldobj->param5) {
$defaultvalue->field['maxbytes'] = $fieldobj->param5;
}
writer::with_context($context)->export_data($subcontext, $defaultvalue);
}
/**
* Allows plugins to delete locally stored data.
*
* @param \context_module $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}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj) {
}
}
@@ -28,4 +28,4 @@ $string['maxbytes'] = 'Maximum embedded file size (bytes)';
$string['maxbytes_desc'] = 'If set to zero will be unlimited by default';
$string['pluginname'] = 'Text area';
$string['fieldtypelabel'] = 'Textarea field';
$string['privacy:metadata'] = 'The Textarea field component does not store any personal data.';
$string['privacy:metadata'] = 'The Textarea field component does not store any personal data, it uses tables defined in mod_data.';
@@ -21,6 +21,10 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace datafield_url\privacy;
use core_privacy\local\request\transform;
use core_privacy\local\request\writer;
use mod_data\privacy\datafield_provider;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for datafield_url implementing null_provider.
@@ -28,7 +32,8 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2018 Carlos Escobedo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
class provider implements \core_privacy\local\metadata\null_provider,
datafield_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
@@ -38,4 +43,38 @@ class provider implements \core_privacy\local\metadata\null_provider {
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Exports data about one record in {data_content} table.
*
* @param \context_module $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}
* @param \stdClass $defaultvalue pre-populated default value that most of plugins will use
*/
public static function export_data_content($context, $recordobj, $fieldobj, $contentobj, $defaultvalue) {
$defaultvalue->field['autolink'] = transform::yesno($fieldobj->param1);
if (!empty($fieldobj->param2)) {
$defaultvalue->field['forcetext'] = $fieldobj->param2;
}
$defaultvalue->field['blanktarget'] = transform::yesno($fieldobj->param3);
$defaultvalue->url = $contentobj->content;
$defaultvalue->text = $contentobj->content1;
unset($defaultvalue->content);
unset($defaultvalue->content1);
writer::with_context($context)->export_data([$recordobj->id, $contentobj->id], $defaultvalue);
}
/**
* Allows plugins to delete locally stored data.
*
* @param \context_module $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}
*/
public static function delete_data_content($context, $recordobj, $fieldobj, $contentobj) {
}
}
+1 -1
View File
@@ -27,4 +27,4 @@
$string['pluginname'] = 'URL';
$string['openlinkinnewwindow'] = 'Open link in new window';
$string['fieldtypelabel'] = 'URL field';
$string['privacy:metadata'] = 'The URL field component does not store any personal data.';
$string['privacy:metadata'] = 'The URL field component does not store any personal data, it uses tables defined in mod_data.';
+18
View File
@@ -302,6 +302,24 @@ $string['pluginname'] = 'Database';
$string['portfolionotfile'] = 'Export to a portfolio rather than a file (csv and leap2a only)';
$string['presetinfo'] = 'Saving as a preset will publish this template. Other users may be able to use it in their databases.';
$string['presets'] = 'Presets';
$string['privacy:metadata:commentpurpose'] = 'Comments on database records';
$string['privacy:metadata:data_content'] = 'Represents one answer to one field in database activity module';
$string['privacy:metadata:data_content:fieldid'] = 'Field definition id';
$string['privacy:metadata:data_content:content'] = 'Content';
$string['privacy:metadata:data_content:content1'] = 'Additional content 1';
$string['privacy:metadata:data_content:content2'] = 'Additional content 2';
$string['privacy:metadata:data_content:content3'] = 'Additional content 3';
$string['privacy:metadata:data_content:content4'] = 'Additional content 4';
$string['privacy:metadata:data_records'] = 'Represent records in database acitivity module';
$string['privacy:metadata:data_records:userid'] = 'User who created the record';
$string['privacy:metadata:data_records:groupid'] = 'Group';
$string['privacy:metadata:data_records:timecreated'] = 'Time when record was created';
$string['privacy:metadata:data_records:timemodified'] = 'Time when record was last modified';
$string['privacy:metadata:data_records:approved'] = 'Approval status';
$string['privacy:metadata:datafieldnpluginsummary'] = 'Fields for database activity module';
$string['privacy:metadata:filepurpose'] = 'File attached to the database record';
$string['privacy:metadata:tagpurpose'] = 'Tags on database records';
$string['privacy:metadata:ratingpurpose'] = 'Ratings on database records';
$string['radiobutton'] = 'Radio buttons';
$string['recordapproved'] = 'Entry approved';
$string['recorddeleted'] = 'Entry deleted';
+243
View File
@@ -0,0 +1,243 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy provider tests.
*
* @package mod_data
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use core_privacy\local\metadata\collection;
use mod_data\privacy\provider;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy provider tests class.
*
* @package mod_data
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_data_privacy_provider_testcase extends \core_privacy\tests\provider_testcase {
/** @var stdClass The student object. */
protected $student;
/** @var stdClass The student object. */
protected $student2;
/** @var stdClass The data object. */
protected $datamodule;
/** @var stdClass The course object. */
protected $course;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->resetAfterTest();
global $DB;
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$params = [
'course' => $course->id,
'name' => 'Database module',
'comments' => 1,
'assessed' => 1,
];
// The database activity.
$datamodule = $this->get_generator()->create_instance($params);
$fieldtypes = array('checkbox', 'date', 'menu', 'multimenu', 'number', 'radiobutton', 'text', 'textarea', 'url',
'latlong', 'file', 'picture');
// Creating test Fields with default parameter values.
foreach ($fieldtypes as $count => $fieldtype) {
// Creating variables dynamically.
$fieldname = 'field' . $count;
$record = new \stdClass();
$record->name = $fieldname;
$record->description = $fieldname . ' descr';
$record->type = $fieldtype;
${$fieldname} = $this->get_generator()->create_field($record, $datamodule);
}
$cm = get_coursemodule_from_instance('data', $datamodule->id);
// Create a student.
$student1 = $generator->create_user();
$student2 = $generator->create_user();
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
$generator->enrol_user($student1->id, $course->id, $studentrole->id);
$generator->enrol_user($student2->id, $course->id, $studentrole->id);
// Add records.
$this->setUser($student1);
$record1id = $this->generate_data_record($datamodule);
$this->generate_data_record($datamodule);
$this->setUser($student2);
$this->generate_data_record($datamodule);
$this->generate_data_record($datamodule);
$this->generate_data_record($datamodule);
$this->student = $student1;
$this->student2 = $student2;
$this->datamodule = $datamodule;
$this->course = $course;
}
/**
* Get mod_data generator
*
* @return mod_data_generator
*/
protected function get_generator() {
return $this->getDataGenerator()->get_plugin_generator('mod_data');
}
/**
* Generates one record in the database module as the current student
*
* @param stdClass $datamodule
* @return mixed
*/
protected function generate_data_record($datamodule) {
global $DB;
static $counter = 0;
$counter++;
$contents = array();
$contents[] = array('opt1', 'opt2', 'opt3', 'opt4');
$contents[] = sprintf("%02f", $counter) . '-01-2000';
$contents[] = 'menu1';
$contents[] = array('multimenu1', 'multimenu2', 'multimenu3', 'multimenu4');
$contents[] = 5 * $counter;
$contents[] = 'radioopt1';
$contents[] = 'text for testing' . $counter;
$contents[] = "<p>text area testing $counter<br /></p>";
$contents[] = array('example.url', 'sampleurl' . $counter);
$contents[] = [-31.9489873, 115.8382036]; // Latlong.
$contents[] = "Filename{$counter}.pdf"; // File - filename.
$contents[] = array("Cat{$counter}.jpg", 'Cat' . $counter); // Picture - filename with alt text.
$count = 0;
$fieldcontents = array();
$fields = $DB->get_records('data_fields', array('dataid' => $datamodule->id), 'id');
foreach ($fields as $fieldrecord) {
$fieldcontents[$fieldrecord->id] = $contents[$count++];
}
$tags = ['Cats', 'mice' . $counter];
return $this->get_generator()->create_entry($datamodule, $fieldcontents, 0, $tags);
}
/**
* Test for provider::get_metadata().
*/
public function test_get_metadata() {
$collection = new collection('mod_data');
$newcollection = provider::get_metadata($collection);
$itemcollection = $newcollection->get_collection();
$this->assertCount(7, $itemcollection);
$table = reset($itemcollection);
$this->assertEquals('data_records', $table->get_name());
$table = next($itemcollection);
$this->assertEquals('data_content', $table->get_name());
}
/**
* Test for provider::get_contexts_for_userid().
*/
public function test_get_contexts_for_userid() {
$cm = get_coursemodule_from_instance('data', $this->datamodule->id);
$contextlist = provider::get_contexts_for_userid($this->student->id);
$this->assertCount(1, $contextlist);
$contextforuser = $contextlist->current();
$cmcontext = context_module::instance($cm->id);
$this->assertEquals($cmcontext->id, $contextforuser->id);
}
/**
* Get test privacy writer
*
* @param context $context
* @return \core_privacy\tests\request\content_writer
*/
protected function get_writer($context) {
return \core_privacy\local\request\writer::with_context($context);
}
/**
* Test for provider::export_user_data().
*/
public function test_export_for_context() {
global $DB;
$cm = get_coursemodule_from_instance('data', $this->datamodule->id);
$cmcontext = context_module::instance($cm->id);
$records = $DB->get_records_select('data_records', 'userid = :userid ORDER BY id', ['userid' => $this->student->id]);
$record = reset($records);
$contents = $DB->get_records('data_content', ['recordid' => $record->id]);
// Export all of the data for the context.
$this->export_context_data_for_user($this->student->id, $cmcontext, 'mod_data');
$writer = $this->get_writer($cmcontext);
$data = $writer->get_data([$record->id]);
$this->assertNotEmpty($data);
foreach ($contents as $content) {
$data = $writer->get_data([$record->id, $content->id]);
$this->assertNotEmpty($data);
$hasfile = in_array($data->field['type'], ['file', 'picture']);
$this->assertEquals($hasfile, !empty($writer->get_files([$record->id, $content->id])));
}
$tags = $writer->get_related_data([$record->id], 'tags');
$this->assertNotEmpty($tags);
}
/**
* Test for provider::delete_data_for_all_users_in_context().
*/
public function test_delete_data_for_all_users_in_context() {
$cm = get_coursemodule_from_instance('data', $this->datamodule->id);
$cmcontext = context_module::instance($cm->id);
provider::delete_data_for_all_users_in_context($cmcontext);
$appctxt = new \core_privacy\local\request\approved_contextlist($this->student, 'mod_data', [$cmcontext->id]);
provider::export_user_data($appctxt);
$this->assertFalse($this->get_writer($cmcontext)->has_any_data());
}
/**
* Test for provider::delete_data_for_user().
*/
public function test_delete_data_for_user() {
$cm = get_coursemodule_from_instance('data', $this->datamodule->id);
$cmcontext = context_module::instance($cm->id);
$appctxt = new \core_privacy\local\request\approved_contextlist($this->student, 'mod_data', [$cmcontext->id]);
provider::delete_data_for_user($appctxt);
provider::export_user_data($appctxt);
$this->assertFalse($this->get_writer($cmcontext)->has_any_data());
}
}