MDL-72983 tool_dataprivacy: respect capability to export own data.
[moodle.git] / admin / tool / dataprivacy / lib.php
blob0ef70eb8880e530feca488ccdf1195626da4e810
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 /**
18 * Data privacy plugin library
19 * @package tool_dataprivacy
20 * @copyright 2018 onwards Jun Pataleta
21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 use core_user\output\myprofile\tree;
26 defined('MOODLE_INTERNAL') || die();
28 /**
29 * Add nodes to myprofile page.
31 * @param tree $tree Tree object
32 * @param stdClass $user User object
33 * @param bool $iscurrentuser
34 * @param stdClass $course Course object
35 * @return bool
36 * @throws coding_exception
37 * @throws dml_exception
38 * @throws moodle_exception
40 function tool_dataprivacy_myprofile_navigation(tree $tree, $user, $iscurrentuser, $course) {
41 global $PAGE, $USER;
43 // Get the Privacy and policies category.
44 if (!array_key_exists('privacyandpolicies', $tree->__get('categories'))) {
45 // Create the category.
46 $categoryname = get_string('privacyandpolicies', 'admin');
47 $category = new core_user\output\myprofile\category('privacyandpolicies', $categoryname, 'contact');
48 $tree->add_category($category);
49 } else {
50 // Get the existing category.
51 $category = $tree->__get('categories')['privacyandpolicies'];
54 // Contact data protection officer link.
55 if (\tool_dataprivacy\api::can_contact_dpo() && $iscurrentuser) {
56 $renderer = $PAGE->get_renderer('tool_dataprivacy');
57 $content = $renderer->render_contact_dpo_link();
58 $node = new core_user\output\myprofile\node('privacyandpolicies', 'contactdpo', null, null, null, $content);
59 $category->add_node($node);
61 // Require our Javascript module to handle contact DPO interaction.
62 $PAGE->requires->js_call_amd('tool_dataprivacy/contactdpo', 'init');
64 $url = new moodle_url('/admin/tool/dataprivacy/mydatarequests.php');
65 $node = new core_user\output\myprofile\node('privacyandpolicies', 'datarequests',
66 get_string('datarequests', 'tool_dataprivacy'), null, $url);
67 $category->add_node($node);
69 // Check if the user has an ongoing data export request.
70 $hasexportrequest = \tool_dataprivacy\api::has_ongoing_request($user->id, \tool_dataprivacy\api::DATAREQUEST_TYPE_EXPORT);
71 // Show data export link only if the user doesn't have an ongoing data export request and has permission
72 // to download own data.
73 if (!$hasexportrequest && \tool_dataprivacy\api::can_create_data_download_request_for_self()) {
74 $exportparams = ['type' => \tool_dataprivacy\api::DATAREQUEST_TYPE_EXPORT];
75 $exporturl = new moodle_url('/admin/tool/dataprivacy/createdatarequest.php', $exportparams);
76 $exportnode = new core_user\output\myprofile\node('privacyandpolicies', 'requestdataexport',
77 get_string('requesttypeexport', 'tool_dataprivacy'), null, $exporturl);
78 $category->add_node($exportnode);
81 // Check if the user has an ongoing data deletion request.
82 $hasdeleterequest = \tool_dataprivacy\api::has_ongoing_request($user->id, \tool_dataprivacy\api::DATAREQUEST_TYPE_DELETE);
83 // Show data deletion link only if the user doesn't have an ongoing data deletion request and has permission
84 // to create data deletion request.
85 if (!$hasdeleterequest && \tool_dataprivacy\api::can_create_data_deletion_request_for_self()) {
86 $deleteparams = ['type' => \tool_dataprivacy\api::DATAREQUEST_TYPE_DELETE];
87 $deleteurl = new moodle_url('/admin/tool/dataprivacy/createdatarequest.php', $deleteparams);
88 $deletenode = new core_user\output\myprofile\node('privacyandpolicies', 'requestdatadeletion',
89 get_string('deletemyaccount', 'tool_dataprivacy'), null, $deleteurl);
90 $category->add_node($deletenode);
94 // A returned 0 means that the setting was set and disabled, false means that there is no value for the provided setting.
95 $showsummary = get_config('tool_dataprivacy', 'showdataretentionsummary');
96 if ($showsummary === false) {
97 // This means that no value is stored in db. We use the default value in this case.
98 $showsummary = true;
101 if ($showsummary && $iscurrentuser) {
102 $summaryurl = new moodle_url('/admin/tool/dataprivacy/summary.php');
103 $summarynode = new core_user\output\myprofile\node('privacyandpolicies', 'retentionsummary',
104 get_string('dataretentionsummary', 'tool_dataprivacy'), null, $summaryurl);
105 $category->add_node($summarynode);
108 // Add the Privacy category to the tree if it's not empty and it doesn't exist.
109 $nodes = $category->nodes;
110 if (!empty($nodes)) {
111 if (!array_key_exists('privacyandpolicies', $tree->__get('categories'))) {
112 $tree->add_category($category);
114 return true;
117 return false;
121 * Callback to add footer elements.
123 * @return string HTML footer content
125 function tool_dataprivacy_standard_footer_html() {
126 $output = '';
128 // A returned 0 means that the setting was set and disabled, false means that there is no value for the provided setting.
129 $showsummary = get_config('tool_dataprivacy', 'showdataretentionsummary');
130 if ($showsummary === false) {
131 // This means that no value is stored in db. We use the default value in this case.
132 $showsummary = true;
135 if ($showsummary) {
136 $url = new moodle_url('/admin/tool/dataprivacy/summary.php');
137 $output = html_writer::link($url, get_string('dataretentionsummary', 'tool_dataprivacy'));
138 $output = html_writer::div($output, 'tool_dataprivacy');
140 return $output;
144 * Fragment to add a new purpose.
146 * @param array $args The fragment arguments.
147 * @return string The rendered mform fragment.
149 function tool_dataprivacy_output_fragment_addpurpose_form($args) {
151 $formdata = [];
152 if (!empty($args['jsonformdata'])) {
153 $serialiseddata = json_decode($args['jsonformdata']);
154 parse_str($serialiseddata, $formdata);
157 $persistent = new \tool_dataprivacy\purpose();
158 $mform = new \tool_dataprivacy\form\purpose(null, ['persistent' => $persistent],
159 'post', '', null, true, $formdata);
161 if (!empty($args['jsonformdata'])) {
162 // Show errors if data was received.
163 $mform->is_validated();
166 return $mform->render();
170 * Fragment to add a new category.
172 * @param array $args The fragment arguments.
173 * @return string The rendered mform fragment.
175 function tool_dataprivacy_output_fragment_addcategory_form($args) {
177 $formdata = [];
178 if (!empty($args['jsonformdata'])) {
179 $serialiseddata = json_decode($args['jsonformdata']);
180 parse_str($serialiseddata, $formdata);
183 $persistent = new \tool_dataprivacy\category();
184 $mform = new \tool_dataprivacy\form\category(null, ['persistent' => $persistent],
185 'post', '', null, true, $formdata);
187 if (!empty($args['jsonformdata'])) {
188 // Show errors if data was received.
189 $mform->is_validated();
192 return $mform->render();
196 * Fragment to edit a context purpose and category.
198 * @param array $args The fragment arguments.
199 * @return string The rendered mform fragment.
201 function tool_dataprivacy_output_fragment_context_form($args) {
202 global $PAGE;
204 $contextid = $args[0];
206 $context = \context_helper::instance_by_id($contextid);
207 $customdata = \tool_dataprivacy\form\context_instance::get_context_instance_customdata($context);
209 if (!empty($customdata['purposeretentionperiods'])) {
210 $PAGE->requires->js_call_amd('tool_dataprivacy/effective_retention_period', 'init',
211 [$customdata['purposeretentionperiods']]);
213 $mform = new \tool_dataprivacy\form\context_instance(null, $customdata);
214 return $mform->render();
218 * Fragment to edit a contextlevel purpose and category.
220 * @param array $args The fragment arguments.
221 * @return string The rendered mform fragment.
223 function tool_dataprivacy_output_fragment_contextlevel_form($args) {
224 global $PAGE;
226 $contextlevel = $args[0];
227 $customdata = \tool_dataprivacy\form\contextlevel::get_contextlevel_customdata($contextlevel);
229 if (!empty($customdata['purposeretentionperiods'])) {
230 $PAGE->requires->js_call_amd('tool_dataprivacy/effective_retention_period', 'init',
231 [$customdata['purposeretentionperiods']]);
234 $mform = new \tool_dataprivacy\form\contextlevel(null, $customdata);
235 return $mform->render();
239 * Serves any files associated with the data privacy settings.
241 * @param stdClass $course Course object
242 * @param stdClass $cm Course module object
243 * @param context $context Context
244 * @param string $filearea File area for data privacy
245 * @param array $args Arguments
246 * @param bool $forcedownload If we are forcing the download
247 * @param array $options More options
248 * @return bool Returns false if we don't find a file.
250 function tool_dataprivacy_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = array()) {
251 if ($context->contextlevel == CONTEXT_USER) {
252 // Make sure the user is logged in.
253 require_login(null, false);
255 // Get the data request ID. This should be the first element of the $args array.
256 $itemid = $args[0];
257 // Fetch the data request object. An invalid ID will throw an exception.
258 $datarequest = new \tool_dataprivacy\data_request($itemid);
260 // Check if user is allowed to download it.
261 if (!\tool_dataprivacy\api::can_download_data_request_for_user($context->instanceid, $datarequest->get('requestedby'))) {
262 return false;
265 // Make the file unavailable if it has expired.
266 if (\tool_dataprivacy\data_request::is_expired($datarequest)) {
267 send_file_not_found();
270 // All good. Serve the exported data.
271 $fs = get_file_storage();
272 $relativepath = implode('/', $args);
273 $fullpath = "/$context->id/tool_dataprivacy/$filearea/$relativepath";
274 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
275 return false;
277 send_stored_file($file, 0, 0, $forcedownload, $options);
278 } else {
279 send_file_not_found();