See Release Notes
Long Term Support Release
<?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/>. /** * Data privacy plugin library * @package tool_dataprivacy * @copyright 2018 onwards Jun Pataleta * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ use core_user\output\myprofile\tree; defined('MOODLE_INTERNAL') || die(); /** * Add nodes to myprofile page. * * @param tree $tree Tree object * @param stdClass $user User object * @param bool $iscurrentuser * @param stdClass $course Course object * @return bool * @throws coding_exception * @throws dml_exception * @throws moodle_exception */ function tool_dataprivacy_myprofile_navigation(tree $tree, $user, $iscurrentuser, $course) { global $PAGE, $USER; // Get the Privacy and policies category. if (!array_key_exists('privacyandpolicies', $tree->__get('categories'))) { // Create the category. $categoryname = get_string('privacyandpolicies', 'admin'); $category = new core_user\output\myprofile\category('privacyandpolicies', $categoryname, 'contact'); $tree->add_category($category); } else { // Get the existing category. $category = $tree->__get('categories')['privacyandpolicies']; } // Contact data protection officer link. if (\tool_dataprivacy\api::can_contact_dpo() && $iscurrentuser) { $renderer = $PAGE->get_renderer('tool_dataprivacy');< $content = $renderer->render_contact_dpo_link($USER->email);> $content = $renderer->render_contact_dpo_link();$node = new core_user\output\myprofile\node('privacyandpolicies', 'contactdpo', null, null, null, $content); $category->add_node($node);< $PAGE->requires->js_call_amd('tool_dataprivacy/myrequestactions', 'init');> > // Require our Javascript module to handle contact DPO interaction. > $PAGE->requires->js_call_amd('tool_dataprivacy/contactdpo', 'init');$url = new moodle_url('/admin/tool/dataprivacy/mydatarequests.php'); $node = new core_user\output\myprofile\node('privacyandpolicies', 'datarequests', get_string('datarequests', 'tool_dataprivacy'), null, $url); $category->add_node($node); // Check if the user has an ongoing data export request. $hasexportrequest = \tool_dataprivacy\api::has_ongoing_request($user->id, \tool_dataprivacy\api::DATAREQUEST_TYPE_EXPORT);< // Show data export link only if the user doesn't have an ongoing data export request. < if (!$hasexportrequest) {> // Show data export link only if the user doesn't have an ongoing data export request and has permission > // to download own data. > if (!$hasexportrequest && \tool_dataprivacy\api::can_create_data_download_request_for_self()) {$exportparams = ['type' => \tool_dataprivacy\api::DATAREQUEST_TYPE_EXPORT]; $exporturl = new moodle_url('/admin/tool/dataprivacy/createdatarequest.php', $exportparams); $exportnode = new core_user\output\myprofile\node('privacyandpolicies', 'requestdataexport', get_string('requesttypeexport', 'tool_dataprivacy'), null, $exporturl); $category->add_node($exportnode); } // Check if the user has an ongoing data deletion request. $hasdeleterequest = \tool_dataprivacy\api::has_ongoing_request($user->id, \tool_dataprivacy\api::DATAREQUEST_TYPE_DELETE); // Show data deletion link only if the user doesn't have an ongoing data deletion request and has permission // to create data deletion request. if (!$hasdeleterequest && \tool_dataprivacy\api::can_create_data_deletion_request_for_self()) { $deleteparams = ['type' => \tool_dataprivacy\api::DATAREQUEST_TYPE_DELETE]; $deleteurl = new moodle_url('/admin/tool/dataprivacy/createdatarequest.php', $deleteparams); $deletenode = new core_user\output\myprofile\node('privacyandpolicies', 'requestdatadeletion', get_string('deletemyaccount', 'tool_dataprivacy'), null, $deleteurl); $category->add_node($deletenode); } } // A returned 0 means that the setting was set and disabled, false means that there is no value for the provided setting. $showsummary = get_config('tool_dataprivacy', 'showdataretentionsummary'); if ($showsummary === false) { // This means that no value is stored in db. We use the default value in this case. $showsummary = true; } if ($showsummary && $iscurrentuser) { $summaryurl = new moodle_url('/admin/tool/dataprivacy/summary.php'); $summarynode = new core_user\output\myprofile\node('privacyandpolicies', 'retentionsummary', get_string('dataretentionsummary', 'tool_dataprivacy'), null, $summaryurl); $category->add_node($summarynode); } // Add the Privacy category to the tree if it's not empty and it doesn't exist. $nodes = $category->nodes; if (!empty($nodes)) { if (!array_key_exists('privacyandpolicies', $tree->__get('categories'))) { $tree->add_category($category); } return true; } return false; } /** * Callback to add footer elements. * * @return string HTML footer content */ function tool_dataprivacy_standard_footer_html() { $output = ''; // A returned 0 means that the setting was set and disabled, false means that there is no value for the provided setting. $showsummary = get_config('tool_dataprivacy', 'showdataretentionsummary'); if ($showsummary === false) { // This means that no value is stored in db. We use the default value in this case. $showsummary = true; } if ($showsummary) { $url = new moodle_url('/admin/tool/dataprivacy/summary.php'); $output = html_writer::link($url, get_string('dataretentionsummary', 'tool_dataprivacy')); $output = html_writer::div($output, 'tool_dataprivacy'); } return $output; } /** * Fragment to add a new purpose. * * @param array $args The fragment arguments. * @return string The rendered mform fragment. */ function tool_dataprivacy_output_fragment_addpurpose_form($args) { $formdata = []; if (!empty($args['jsonformdata'])) { $serialiseddata = json_decode($args['jsonformdata']); parse_str($serialiseddata, $formdata); } $persistent = new \tool_dataprivacy\purpose(); $mform = new \tool_dataprivacy\form\purpose(null, ['persistent' => $persistent], 'post', '', null, true, $formdata); if (!empty($args['jsonformdata'])) { // Show errors if data was received. $mform->is_validated(); } return $mform->render(); } /** * Fragment to add a new category. * * @param array $args The fragment arguments. * @return string The rendered mform fragment. */ function tool_dataprivacy_output_fragment_addcategory_form($args) { $formdata = []; if (!empty($args['jsonformdata'])) { $serialiseddata = json_decode($args['jsonformdata']); parse_str($serialiseddata, $formdata); } $persistent = new \tool_dataprivacy\category(); $mform = new \tool_dataprivacy\form\category(null, ['persistent' => $persistent], 'post', '', null, true, $formdata); if (!empty($args['jsonformdata'])) { // Show errors if data was received. $mform->is_validated(); } return $mform->render(); } /** * Fragment to edit a context purpose and category. * * @param array $args The fragment arguments. * @return string The rendered mform fragment. */ function tool_dataprivacy_output_fragment_context_form($args) { global $PAGE; $contextid = $args[0]; $context = \context_helper::instance_by_id($contextid); $customdata = \tool_dataprivacy\form\context_instance::get_context_instance_customdata($context); if (!empty($customdata['purposeretentionperiods'])) { $PAGE->requires->js_call_amd('tool_dataprivacy/effective_retention_period', 'init', [$customdata['purposeretentionperiods']]); } $mform = new \tool_dataprivacy\form\context_instance(null, $customdata); return $mform->render(); } /** * Fragment to edit a contextlevel purpose and category. * * @param array $args The fragment arguments. * @return string The rendered mform fragment. */ function tool_dataprivacy_output_fragment_contextlevel_form($args) { global $PAGE; $contextlevel = $args[0]; $customdata = \tool_dataprivacy\form\contextlevel::get_contextlevel_customdata($contextlevel); if (!empty($customdata['purposeretentionperiods'])) { $PAGE->requires->js_call_amd('tool_dataprivacy/effective_retention_period', 'init', [$customdata['purposeretentionperiods']]); } $mform = new \tool_dataprivacy\form\contextlevel(null, $customdata); return $mform->render(); } /** * Serves any files associated with the data privacy settings. * * @param stdClass $course Course object * @param stdClass $cm Course module object * @param context $context Context * @param string $filearea File area for data privacy * @param array $args Arguments * @param bool $forcedownload If we are forcing the download * @param array $options More options * @return bool Returns false if we don't find a file. */ function tool_dataprivacy_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = array()) { if ($context->contextlevel == CONTEXT_USER) { // Make sure the user is logged in. require_login(null, false); // Get the data request ID. This should be the first element of the $args array. $itemid = $args[0]; // Fetch the data request object. An invalid ID will throw an exception. $datarequest = new \tool_dataprivacy\data_request($itemid); // Check if user is allowed to download it. if (!\tool_dataprivacy\api::can_download_data_request_for_user($context->instanceid, $datarequest->get('requestedby'))) { return false; } // Make the file unavailable if it has expired. if (\tool_dataprivacy\data_request::is_expired($datarequest)) { send_file_not_found(); } // All good. Serve the exported data. $fs = get_file_storage(); $relativepath = implode('/', $args); $fullpath = "/$context->id/tool_dataprivacy/$filearea/$relativepath"; if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { return false; } send_stored_file($file, 0, 0, $forcedownload, $options); } else { send_file_not_found(); } }