*/**/build/
node_modules/
vendor/
+admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js
admin/tool/usertours/amd/src/tour.js
auth/cas/CAS/
enrol/lti/ims-blti/
lib/amd/src/adapter.js
lib/validateurlsyntax.php
lib/amd/src/popper.js
+lib/geopattern-php/
media/player/videojs/amd/src/video-lazy.js
media/player/videojs/amd/src/Youtube-lazy.js
media/player/videojs/videojs/
theme/more/style/custom.css
node_modules/
vendor/
+admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js
admin/tool/usertours/amd/src/tour.js
auth/cas/CAS/
enrol/lti/ims-blti/
lib/amd/src/adapter.js
lib/validateurlsyntax.php
lib/amd/src/popper.js
+lib/geopattern-php/
media/player/videojs/amd/src/video-lazy.js
media/player/videojs/amd/src/Youtube-lazy.js
media/player/videojs/videojs/
--- /dev/null
+<?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 core_register.
+ *
+ * @package core_register
+ * @copyright 2018 Carlos Escobedo <carlos@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace core_register\privacy;
+defined('MOODLE_INTERNAL') || die();
+/**
+ * Privacy Subsystem for core_register implementing null_provider.
+ *
+ * @copyright 2018 Carlos Escobedo <carlos@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+ /**
+ * Get the language string identifier with the component's language
+ * file to explain why this plugin stores no data.
+ *
+ * @return string
+ */
+ public static function get_reason() : string {
+ return 'privacy:metadata';
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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 tool_analytics.
+ *
+ * @package tool_analytics
+ * @copyright 2018 Zig Tan <zig@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_analytics\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy Subsystem for tool_analytics implementing null_provider.
+ *
+ * @copyright 2018 Zig Tan <zig@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+
+ /**
+ * Get the language string identifier with the component's language
+ * file to explain why this plugin stores no data.
+ *
+ * @return string
+ */
+ public static function get_reason() : string {
+ return 'privacy:metadata';
+ }
+}
\ No newline at end of file
$string['viewlog'] = 'Log';
$string['weeksenddateautomaticallyset'] = 'End date automatically set based on start date and the number of sections';
$string['weeksenddatedefault'] = 'End date automatically calculated from the course start date.';
+$string['privacy:metadata'] = 'The Analytic models plugin does not store any personal data.';
--- /dev/null
+<?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 tool_assignmentupgrade.
+ *
+ * @package tool_assignmentupgrade
+ * @copyright 2018 Zig Tan <zig@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_assignmentupgrade\privacy;
+
+use core_privacy\local\metadata\collection;
+use core_privacy\local\request\writer;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy Subsystem for tool_assignmentupgrade implementing metadata, plugin, and user_preference providers.
+ *
+ * @copyright 2018 Zig Tan <zig@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements
+ \core_privacy\local\metadata\provider,
+ \core_privacy\local\request\user_preference_provider {
+
+ /**
+ * Returns meta data about this system.
+ *
+ * @param collection $collection The initialised collection to add items to.
+ * @return collection A listing of user data stored through this system.
+ */
+ public static function get_metadata(collection $collection) : collection {
+ $collection->add_user_preference(
+ 'tool_assignmentupgrade_perpage',
+ 'privacy:metadata:preference:perpage'
+ );
+ return $collection;
+ }
+
+ /**
+ * Export all user preferences for the plugin.
+ *
+ * @param int $userid The userid of the user whose data is to be exported.
+ */
+ public static function export_user_preferences(int $userid) {
+ $perpage = get_user_preferences('tool_assignmentupgrade_perpage', null, $userid);
+ if ($perpage !== null) {
+ writer::export_user_preference(
+ 'tool_assignmentupgrade',
+ 'perpage',
+ $perpage,
+ get_string('privacy:metadata:preference:perpage', 'tool_assignmentupgrade')
+ );
+ }
+ }
+
+}
$string['upgradeprogress'] = 'Upgrade assignment {$a->current} of {$a->total}';
$string['upgradesingle'] = 'Upgrade single assignment';
$string['viewcourse'] = 'View the course with the converted assignment';
+$string['privacy:metadata:preference:perpage'] = 'The assignment upgrade records per page preference set for the user.';
--- /dev/null
+<?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 tests for tool_assignmentupgrade.
+ *
+ * @package tool_assignmentupgrade
+ * @category test
+ * @copyright 2018 Zig Tan <zig@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+use \core_privacy\tests\provider_testcase;
+use \core_privacy\local\request\writer;
+use \tool_assignmentupgrade\privacy\provider;
+
+/**
+ * Unit tests for tool_assignmentupgrade/classes/privacy/policy
+ *
+ * @copyright 2018 Zig Tan <zig@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_assignmentupgrade_privacy_testcase extends provider_testcase {
+
+ /**
+ * Overriding setUp() function to always reset after tests.
+ */
+ public function setUp() {
+ $this->resetAfterTest(true);
+ }
+
+ /**
+ * Test for provider::test_export_user_preferences().
+ */
+ public function test_export_user_preferences() {
+ // Test setup.
+ $user = $this->getDataGenerator()->create_user();
+ $this->setUser($user);
+
+ // Add a user home page preference for the User.
+ set_user_preference('tool_assignmentupgrade_perpage', '100', $user);
+
+ // Test the user preference exists.
+ $params = [
+ 'userid' => $user->id,
+ 'name' => 'tool_assignmentupgrade_perpage'
+ ];
+
+ // Test the user preferences export contains 1 user preference record for the User.
+ provider::export_user_preferences($user->id);
+ $contextuser = context_user::instance($user->id);
+ $writer = writer::with_context($contextuser);
+ $this->assertTrue($writer->has_any_data());
+
+ $exportedpreferences = $writer->get_user_preferences('tool_assignmentupgrade');
+ $this->assertCount(1, (array) $exportedpreferences);
+ $this->assertEquals('100', $exportedpreferences->perpage->value);
+ }
+
+}
--- /dev/null
+<?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 tool_availabilityconditions.
+ *
+ * @package tool_availabilityconditions
+ * @copyright 2018 Zig Tan <zig@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_availabilityconditions\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy Subsystem for tool_availabilityconditions implementing null_provider.
+ *
+ * @copyright 2018 Zig Tan <zig@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+
+ /**
+ * Get the language string identifier with the component's language
+ * file to explain why this plugin stores no data.
+ *
+ * @return string
+ */
+ public static function get_reason() : string {
+ return 'privacy:metadata';
+ }
+}
\ No newline at end of file
$string['manageplugins'] = 'Manage restrictions';
$string['pluginname'] = 'Availability condition management';
+$string['privacy:metadata'] = 'The Availability condition management plugin does not store any personal data.';
--- /dev/null
+<?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 tool_behat.
+ *
+ * @package tool_behat
+ * @copyright 2018 Zig Tan <zig@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_behat\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy Subsystem for tool_behat implementing null_provider.
+ *
+ * @copyright 2018 Zig Tan <zig@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+
+ /**
+ * Get the language string identifier with the component's language
+ * file to explain why this plugin stores no data.
+ *
+ * @return string
+ */
+ public static function get_reason() : string {
+ return 'privacy:metadata';
+ }
+}
\ No newline at end of file
<li>$CFG->behat_dataroot, $CFG->behat_prefix and $CFG->behat_wwwroot are set in config.php with different values from $CFG->dataroot, $CFG->prefix and $CFG->wwwroot.</li>
<li>You ran "{$a->behatinit}" from your Moodle root directory.</li>
<li>Dependencies are installed in vendor/ and {$a->behatcommand} file has execution permissions.</li></ul>';
+$string['privacy:metadata'] = 'The Acceptance testing plugin does not store any personal data.';
--- /dev/null
+<?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 tool_capability.
+ *
+ * @package tool_capability
+ * @copyright 2018 Zig Tan <zig@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_capability\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy Subsystem for tool_capability implementing null_provider.
+ *
+ * @copyright 2018 Zig Tan <zig@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+
+ /**
+ * Get the language string identifier with the component's language
+ * file to explain why this plugin stores no data.
+ *
+ * @return string
+ */
+ public static function get_reason() : string {
+ return 'privacy:metadata';
+ }
+}
\ No newline at end of file
$string['reportforcapability'] = 'Report for capability \'{$a}\'';
$string['reportsettings'] = 'Report settings';
$string['roleslabel'] = 'Roles:';
+$string['privacy:metadata'] = 'The Capability overview plugin does not store any personal data.';
'idnumber' => $data->cohortidnumber,
'description' => $data->cohortdescription,
'visible' => $data->cohortvisible,
- 'name' => $data->cohortname
+ 'name' => $data->cohortname,
+ 'theme' => $data->cohorttheme
);
$context = context_helper::instance_by_id($data->cohortcontextid);
protected function get_sql_and_params($count = false) {
$fields = 'uca.id, uca.cohortid, uca.userid, uca.roleid, ';
$fields .= 'c.name as cohortname, c.idnumber as cohortidnumber, c.contextid as cohortcontextid, ';
- $fields .= 'c.visible as cohortvisible, c.description as cohortdescription, ';
+ $fields .= 'c.visible as cohortvisible, c.description as cohortdescription, c.theme as cohorttheme, ';
// Add extra user fields that we need for the graded user.
$extrafields = get_extra_user_fields($this->context);
* idnumber cohort idnumber field
* description cohort description field
* visible cohort visible field
+ * theme cohort theme field
Example context (json):
{ "id": "1",
"name": "Cohort 1",
"visible": true,
"idnumber": "014",
- "description": "Some users"
+ "description": "Some users",
+ "theme": "clean"
}
}}
{{> tool_lp/form-cohort-selector-suggestion }}
--- /dev/null
+<?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 tool_customlang.
+ *
+ * @package tool_customlang
+ * @copyright 2018 Zig Tan <zig@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_customlang\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy Subsystem for tool_customlang implementing null_provider.
+ *
+ * @copyright 2018 Zig Tan <zig@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+
+ /**
+ * Get the language string identifier with the component's language
+ * file to explain why this plugin stores no data.
+ *
+ * @return string
+ */
+ public static function get_reason() : string {
+ return 'privacy:metadata';
+ }
+}
\ No newline at end of file
$string['pluginname'] = 'Language customisation';
$string['savecheckin'] = 'Save changes to the language pack';
$string['savecontinue'] = 'Apply changes and continue editing';
+$string['privacy:metadata'] = 'The Language customisation plugin does not store any personal data.';
--- /dev/null
+// 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/>.
+
+/**
+ * Module to add categories.
+ *
+ * @module tool_dataprivacy/add_category
+ * @package tool_dataprivacy
+ * @copyright 2018 David Monllao
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery', 'core/str', 'core/ajax', 'core/notification', 'core/modal_factory', 'core/modal_events', 'core/fragment'],
+ function($, Str, Ajax, Notification, ModalFactory, ModalEvents, Fragment) {
+
+ var SELECTORS = {
+ CATEGORY_LINK: '[data-add-element="category"]',
+ };
+
+ var AddCategory = function(contextId) {
+ this.contextId = contextId;
+
+ var stringKeys = [
+ {
+ key: 'addcategory',
+ component: 'tool_dataprivacy'
+ },
+ {
+ key: 'save',
+ component: 'admin'
+ }
+ ];
+ this.strings = Str.get_strings(stringKeys);
+
+ this.registerEventListeners();
+ };
+
+ /**
+ * @var {int} contextId
+ * @private
+ */
+ AddCategory.prototype.contextId = 0;
+
+ /**
+ * @var {Promise}
+ * @private
+ */
+ AddCategory.prototype.strings = 0;
+
+ AddCategory.prototype.registerEventListeners = function() {
+
+ var trigger = $(SELECTORS.CATEGORY_LINK);
+ trigger.on('click', function() {
+ return this.strings.then(function(strings) {
+ ModalFactory.create({
+ type: ModalFactory.types.SAVE_CANCEL,
+ title: strings[0],
+ body: '',
+ }, trigger).done(function(modal) {
+ this.setupFormModal(modal, strings[1]);
+ }.bind(this));
+ }.bind(this))
+ .fail(Notification.exception);
+ }.bind(this));
+
+ };
+
+ /**
+ * @method getBody
+ * @param {Object} formdata
+ * @private
+ * @return {Promise}
+ */
+ AddCategory.prototype.getBody = function(formdata) {
+
+ var params = null;
+ if (typeof formdata !== "undefined") {
+ params = {jsonformdata: JSON.stringify(formdata)};
+ }
+ // Get the content of the modal.
+ return Fragment.loadFragment('tool_dataprivacy', 'addcategory_form', this.contextId, params);
+ };
+
+ AddCategory.prototype.setupFormModal = function(modal, saveText) {
+ modal.setLarge();
+
+ modal.setSaveButtonText(saveText);
+
+ // We want to reset the form every time it is opened.
+ modal.getRoot().on(ModalEvents.hidden, this.destroy.bind(this));
+
+ modal.setBody(this.getBody());
+
+ // We catch the modal save event, and use it to submit the form inside the modal.
+ // Triggering a form submission will give JS validation scripts a chance to check for errors.
+ modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));
+ // We also catch the form submit event and use it to submit the form with ajax.
+ modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));
+
+ this.modal = modal;
+
+ modal.show();
+ };
+
+ /**
+ * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.
+ *
+ * @method submitForm
+ * @param {Event} e Form submission event.
+ * @private
+ */
+ AddCategory.prototype.submitForm = function(e) {
+ e.preventDefault();
+ this.modal.getRoot().find('form').submit();
+ };
+
+ AddCategory.prototype.submitFormAjax = function(e) {
+ // We don't want to do a real form submission.
+ e.preventDefault();
+
+ // Convert all the form elements values to a serialised string.
+ var formData = this.modal.getRoot().find('form').serialize();
+
+ Ajax.call([{
+ methodname: 'tool_dataprivacy_create_category_form',
+ args: {jsonformdata: JSON.stringify(formData)},
+ done: function(data) {
+ if (data.validationerrors) {
+ this.modal.setBody(this.getBody(formData));
+ } else {
+ this.close();
+ }
+ }.bind(this),
+ fail: Notification.exception
+ }]);
+ };
+
+ AddCategory.prototype.close = function() {
+ this.destroy();
+ document.location.reload();
+ };
+
+ AddCategory.prototype.destroy = function() {
+ Y.use('moodle-core-formchangechecker', function() {
+ M.core_formchangechecker.reset_form_dirty_state();
+ });
+ this.modal.destroy();
+ };
+
+ AddCategory.prototype.removeListeners = function() {
+ $(SELECTORS.CATEGORY_LINK).off('click');
+ };
+
+ return /** @alias module:tool_dataprivacy/add_category */ {
+ getInstance: function(contextId) {
+ return new AddCategory(contextId);
+ }
+ };
+ }
+);
+
--- /dev/null
+// 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/>.
+
+/**
+ * Module to add purposes.
+ *
+ * @module tool_dataprivacy/add_purpose
+ * @package tool_dataprivacy
+ * @copyright 2018 David Monllao
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery', 'core/str', 'core/ajax', 'core/notification', 'core/modal_factory', 'core/modal_events', 'core/fragment'],
+ function($, Str, Ajax, Notification, ModalFactory, ModalEvents, Fragment) {
+
+ var SELECTORS = {
+ PURPOSE_LINK: '[data-add-element="purpose"]',
+ };
+
+ var AddPurpose = function(contextId) {
+ this.contextId = contextId;
+
+ var stringKeys = [
+ {
+ key: 'addpurpose',
+ component: 'tool_dataprivacy'
+ },
+ {
+ key: 'save',
+ component: 'admin'
+ }
+ ];
+ this.strings = Str.get_strings(stringKeys);
+
+ this.registerEventListeners();
+ };
+
+ /**
+ * @var {int} contextId
+ * @private
+ */
+ AddPurpose.prototype.contextId = 0;
+
+ /**
+ * @var {Promise}
+ * @private
+ */
+ AddPurpose.prototype.strings = 0;
+
+ AddPurpose.prototype.registerEventListeners = function() {
+
+ var trigger = $(SELECTORS.PURPOSE_LINK);
+ trigger.on('click', function() {
+ return this.strings.then(function(strings) {
+ ModalFactory.create({
+ type: ModalFactory.types.SAVE_CANCEL,
+ title: strings[0],
+ body: '',
+ }, trigger).done(function(modal) {
+ this.setupFormModal(modal, strings[1]);
+ }.bind(this));
+ }.bind(this))
+ .fail(Notification.exception);
+ }.bind(this));
+
+ };
+
+ /**
+ * @method getBody
+ * @param {Object} formdata
+ * @private
+ * @return {Promise}
+ */
+ AddPurpose.prototype.getBody = function(formdata) {
+
+ var params = null;
+ if (typeof formdata !== "undefined") {
+ params = {jsonformdata: JSON.stringify(formdata)};
+ }
+ // Get the content of the modal.
+ return Fragment.loadFragment('tool_dataprivacy', 'addpurpose_form', this.contextId, params);
+ };
+
+ AddPurpose.prototype.setupFormModal = function(modal, saveText) {
+ modal.setLarge();
+
+ modal.setSaveButtonText(saveText);
+
+ // We want to reset the form every time it is opened.
+ modal.getRoot().on(ModalEvents.hidden, this.destroy.bind(this));
+
+ modal.setBody(this.getBody());
+
+ // We catch the modal save event, and use it to submit the form inside the modal.
+ // Triggering a form submission will give JS validation scripts a chance to check for errors.
+ modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));
+ // We also catch the form submit event and use it to submit the form with ajax.
+ modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));
+
+ this.modal = modal;
+
+ modal.show();
+ };
+
+ /**
+ * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.
+ *
+ * @method submitForm
+ * @param {Event} e Form submission event.
+ * @private
+ */
+ AddPurpose.prototype.submitForm = function(e) {
+ e.preventDefault();
+ this.modal.getRoot().find('form').submit();
+ };
+
+ AddPurpose.prototype.submitFormAjax = function(e) {
+ // We don't want to do a real form submission.
+ e.preventDefault();
+
+ // Convert all the form elements values to a serialised string.
+ var formData = this.modal.getRoot().find('form').serialize();
+
+ Ajax.call([{
+ methodname: 'tool_dataprivacy_create_purpose_form',
+ args: {jsonformdata: JSON.stringify(formData)},
+ done: function(data) {
+ if (data.validationerrors) {
+ this.modal.setBody(this.getBody(formData));
+ } else {
+ this.close();
+ }
+ }.bind(this),
+
+ fail: Notification.exception
+ }]);
+ };
+
+ AddPurpose.prototype.close = function() {
+ this.destroy();
+ document.location.reload();
+ };
+
+ AddPurpose.prototype.destroy = function() {
+ Y.use('moodle-core-formchangechecker', function() {
+ M.core_formchangechecker.reset_form_dirty_state();
+ });
+ this.modal.destroy();
+ };
+
+ AddPurpose.prototype.removeListeners = function() {
+ $(SELECTORS.PURPOSE_LINK).off('click');
+ };
+
+ return /** @alias module:tool_dataprivacy/add_purpose */ {
+ getInstance: function(contextId) {
+ return new AddPurpose(contextId);
+ }
+ };
+ }
+);
+
--- /dev/null
+// 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/>.
+
+/**
+ * AMD module for categories actions.
+ *
+ * @module tool_dataprivacy/categoriesactions
+ * @package tool_dataprivacy
+ * @copyright 2018 David Monllao
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([
+ 'jquery',
+ 'core/ajax',
+ 'core/notification',
+ 'core/str',
+ 'core/modal_factory',
+ 'core/modal_events'],
+function($, Ajax, Notification, Str, ModalFactory, ModalEvents) {
+
+ /**
+ * List of action selectors.
+ *
+ * @type {{DELETE: string}}
+ */
+ var ACTIONS = {
+ DELETE: '[data-action="deletecategory"]',
+ };
+
+ /**
+ * CategoriesActions class.
+ */
+ var CategoriesActions = function() {
+ this.registerEvents();
+ };
+
+ /**
+ * Register event listeners.
+ */
+ CategoriesActions.prototype.registerEvents = function() {
+ $(ACTIONS.DELETE).click(function(e) {
+ e.preventDefault();
+
+ var id = $(this).data('id');
+ var categoryname = $(this).data('name');
+ var stringkeys = [
+ {
+ key: 'deletecategory',
+ component: 'tool_dataprivacy',
+ param: categoryname
+ },
+ {
+ key: 'deletecategorytext',
+ component: 'tool_dataprivacy',
+ param: categoryname
+ }
+ ];
+
+ Str.get_strings(stringkeys).then(function(langStrings) {
+ var title = langStrings[0];
+ var confirmMessage = langStrings[1];
+ return ModalFactory.create({
+ title: title,
+ body: confirmMessage,
+ type: ModalFactory.types.SAVE_CANCEL
+ }).then(function(modal) {
+ modal.setSaveButtonText(title);
+
+ // Handle save event.
+ modal.getRoot().on(ModalEvents.save, function() {
+
+ var request = {
+ methodname: 'tool_dataprivacy_delete_category',
+ args: {'id': id}
+ };
+
+ Ajax.call([request])[0].done(function(data) {
+ if (data.result) {
+ $('tr[data-categoryid="' + id + '"]').remove();
+ } else {
+ Notification.addNotification({
+ message: data.warnings[0].message,
+ type: 'error'
+ });
+ }
+ }).fail(Notification.exception);
+ });
+
+ // Handle hidden event.
+ modal.getRoot().on(ModalEvents.hidden, function() {
+ // Destroy when hidden.
+ modal.destroy();
+ });
+
+ return modal;
+ });
+ }).done(function(modal) {
+ modal.show();
+
+ }).fail(Notification.exception);
+ });
+ };
+
+ return /** @alias module:tool_dataprivacy/categoriesactions */ {
+ // Public variables and functions.
+
+ /**
+ * Initialise the module.
+ *
+ * @method init
+ * @return {CategoriesActions}
+ */
+ 'init': function() {
+ return new CategoriesActions();
+ }
+ };
+});
--- /dev/null
+// 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/>.
+
+/**
+ * Request actions.
+ *
+ * @module tool_dataprivacy/data_deletion
+ * @package tool_dataprivacy
+ * @copyright 2018 Jun Pataleta
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([
+ 'jquery',
+ 'core/ajax',
+ 'core/notification',
+ 'core/str',
+ 'core/modal_factory',
+ 'core/modal_events'],
+function($, Ajax, Notification, Str, ModalFactory, ModalEvents) {
+
+ /**
+ * List of action selectors.
+ *
+ * @type {{MARK_FOR_DELETION: string}}
+ * @type {{SELECT_ALL: string}}
+ */
+ var ACTIONS = {
+ MARK_FOR_DELETION: '[data-action="markfordeletion"]',
+ SELECT_ALL: '[data-action="selectall"]',
+ };
+
+ /**
+ * List of selectors.
+ *
+ * @type {{SELECTCONTEXT: string}}
+ */
+ var SELECTORS = {
+ SELECTCONTEXT: '.selectcontext',
+ };
+
+ /**
+ * DataDeletionActions class.
+ */
+ var DataDeletionActions = function() {
+ this.registerEvents();
+ };
+
+ /**
+ * Register event listeners.
+ */
+ DataDeletionActions.prototype.registerEvents = function() {
+ $(ACTIONS.MARK_FOR_DELETION).click(function(e) {
+ e.preventDefault();
+
+ var selectedIds = [];
+ $(SELECTORS.SELECTCONTEXT).each(function() {
+ var checkbox = $(this);
+ if (checkbox.is(':checked')) {
+ selectedIds.push(checkbox.val());
+ }
+ });
+ showConfirmation(selectedIds);
+ });
+
+ $(ACTIONS.SELECT_ALL).change(function(e) {
+ e.preventDefault();
+
+ var selectallnone = $(this);
+ if (selectallnone.is(':checked')) {
+ $(SELECTORS.SELECTCONTEXT).attr('checked', 'checked');
+ } else {
+ $(SELECTORS.SELECTCONTEXT).removeAttr('checked');
+ }
+ });
+ };
+
+ /**
+ * Show the confirmation dialogue.
+ *
+ * @param {Array} ids The array of expired context record IDs.
+ */
+ function showConfirmation(ids) {
+ var keys = [
+ {
+ key: 'confirm',
+ component: 'moodle'
+ },
+ {
+ key: 'confirmcontextdeletion',
+ component: 'tool_dataprivacy'
+ }
+ ];
+ var wsfunction = 'tool_dataprivacy_confirm_contexts_for_deletion';
+
+ var modalTitle = '';
+ Str.get_strings(keys).then(function(langStrings) {
+ modalTitle = langStrings[0];
+ var confirmMessage = langStrings[1];
+ return ModalFactory.create({
+ title: modalTitle,
+ body: confirmMessage,
+ type: ModalFactory.types.SAVE_CANCEL
+ });
+ }).then(function(modal) {
+ modal.setSaveButtonText(modalTitle);
+
+ // Handle save event.
+ modal.getRoot().on(ModalEvents.save, function() {
+ // Confirm the request.
+ var params = {
+ 'ids': ids
+ };
+
+ var request = {
+ methodname: wsfunction,
+ args: params
+ };
+
+ Ajax.call([request])[0].done(function(data) {
+ if (data.result) {
+ window.location.reload();
+ } else {
+ Notification.addNotification({
+ message: data.warnings[0].message,
+ type: 'error'
+ });
+ }
+ }).fail(Notification.exception);
+ });
+
+ // Handle hidden event.
+ modal.getRoot().on(ModalEvents.hidden, function() {
+ // Destroy when hidden.
+ modal.destroy();
+ });
+
+ return modal;
+ }).done(function(modal) {
+ modal.show();
+ }).fail(Notification.exception);
+ }
+
+ return DataDeletionActions;
+});
--- /dev/null
+// 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/>.
+
+/**
+ * Request actions.
+ *
+ * @module tool_dataprivacy/data_registry
+ * @package tool_dataprivacy
+ * @copyright 2018 David Monllao
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery', 'core/str', 'core/ajax', 'core/notification', 'core/templates', 'core/modal_factory',
+ 'core/modal_events', 'core/fragment', 'tool_dataprivacy/add_purpose', 'tool_dataprivacy/add_category'],
+ function($, Str, Ajax, Notification, Templates, ModalFactory, ModalEvents, Fragment, AddPurpose, AddCategory) {
+
+ var SELECTORS = {
+ TREE_NODES: '[data-context-tree-node=1]',
+ FORM_CONTAINER: '#context-form-container',
+ };
+
+ var DataRegistry = function(systemContextId, initContextLevel, initContextId) {
+ this.systemContextId = systemContextId;
+ this.currentContextLevel = initContextLevel;
+ this.currentContextId = initContextId;
+ this.init();
+ };
+
+ /**
+ * @var {int} systemContextId
+ * @private
+ */
+ DataRegistry.prototype.systemContextId = 0;
+
+ /**
+ * @var {int} currentContextLevel
+ * @private
+ */
+ DataRegistry.prototype.currentContextLevel = 0;
+
+ /**
+ * @var {int} currentContextId
+ * @private
+ */
+ DataRegistry.prototype.currentContextId = 0;
+
+ /**
+ * @var {AddPurpose} addpurpose
+ * @private
+ */
+ DataRegistry.prototype.addpurpose = null;
+
+ /**
+ * @var {AddCategory} addcategory
+ * @private
+ */
+ DataRegistry.prototype.addcategory = null;
+
+ DataRegistry.prototype.init = function() {
+ // Add purpose and category modals always at system context.
+ this.addpurpose = AddPurpose.getInstance(this.systemContextId);
+ this.addcategory = AddCategory.getInstance(this.systemContextId);
+
+ var stringKeys = [
+ {
+ key: 'changessaved',
+ component: 'moodle'
+ }, {
+ key: 'contextpurposecategorysaved',
+ component: 'tool_dataprivacy'
+ }, {
+ key: 'noblockstoload',
+ component: 'tool_dataprivacy'
+ }, {
+ key: 'noactivitiestoload',
+ component: 'tool_dataprivacy'
+ }, {
+ key: 'nocoursestoload',
+ component: 'tool_dataprivacy'
+ }
+ ];
+ this.strings = Str.get_strings(stringKeys);
+
+ this.registerEventListeners();
+
+ // Load the default context level form.
+ if (this.currentContextId) {
+ this.loadForm('context_form', [this.currentContextId], this.submitContextFormAjax.bind(this));
+ } else {
+ this.loadForm('contextlevel_form', [this.currentContextLevel], this.submitContextLevelFormAjax.bind(this));
+ }
+ };
+
+ DataRegistry.prototype.registerEventListeners = function() {
+ $(SELECTORS.TREE_NODES).on('click', function(ev) {
+ ev.preventDefault();
+
+ var trigger = $(ev.currentTarget);
+
+ // Active node.
+ $(SELECTORS.TREE_NODES).removeClass('active');
+ trigger.addClass('active');
+
+ var contextLevel = trigger.data('contextlevel');
+ var contextId = trigger.data('contextid');
+ if (contextLevel) {
+ // Context level level.
+
+ window.history.pushState({}, null, '?contextlevel=' + contextLevel);
+
+ // Remove previous add purpose and category listeners to avoid memory leaks.
+ this.addpurpose.removeListeners();
+ this.addcategory.removeListeners();
+
+ // Load the context level form.
+ this.currentContextLevel = contextLevel;
+ this.loadForm('contextlevel_form', [this.currentContextLevel], this.submitContextLevelFormAjax.bind(this));
+ } else if (contextId) {
+ // Context instance level.
+
+ window.history.pushState({}, null, '?contextid=' + contextId);
+
+ // Remove previous add purpose and category listeners to avoid memory leaks.
+ this.addpurpose.removeListeners();
+ this.addcategory.removeListeners();
+
+ // Load the context level form.
+ this.currentContextId = contextId;
+ this.loadForm('context_form', [this.currentContextId], this.submitContextFormAjax.bind(this));
+ } else {
+ // Expandable nodes.
+
+ var expandContextId = trigger.data('expandcontextid');
+ var expandElement = trigger.data('expandelement');
+ var expanded = trigger.data('expanded');
+
+ // Extra checking that there is an expandElement because we remove it after loading 0 branches.
+ if (expandElement) {
+
+ if (!expanded) {
+ if (trigger.data('loaded') || !expandContextId || !expandElement) {
+ this.expand(trigger);
+ } else {
+
+ trigger.find('> i').removeClass('fa-plus');
+ trigger.find('> i').addClass('fa-circle-o-notch fa-spin');
+ this.loadExtra(trigger, expandContextId, expandElement);
+ }
+ } else {
+ this.collapse(trigger);
+ }
+ }
+ }
+
+ }.bind(this));
+ };
+
+ DataRegistry.prototype.removeListeners = function() {
+ $(SELECTORS.TREE_NODES).off('click');
+ };
+
+ DataRegistry.prototype.loadForm = function(fragmentName, fragmentArgs, formSubmitCallback) {
+
+ this.clearForm();
+
+ var fragment = Fragment.loadFragment('tool_dataprivacy', fragmentName, this.systemContextId, fragmentArgs);
+ fragment.done(function(html, js) {
+
+ $(SELECTORS.FORM_CONTAINER).html(html);
+ Templates.runTemplateJS(js);
+
+ this.addpurpose.registerEventListeners();
+ this.addcategory.registerEventListeners();
+
+ // We also catch the form submit event and use it to submit the form with ajax.
+ $(SELECTORS.FORM_CONTAINER).on('submit', 'form', formSubmitCallback);
+
+ }.bind(this)).fail(Notification.exception);
+ };
+
+ DataRegistry.prototype.clearForm = function() {
+ // For the previously loaded form.
+ Y.use('moodle-core-formchangechecker', function() {
+ M.core_formchangechecker.reset_form_dirty_state();
+ });
+
+ // Remove previous listeners.
+ $(SELECTORS.FORM_CONTAINER).off('submit', 'form');
+ };
+
+ /**
+ * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.
+ *
+ * @method submitForm
+ * @param {Event} e Form submission event.
+ * @private
+ */
+ DataRegistry.prototype.submitForm = function(e) {
+ e.preventDefault();
+ $(SELECTORS.FORM_CONTAINER).find('form').submit();
+ };
+
+ DataRegistry.prototype.submitContextLevelFormAjax = function(e) {
+ this.submitFormAjax(e, 'tool_dataprivacy_set_contextlevel_form');
+ };
+
+ DataRegistry.prototype.submitContextFormAjax = function(e) {
+ this.submitFormAjax(e, 'tool_dataprivacy_set_context_form');
+ };
+
+ DataRegistry.prototype.submitFormAjax = function(e, saveMethodName) {
+ // We don't want to do a real form submission.
+ e.preventDefault();
+
+ // Convert all the form elements values to a serialised string.
+ var formData = $(SELECTORS.FORM_CONTAINER).find('form').serialize();
+ return this.strings.then(function(strings) {
+ Ajax.call([{
+ methodname: saveMethodName,
+ args: {jsonformdata: JSON.stringify(formData)},
+ done: function() {
+ Notification.alert(strings[0], strings[1]);
+ },
+ fail: Notification.exception
+ }]);
+ }).catch(Notification.exception);
+
+ };
+
+ DataRegistry.prototype.loadExtra = function(parentNode, expandContextId, expandElement) {
+
+ Ajax.call([{
+ methodname: 'tool_dataprivacy_tree_extra_branches',
+ args: {
+ contextid: expandContextId,
+ element: expandElement,
+ },
+ done: function(data) {
+ if (data.branches.length == 0) {
+ this.noElements(parentNode, expandElement);
+ return;
+ }
+ Templates.render('tool_dataprivacy/context_tree_branches', data)
+ .then(function(html) {
+ parentNode.after(html);
+ this.removeListeners();
+ this.registerEventListeners();
+ this.expand(parentNode);
+ parentNode.data('loaded', 1);
+ return;
+ }.bind(this))
+ .fail(Notification.exception);
+ }.bind(this),
+ fail: Notification.exception
+ }]);
+ };
+
+ DataRegistry.prototype.noElements = function(node, expandElement) {
+ node.data('expandcontextid', '');
+ node.data('expandelement', '');
+ this.strings.then(function(strings) {
+
+ // 2 = blocks, 3 = activities, 4 = courses (although courses is not likely really).
+ var key = 2;
+ if (expandElement == 'module') {
+ key = 3;
+ } else if (expandElement == 'course') {
+ key = 4;
+ }
+ node.text(strings[key]);
+ return;
+ }).fail(Notification.exception);
+ };
+
+ DataRegistry.prototype.collapse = function(node) {
+ node.data('expanded', 0);
+ node.siblings('nav').addClass('hidden');
+ node.find('> i').removeClass('fa-minus');
+ node.find('> i').addClass('fa-plus');
+ };
+
+ DataRegistry.prototype.expand = function(node) {
+ node.data('expanded', 1);
+ node.siblings('nav').removeClass('hidden');
+ node.find('> i').removeClass('fa-plus');
+ // Also remove the spinning one if data was just loaded.
+ node.find('> i').removeClass('fa-circle-o-notch fa-spin');
+ node.find('> i').addClass('fa-minus');
+ };
+ return /** @alias module:tool_dataprivacy/data_registry */ {
+
+ /**
+ * Initialise the page.
+ *
+ * @param {Number} systemContextId
+ * @param {Number} initContextLevel
+ * @param {Number} initContextId
+ * @return {DataRegistry}
+ */
+ init: function(systemContextId, initContextLevel, initContextId) {
+ return new DataRegistry(systemContextId, initContextLevel, initContextId);
+ }
+ };
+ }
+);
+
--- /dev/null
+// 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/>.
+
+/**
+ * Request actions.
+ *
+ * @module tool_dataprivacy/data_request_modal
+ * @package tool_dataprivacy
+ * @copyright 2018 Jun Pataleta
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery', 'core/notification', 'core/custom_interaction_events', 'core/modal', 'core/modal_registry',
+ 'tool_dataprivacy/events'],
+ function($, Notification, CustomEvents, Modal, ModalRegistry, DataPrivacyEvents) {
+
+ var registered = false;
+ var SELECTORS = {
+ APPROVE_BUTTON: '[data-action="approve"]',
+ DENY_BUTTON: '[data-action="deny"]',
+ };
+
+ /**
+ * Constructor for the Modal.
+ *
+ * @param {object} root The root jQuery element for the modal
+ */
+ var ModalDataRequest = function(root) {
+ Modal.call(this, root);
+
+ if (!this.getFooter().find(SELECTORS.APPROVE_BUTTON).length) {
+ Notification.exception({message: 'No approve button found'});
+ }
+
+ if (!this.getFooter().find(SELECTORS.DENY_BUTTON).length) {
+ Notification.exception({message: 'No deny button found'});
+ }
+ };
+
+ ModalDataRequest.TYPE = 'tool_dataprivacy-data_request';
+ ModalDataRequest.prototype = Object.create(Modal.prototype);
+ ModalDataRequest.prototype.constructor = ModalDataRequest;
+
+ /**
+ * Set up all of the event handling for the modal.
+ *
+ * @method registerEventListeners
+ */
+ ModalDataRequest.prototype.registerEventListeners = function() {
+ // Apply parent event listeners.
+ Modal.prototype.registerEventListeners.call(this);
+
+ this.getModal().on(CustomEvents.events.activate, SELECTORS.APPROVE_BUTTON, function(e, data) {
+ var approveEvent = $.Event(DataPrivacyEvents.approve);
+ this.getRoot().trigger(approveEvent, this);
+
+ if (!approveEvent.isDefaultPrevented()) {
+ this.hide();
+ data.originalEvent.preventDefault();
+ }
+ }.bind(this));
+
+ this.getModal().on(CustomEvents.events.activate, SELECTORS.DENY_BUTTON, function(e, data) {
+ var denyEvent = $.Event(DataPrivacyEvents.deny);
+ this.getRoot().trigger(denyEvent, this);
+
+ if (!denyEvent.isDefaultPrevented()) {
+ this.hide();
+ data.originalEvent.preventDefault();
+ }
+ }.bind(this));
+ };
+
+ // Automatically register with the modal registry the first time this module is imported so that you can create modals
+ // of this type using the modal factory.
+ if (!registered) {
+ ModalRegistry.register(ModalDataRequest.TYPE, ModalDataRequest, 'tool_dataprivacy/data_request_modal');
+ registered = true;
+ }
+
+ return ModalDataRequest;
+ });
\ No newline at end of file
--- /dev/null
+// 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/>.
+
+/**
+ * Module to update the displayed retention period.
+ *
+ * @module tool_dataprivacy/effective_retention_period
+ * @package tool_dataprivacy
+ * @copyright 2018 David Monllao
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery'],
+ function($) {
+
+ var SELECTORS = {
+ PURPOSE_SELECT: '#id_purposeid',
+ RETENTION_FIELD_BOOST: '#id_error_retention_current',
+ RETENTION_FIELD_CLEAN: '#fitem_id_retention_current [data-fieldtype=static]',
+ };
+
+ /**
+ * Constructor for the retention period display.
+ *
+ * @param {Array} purposeRetentionPeriods Associative array of purposeids with effective retention period at this context
+ */
+ var EffectiveRetentionPeriod = function(purposeRetentionPeriods) {
+ this.purposeRetentionPeriods = purposeRetentionPeriods;
+ this.registerEventListeners();
+ };
+
+ /**
+ * Removes the current 'change' listeners.
+ *
+ * Useful when a new form is loaded.
+ */
+ var removeListeners = function() {
+ $(SELECTORS.PURPOSE_SELECT).off('change');
+ };
+
+ /**
+ * @var {Array} purposeRetentionPeriods
+ * @private
+ */
+ EffectiveRetentionPeriod.prototype.purposeRetentionPeriods = [];
+
+ /**
+ * Add purpose change listeners.
+ *
+ * @method registerEventListeners
+ */
+ EffectiveRetentionPeriod.prototype.registerEventListeners = function() {
+
+ $(SELECTORS.PURPOSE_SELECT).on('change', function(ev) {
+ var selected = $(ev.currentTarget).val();
+ var selectedPurpose = this.purposeRetentionPeriods[selected];
+
+ var cleanSelector = $(SELECTORS.RETENTION_FIELD_CLEAN);
+ if (cleanSelector.length > 0) {
+ cleanSelector.text(selectedPurpose);
+ } else {
+ var boostSelector = $(SELECTORS.RETENTION_FIELD_BOOST);
+ var retentionField = boostSelector.siblings();
+ if (retentionField.length > 0) {
+ retentionField.text(selectedPurpose);
+ }
+ }
+
+ }.bind(this));
+ };
+
+ return /** @alias module:tool_dataprivacy/effective_retention_period */ {
+ init: function(purposeRetentionPeriods) {
+ // Remove previously attached listeners.
+ removeListeners();
+ return new EffectiveRetentionPeriod(purposeRetentionPeriods);
+ }
+ };
+ }
+);
+
--- /dev/null
+// 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/>.
+
+/**
+ * Contain the events the data privacy tool can fire.
+ *
+ * @module tool_dataprivacy/events
+ * @class events
+ * @package tool_dataprivacy
+ * @copyright 2018 Jun Pataleta
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([], function() {
+ return {
+ approve: 'tool_dataprivacy-data_request:approve',
+ deny: 'tool_dataprivacy-data_request:deny',
+ };
+});
--- /dev/null
+// 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/>.
+
+/**
+ * Potential user selector module.
+ *
+ * @module tool_dataprivacy/expand_contract
+ * @class page-expand-contract
+ * @package tool_dataprivacy
+ * @copyright 2018 Adrian Greeve
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define(['jquery', 'core/url', 'core/str'], function($, url, str) {
+
+ var expandedImage = $('<img alt="" src="' + url.imageUrl('t/expanded') + '"/>');
+ var collapsedImage = $('<img alt="" src="' + url.imageUrl('t/collapsed') + '"/>');
+
+ return /** @alias module:tool_dataprivacy/expand-collapse */ {
+ /**
+ * Expand or collapse a selected node.
+ *
+ * @param {object} targetnode The node that we want to expand / collapse
+ * @param {object} thisnode The node that was clicked.
+ * @return {null}
+ */
+ expandCollapse: function(targetnode, thisnode) {
+ if (targetnode.hasClass('hide')) {
+ targetnode.removeClass('hide');
+ targetnode.addClass('visible');
+ targetnode.attr('aria-expanded', true);
+ thisnode.find(':header i.fa').removeClass('fa-plus-square');
+ thisnode.find(':header i.fa').addClass('fa-minus-square');
+ thisnode.find(':header img.icon').attr('src', expandedImage.attr('src'));
+ } else {
+ targetnode.removeClass('visible');
+ targetnode.addClass('hide');
+ targetnode.attr('aria-expanded', false);
+ thisnode.find(':header i.fa').removeClass('fa-minus-square');
+ thisnode.find(':header i.fa').addClass('fa-plus-square');
+ thisnode.find(':header img.icon').attr('src', collapsedImage.attr('src'));
+ }
+ },
+
+ /**
+ * Expand or collapse all nodes on this page.
+ *
+ * @param {string} nextstate The next state to change to.
+ * @return {null}
+ */
+ expandCollapseAll: function(nextstate) {
+ var currentstate = (nextstate == 'visible') ? 'hide' : 'visible';
+ var ariaexpandedstate = (nextstate == 'visible') ? true : false;
+ var iconclassnow = (nextstate == 'visible') ? 'fa-plus-square' : 'fa-minus-square';
+ var iconclassnext = (nextstate == 'visible') ? 'fa-minus-square' : 'fa-plus-square';
+ var imagenow = (nextstate == 'visible') ? expandedImage.attr('src') : collapsedImage.attr('src');
+ $('.' + currentstate).each(function() {
+ $(this).removeClass(currentstate);
+ $(this).addClass(nextstate);
+ $(this).attr('aria-expanded', ariaexpandedstate);
+ });
+ $('.tool_dataprivacy-expand-all').data('visibilityState', currentstate);
+
+ str.get_string(currentstate, 'tool_dataprivacy').then(function(langString) {
+ $('.tool_dataprivacy-expand-all').html(langString);
+ }).catch(Notification.exception);
+
+ $(':header i.fa').each(function() {
+ $(this).removeClass(iconclassnow);
+ $(this).addClass(iconclassnext);
+ });
+ $(':header img.icon').each(function() {
+ $(this).attr('src', imagenow);
+ });
+ }
+ };
+});
--- /dev/null
+// 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/>.
+
+/**
+ * Potential user selector module.
+ *
+ * @module tool_dataprivacy/form-user-selector
+ * @class form-user-selector
+ * @package tool_dataprivacy
+ * @copyright 2018 Jun Pataleta
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {
+
+ return /** @alias module:tool_dataprivacy/form-user-selector */ {
+
+ processResults: function(selector, results) {
+ var users = [];
+ $.each(results, function(index, user) {
+ users.push({
+ value: user.id,
+ label: user._label
+ });
+ });
+ return users;
+ },
+
+ transport: function(selector, query, success, failure) {
+ var promise;
+
+ promise = Ajax.call([{
+ methodname: 'tool_dataprivacy_get_users',
+ args: {
+ query: query
+ }
+ }]);
+
+ promise[0].then(function(results) {
+ var promises = [],
+ i = 0;
+
+ // Render the label.
+ $.each(results, function(index, user) {
+ promises.push(Templates.render('tool_dataprivacy/form-user-selector-suggestion', user));
+ });
+
+ // Apply the label to the results.
+ return $.when.apply($.when, promises).then(function() {
+ var args = arguments;
+ $.each(results, function(index, user) {
+ user._label = args[i];
+ i++;
+ });
+ success(results);
+ return;
+ });
+
+ }).fail(failure);
+ }
+
+ };
+
+});
--- /dev/null
+// 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/>.
+
+/**
+ * AMD module to enable users to manage their own data requests.
+ *
+ * @module tool_dataprivacy/myrequestactions
+ * @package tool_dataprivacy
+ * @copyright 2018 Jun Pataleta
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([
+ 'jquery',
+ 'core/ajax',
+ 'core/notification',
+ 'core/str',
+ 'core/modal_factory',
+ 'core/modal_events',
+ 'core/templates'],
+function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates) {
+
+ /**
+ * List of action selectors.
+ *
+ * @type {{CANCEL_REQUEST: string}}
+ * @type {{CONTACT_DPO: string}}
+ */
+ var ACTIONS = {
+ CANCEL_REQUEST: '[data-action="cancel"]',
+ CONTACT_DPO: '[data-action="contactdpo"]',
+ };
+
+ /**
+ * MyRequestActions class.
+ */
+ var MyRequestActions = function() {
+ this.registerEvents();
+ };
+
+ /**
+ * Register event listeners.
+ */
+ MyRequestActions.prototype.registerEvents = function() {
+ $(ACTIONS.CANCEL_REQUEST).click(function(e) {
+ e.preventDefault();
+
+ var requestId = $(this).data('requestid');
+ var stringkeys = [
+ {
+ key: 'cancelrequest',
+ component: 'tool_dataprivacy'
+ },
+ {
+ key: 'cancelrequestconfirmation',
+ component: 'tool_dataprivacy'
+ }
+ ];
+
+ Str.get_strings(stringkeys).then(function(langStrings) {
+ var title = langStrings[0];
+ var confirmMessage = langStrings[1];
+ return ModalFactory.create({
+ title: title,
+ body: confirmMessage,
+ type: ModalFactory.types.SAVE_CANCEL
+ }).then(function(modal) {
+ modal.setSaveButtonText(title);
+
+ // Handle save event.
+ modal.getRoot().on(ModalEvents.save, function() {
+ // Cancel the request.
+ var params = {
+ 'requestid': requestId
+ };
+
+ var request = {
+ methodname: 'tool_dataprivacy_cancel_data_request',
+ args: params
+ };
+
+ Ajax.call([request])[0].done(function(data) {
+ if (data.result) {
+ window.location.reload();
+ } else {
+ Notification.addNotification({
+ message: data.warnings[0].message,
+ type: 'error'
+ });
+ }
+ }).fail(Notification.exception);
+ });
+
+ // Handle hidden event.
+ modal.getRoot().on(ModalEvents.hidden, function() {
+ // Destroy when hidden.
+ modal.destroy();
+ });
+
+ return modal;
+ });
+ }).done(function(modal) {
+ // Show the modal!
+ modal.show();
+
+ }).fail(Notification.exception);
+ });
+
+ $(ACTIONS.CONTACT_DPO).click(function(e) {
+ e.preventDefault();
+
+ var replyToEmail = $(this).data('replytoemail');
+
+ var keys = [
+ {
+ key: 'contactdataprotectionofficer',
+ component: 'tool_dataprivacy'
+ },
+ {
+ key: 'send',
+ component: 'tool_dataprivacy'
+ },
+ ];
+
+ var sendButtonText = '';
+ Str.get_strings(keys).then(function(langStrings) {
+ var modalTitle = langStrings[0];
+ sendButtonText = langStrings[1];
+ var context = {
+ 'replytoemail': replyToEmail
+ };
+ return ModalFactory.create({
+ title: modalTitle,
+ body: Templates.render('tool_dataprivacy/contact_dpo', context),
+ type: ModalFactory.types.SAVE_CANCEL,
+ large: true
+ });
+ }).done(function(modal) {
+ modal.setSaveButtonText(sendButtonText);
+
+ // Handle send event.
+ modal.getRoot().on(ModalEvents.save, function(e) {
+ var message = $('#message').val().trim();
+ if (message.length === 0) {
+ e.preventDefault();
+ // Show validation error when the message is empty.
+ $('[data-region="messageinput"]').addClass('has-danger notifyproblem');
+ $('#id_error_message').removeAttr('hidden');
+ } else {
+ // Send the message.
+ sendMessageToDPO(message);
+ }
+ });
+
+ // Handle hidden event.
+ modal.getRoot().on(ModalEvents.hidden, function() {
+ // Destroy when hidden.
+ modal.destroy();
+ });
+
+ // Show the modal!
+ modal.show();
+ }).fail(Notification.exception);
+ });
+ };
+
+ /**
+ * Send message to the Data Protection Officer.
+ *
+ * @param {String} message The message to send.
+ */
+ function sendMessageToDPO(message) {
+ var request = {
+ methodname: 'tool_dataprivacy_contact_dpo',
+ args: {
+ message: message
+ }
+ };
+
+ var requestType = 'success';
+ Ajax.call([request])[0].then(function(data) {
+ if (data.result) {
+ return Str.get_string('requestsubmitted', 'tool_dataprivacy');
+ }
+ requestType = 'error';
+ return data.warnings.join('<br>');
+
+ }).done(function(message) {
+ Notification.addNotification({
+ message: message,
+ type: requestType
+ });
+
+ }).fail(Notification.exception);
+ }
+
+ return /** @alias module:tool_dataprivacy/myrequestactions */ {
+ // Public variables and functions.
+
+ /**
+ * Initialise the unified user filter.
+ *
+ * @method init
+ * @return {MyRequestActions}
+ */
+ 'init': function() {
+ return new MyRequestActions();
+ }
+ };
+});
--- /dev/null
+// 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/>.
+
+/**
+ * AMD module for purposes actions.
+ *
+ * @module tool_dataprivacy/purposesactions
+ * @package tool_dataprivacy
+ * @copyright 2018 David Monllao
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([
+ 'jquery',
+ 'core/ajax',
+ 'core/notification',
+ 'core/str',
+ 'core/modal_factory',
+ 'core/modal_events'],
+function($, Ajax, Notification, Str, ModalFactory, ModalEvents) {
+
+ /**
+ * List of action selectors.
+ *
+ * @type {{DELETE: string}}
+ */
+ var ACTIONS = {
+ DELETE: '[data-action="deletepurpose"]',
+ };
+
+ /**
+ * PurposesActions class.
+ */
+ var PurposesActions = function() {
+ this.registerEvents();
+ };
+
+ /**
+ * Register event listeners.
+ */
+ PurposesActions.prototype.registerEvents = function() {
+ $(ACTIONS.DELETE).click(function(e) {
+ e.preventDefault();
+
+ var id = $(this).data('id');
+ var purposename = $(this).data('name');
+ var stringkeys = [
+ {
+ key: 'deletepurpose',
+ component: 'tool_dataprivacy',
+ param: purposename
+ },
+ {
+ key: 'deletepurposetext',
+ component: 'tool_dataprivacy',
+ param: purposename
+ }
+ ];
+
+ Str.get_strings(stringkeys).then(function(langStrings) {
+ var title = langStrings[0];
+ var confirmMessage = langStrings[1];
+ return ModalFactory.create({
+ title: title,
+ body: confirmMessage,
+ type: ModalFactory.types.SAVE_CANCEL
+ }).then(function(modal) {
+ modal.setSaveButtonText(title);
+
+ // Handle save event.
+ modal.getRoot().on(ModalEvents.save, function() {
+
+ var request = {
+ methodname: 'tool_dataprivacy_delete_purpose',
+ args: {'id': id}
+ };
+
+ Ajax.call([request])[0].done(function(data) {
+ if (data.result) {
+ $('tr[data-purposeid="' + id + '"]').remove();
+ } else {
+ Notification.addNotification({
+ message: data.warnings[0].message,
+ type: 'error'
+ });
+ }
+ }).fail(Notification.exception);
+ });
+
+ // Handle hidden event.
+ modal.getRoot().on(ModalEvents.hidden, function() {
+ // Destroy when hidden.
+ modal.destroy();
+ });
+
+ return modal;
+ });
+ }).done(function(modal) {
+ modal.show();
+
+ }).fail(Notification.exception);
+ });
+ };
+
+ return /** @alias module:tool_dataprivacy/purposesactions */ {
+ // Public variables and functions.
+
+ /**
+ * Initialise the module.
+ *
+ * @method init
+ * @return {PurposesActions}
+ */
+ 'init': function() {
+ return new PurposesActions();
+ }
+ };
+});
--- /dev/null
+// 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/>.
+
+/**
+ * Request actions.
+ *
+ * @module tool_dataprivacy/requestactions
+ * @package tool_dataprivacy
+ * @copyright 2018 Jun Pataleta
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([
+ 'jquery',
+ 'core/ajax',
+ 'core/notification',
+ 'core/str',
+ 'core/modal_factory',
+ 'core/modal_events',
+ 'core/templates',
+ 'tool_dataprivacy/data_request_modal',
+ 'tool_dataprivacy/events'],
+function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, ModalDataRequest, DataPrivacyEvents) {
+
+ /**
+ * List of action selectors.
+ *
+ * @type {{APPROVE_REQUEST: string}}
+ * @type {{DENY_REQUEST: string}}
+ * @type {{VIEW_REQUEST: string}}
+ */
+ var ACTIONS = {
+ APPROVE_REQUEST: '[data-action="approve"]',
+ DENY_REQUEST: '[data-action="deny"]',
+ VIEW_REQUEST: '[data-action="view"]'
+ };
+
+ /**
+ * RequestActions class.
+ */
+ var RequestActions = function() {
+ this.registerEvents();
+ };
+
+ /**
+ * Register event listeners.
+ */
+ RequestActions.prototype.registerEvents = function() {
+ $(ACTIONS.VIEW_REQUEST).click(function(e) {
+ e.preventDefault();
+
+ var requestId = $(this).data('requestid');
+
+ // Cancel the request.
+ var params = {
+ 'requestid': requestId
+ };
+
+ var request = {
+ methodname: 'tool_dataprivacy_get_data_request',
+ args: params
+ };
+
+ var promises = Ajax.call([request]);
+ var modalTitle = '';
+ var modalType = ModalFactory.types.DEFAULT;
+ $.when(promises[0]).then(function(data) {
+ if (data.result) {
+ // Check if the status is awaiting approval.
+ if (data.result.status == 2) {
+ modalType = ModalDataRequest.TYPE;
+ }
+ modalTitle = data.result.typename;
+ return Templates.render('tool_dataprivacy/request_details', data.result);
+ }
+ // Fail.
+ Notification.addNotification({
+ message: data.warnings[0].message,
+ type: 'error'
+ });
+ return false;
+
+ }).then(function(html) {
+ return ModalFactory.create({
+ title: modalTitle,
+ body: html,
+ type: modalType,
+ large: true
+ }).then(function(modal) {
+ // Handle approve event.
+ modal.getRoot().on(DataPrivacyEvents.approve, function() {
+ showConfirmation(DataPrivacyEvents.approve, requestId);
+ });
+
+ // Handle deny event.
+ modal.getRoot().on(DataPrivacyEvents.deny, function() {
+ showConfirmation(DataPrivacyEvents.deny, requestId);
+ });
+
+ // Handle hidden event.
+ modal.getRoot().on(ModalEvents.hidden, function() {
+ // Destroy when hidden.
+ modal.destroy();
+ });
+
+ return modal;
+ });
+ }).done(function(modal) {
+ // Show the modal!
+ modal.show();
+ }).fail(Notification.exception);
+ });
+
+ $(ACTIONS.APPROVE_REQUEST).click(function(e) {
+ e.preventDefault();
+
+ var requestId = $(this).data('requestid');
+ showConfirmation(DataPrivacyEvents.approve, requestId);
+ });
+
+ $(ACTIONS.DENY_REQUEST).click(function(e) {
+ e.preventDefault();
+
+ var requestId = $(this).data('requestid');
+ showConfirmation(DataPrivacyEvents.deny, requestId);
+ });
+ };
+
+ /**
+ * Show the confirmation dialogue.
+ *
+ * @param {String} action The action name.
+ * @param {Number} requestId The request ID.
+ */
+ function showConfirmation(action, requestId) {
+ var keys = [];
+ var wsfunction = '';
+ switch (action) {
+ case DataPrivacyEvents.approve:
+ keys = [
+ {
+ key: 'approverequest',
+ component: 'tool_dataprivacy'
+ },
+ {
+ key: 'confirmapproval',
+ component: 'tool_dataprivacy'
+ }
+ ];
+ wsfunction = 'tool_dataprivacy_approve_data_request';
+ break;
+ case DataPrivacyEvents.deny:
+ keys = [
+ {
+ key: 'denyrequest',
+ component: 'tool_dataprivacy'
+ },
+ {
+ key: 'confirmdenial',
+ component: 'tool_dataprivacy'
+ }
+ ];
+ wsfunction = 'tool_dataprivacy_deny_data_request';
+ break;
+ }
+
+ var modalTitle = '';
+ Str.get_strings(keys).then(function(langStrings) {
+ modalTitle = langStrings[0];
+ var confirmMessage = langStrings[1];
+ return ModalFactory.create({
+ title: modalTitle,
+ body: confirmMessage,
+ type: ModalFactory.types.SAVE_CANCEL
+ });
+ }).then(function(modal) {
+ modal.setSaveButtonText(modalTitle);
+
+ // Handle save event.
+ modal.getRoot().on(ModalEvents.save, function() {
+ // Confirm the request.
+ var params = {
+ 'requestid': requestId
+ };
+
+ var request = {
+ methodname: wsfunction,
+ args: params
+ };
+
+ Ajax.call([request])[0].done(function(data) {
+ if (data.result) {
+ window.location.reload();
+ } else {
+ Notification.addNotification({
+ message: data.warnings[0].message,
+ type: 'error'
+ });
+ }
+ }).fail(Notification.exception);
+ });
+
+ // Handle hidden event.
+ modal.getRoot().on(ModalEvents.hidden, function() {
+ // Destroy when hidden.
+ modal.destroy();
+ });
+
+ return modal;
+ }).done(function(modal) {
+ modal.show();
+ }).fail(Notification.exception);
+ }
+
+ return RequestActions;
+});
--- /dev/null
+<?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/>.
+
+/**
+ * This page lets users manage categories.
+ *
+ * @package tool_dataprivacy
+ * @copyright 2018 David Monllao
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(__DIR__ . '/../../../config.php');
+
+$url = new moodle_url("/admin/tool/dataprivacy/categories.php");
+$title = get_string('editcategories', 'tool_dataprivacy');
+
+\tool_dataprivacy\page_helper::setup($url, $title, 'dataregistry');
+
+$output = $PAGE->get_renderer('tool_dataprivacy');
+echo $output->header();
+
+$categories = \tool_dataprivacy\api::get_categories();
+$renderable = new \tool_dataprivacy\output\categories($categories);
+
+echo $output->render($renderable);
+echo $output->footer();
--- /dev/null
+<?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/>.
+
+/**
+ * Class containing helper methods for processing data requests.
+ *
+ * @package tool_dataprivacy
+ * @copyright 2018 Jun Pataleta
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace tool_dataprivacy;
+
+use coding_exception;
+use context_system;
+use core\invalid_persistent_exception;
+use core\message\message;
+use core\task\manager;
+use core_privacy\local\request\approved_contextlist;
+use core_privacy\local\request\contextlist_collection;
+use core_user;
+use dml_exception;
+use moodle_exception;
+use moodle_url;
+use required_capability_exception;
+use stdClass;
+use tool_dataprivacy\external\data_request_exporter;
+use tool_dataprivacy\task\initiate_data_request_task;
+use tool_dataprivacy\task\process_data_request_task;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Class containing helper methods for processing data requests.
+ *
+ * @copyright 2018 Jun Pataleta
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class api {
+
+ /** Data export request type. */
+ const DATAREQUEST_TYPE_EXPORT = 1;
+
+ /** Data deletion request type. */
+ const DATAREQUEST_TYPE_DELETE = 2;
+
+ /** Other request type. Usually of enquiries to the DPO. */
+ const DATAREQUEST_TYPE_OTHERS = 3;
+
+ /** Newly submitted and we haven't yet started finding out where they have data. */
+ const DATAREQUEST_STATUS_PENDING = 0;
+
+ /** Newly submitted and we have started to find the location of data. */
+ const DATAREQUEST_STATUS_PREPROCESSING = 1;
+
+ /** Metadata ready and awaiting review and approval by the Data Protection officer. */
+ const DATAREQUEST_STATUS_AWAITING_APPROVAL = 2;
+
+ /** Request approved and will be processed soon. */
+ const DATAREQUEST_STATUS_APPROVED = 3;
+
+ /** The request is now being processed. */
+ const DATAREQUEST_STATUS_PROCESSING = 4;
+
+ /** Data request completed. */
+ const DATAREQUEST_STATUS_COMPLETE = 5;
+
+ /** Data request cancelled by the user. */
+ const DATAREQUEST_STATUS_CANCELLED = 6;
+
+ /** Data request rejected by the DPO. */
+ const DATAREQUEST_STATUS_REJECTED = 7;
+
+ /**
+ * Determines whether the user can contact the site's Data Protection Officer via Moodle.
+ *
+ * @return boolean True when tool_dataprivacy|contactdataprotectionofficer is enabled.
+ * @throws dml_exception
+ */
+ public static function can_contact_dpo() {
+ return get_config('tool_dataprivacy', 'contactdataprotectionofficer') == 1;
+ }
+
+ /**
+ * Check's whether the current user has the capability to manage data requests.
+ *
+ * @param int $userid The user ID.
+ * @return bool
+ * @throws coding_exception
+ * @throws dml_exception
+ */
+ public static function can_manage_data_requests($userid) {
+ $context = context_system::instance();
+
+ // A user can manage data requests if he/she has the site DPO role and has the capability to manage data requests.
+ return self::is_site_dpo($userid) && has_capability('tool/dataprivacy:managedatarequests', $context, $userid);
+ }
+
+ /**
+ * Checks if the current user can manage the data registry at the provided id.
+ *
+ * @param int $contextid Fallback to system context id.
+ * @throws \required_capability_exception
+ * @return null
+ */
+ public static function check_can_manage_data_registry($contextid = false) {
+ if ($contextid) {
+ $context = \context_helper::instance_by_id($contextid);
+ } else {
+ $context = \context_system::instance();
+ }
+
+ require_capability('tool/dataprivacy:managedataregistry', $context);
+ }
+
+ /**
+ * Fetches the list of users with the Data Protection Officer role.
+ *
+ * @throws dml_exception
+ */
+ public static function get_site_dpos() {
+ // Get role(s) that can manage data requests.
+ $dporoles = explode(',', get_config('tool_dataprivacy', 'dporoles'));
+
+ $dpos = [];
+ $context = context_system::instance();
+ foreach ($dporoles as $roleid) {
+ if (empty($roleid)) {
+ continue;
+ }
+ $allnames = get_all_user_name_fields(true, 'u');
+ $fields = 'u.id, u.confirmed, u.username, '. $allnames . ', ' .
+ 'u.maildisplay, u.mailformat, u.maildigest, u.email, u.emailstop, u.city, '.
+ 'u.country, u.picture, u.idnumber, u.department, u.institution, '.
+ 'u.lang, u.timezone, u.lastaccess, u.mnethostid, u.auth, u.suspended, u.deleted, ' .
+ 'r.name AS rolename, r.sortorder, '.
+ 'r.shortname AS roleshortname, rn.name AS rolecoursealias';
+ // Fetch users that can manage data requests.
+ $dpos += get_role_users($roleid, $context, false, $fields);
+ }
+
+ // If the site has no data protection officer, defer to site admin(s).
+ if (empty($dpos)) {
+ $dpos = get_admins();
+ }
+ return $dpos;
+ }
+
+ /**
+ * Checks whether a given user is a site DPO.
+ *
+ * @param int $userid The user ID.
+ * @return bool
+ * @throws dml_exception
+ */
+ public static function is_site_dpo($userid) {
+ $dpos = self::get_site_dpos();
+ return array_key_exists($userid, $dpos);
+ }
+
+ /**
+ * Lodges a data request and sends the request details to the site Data Protection Officer(s).
+ *
+ * @param int $foruser The user whom the request is being made for.
+ * @param int $type The request type.
+ * @param string $comments Request comments.
+ * @return data_request
+ * @throws invalid_persistent_exception
+ * @throws coding_exception
+ */
+ public static function create_data_request($foruser, $type, $comments = '') {
+ global $USER;
+
+ $datarequest = new data_request();
+ // The user the request is being made for.
+ $datarequest->set('userid', $foruser);
+ // The user making the request.
+ $datarequest->set('requestedby', $USER->id);
+ // Set status.
+ $datarequest->set('status', self::DATAREQUEST_STATUS_PENDING);
+ // Set request type.
+ $datarequest->set('type', $type);
+ // Set request comments.
+ $datarequest->set('comments', $comments);
+
+ // Store subject access request.
+ $datarequest->create();
+
+ // Fire an ad hoc task to initiate the data request process.
+ $task = new initiate_data_request_task();
+ $task->set_custom_data(['requestid' => $datarequest->get('id')]);
+ manager::queue_adhoc_task($task, true);
+
+ return $datarequest;
+ }
+
+ /**
+ * Fetches the list of the data requests.
+ *
+ * If user ID is provided, it fetches the data requests for the user.
+ * Otherwise, it fetches all of the data requests, provided that the user has the capability to manage data requests.
+ * (e.g. Users with the Data Protection Officer roles)
+ *
+ * @param int $userid The User ID.
+ * @return data_request[]
+ * @throws dml_exception
+ */
+ public static function get_data_requests($userid = 0) {
+ global $USER;
+ $results = [];
+ if ($userid) {
+ // Get the data requests for the user or data requests made by the user.
+ $select = "userid = :userid OR requestedby = :requestedby";
+ $params = [
+ 'userid' => $userid,
+ 'requestedby' => $userid
+ ];
+ $results = data_request::get_records_select($select, $params, 'status DESC, timemodified DESC');
+ } else {
+ // If the current user is one of the site's Data Protection Officers, then fetch all data requests.
+ if (self::is_site_dpo($USER->id)) {
+ $results = data_request::get_records(null, 'status DESC, timemodified DESC', '');
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Checks whether there is already an existing pending/in-progress data request for a user for a given request type.
+ *
+ * @param int $userid The user ID.
+ * @param int $type The request type.
+ * @return bool
+ * @throws coding_exception
+ * @throws dml_exception
+ */
+ public static function has_ongoing_request($userid, $type) {
+ global $DB;
+
+ // Check if the user already has an incomplete data request of the same type.
+ $nonpendingstatuses = [
+ self::DATAREQUEST_STATUS_COMPLETE,
+ self::DATAREQUEST_STATUS_CANCELLED,
+ self::DATAREQUEST_STATUS_REJECTED,
+ ];
+ list($insql, $inparams) = $DB->get_in_or_equal($nonpendingstatuses, SQL_PARAMS_NAMED);
+ $select = 'type = :type AND userid = :userid AND status NOT ' . $insql;
+ $params = array_merge([
+ 'type' => $type,
+ 'userid' => $userid
+ ], $inparams);
+
+ return data_request::record_exists_select($select, $params);
+ }
+
+ /**
+ * Determines whether a request is active or not based on its status.
+ *
+ * @param int $status The request status.
+ * @return bool
+ */
+ public static function is_active($status) {
+ // List of statuses which doesn't require any further processing.
+ $finalstatuses = [
+ self::DATAREQUEST_STATUS_COMPLETE,
+ self::DATAREQUEST_STATUS_CANCELLED,
+ self::DATAREQUEST_STATUS_REJECTED,
+ ];
+
+ return !in_array($status, $finalstatuses);
+ }
+
+ /**
+ * Cancels the data request for a given request ID.
+ *
+ * @param int $requestid The request identifier.
+ * @param int $status The request status.
+ * @param int $dpoid The user ID of the Data Protection Officer
+ * @return bool
+ * @throws invalid_persistent_exception
+ * @throws coding_exception
+ */
+ public static function update_request_status($requestid, $status, $dpoid = 0) {
+ // Update the request.
+ $datarequest = new data_request($requestid);
+ $datarequest->set('status', $status);
+ if ($dpoid) {
+ $datarequest->set('dpo', $dpoid);
+ }
+ return $datarequest->update();
+ }
+
+ /**
+ * Fetches a request based on the request ID.
+ *
+ * @param int $requestid The request identifier
+ * @return data_request
+ */
+ public static function get_request($requestid) {
+ return new data_request($requestid);
+ }
+
+ /**
+ * Approves a data request based on the request ID.
+ *
+ * @param int $requestid The request identifier
+ * @return bool
+ * @throws coding_exception
+ * @throws dml_exception
+ * @throws invalid_persistent_exception
+ * @throws required_capability_exception
+ * @throws moodle_exception
+ */
+ public static function approve_data_request($requestid) {
+ global $USER;
+
+ // Check first whether the user can manage data requests.
+ if (!self::can_manage_data_requests($USER->id)) {
+ $context = context_system::instance();
+ throw new required_capability_exception($context, 'tool/dataprivacy:managedatarequests', 'nopermissions', '');
+ }
+
+ // Check if request is already awaiting for approval.
+ $request = new data_request($requestid);
+ if ($request->get('status') != self::DATAREQUEST_STATUS_AWAITING_APPROVAL) {
+ throw new moodle_exception('errorrequestnotwaitingforapproval', 'tool_dataprivacy');
+ }
+
+ // Update the status and the DPO.
+ $result = self::update_request_status($requestid, self::DATAREQUEST_STATUS_APPROVED, $USER->id);
+
+ // Approve all the contexts attached to the request.
+ // Currently, approving the request implicitly approves all associated contexts, but this may change in future, allowing
+ // users to selectively approve certain contexts only.
+ self::update_request_contexts_with_status($requestid, contextlist_context::STATUS_APPROVED);
+
+ // Fire an ad hoc task to initiate the data request process.
+ $task = new process_data_request_task();
+ $task->set_custom_data(['requestid' => $requestid]);
+ if ($request->get('type') == self::DATAREQUEST_TYPE_EXPORT) {
+ $task->set_userid($request->get('userid'));
+ }
+ manager::queue_adhoc_task($task, true);
+
+ return $result;
+ }
+
+ /**
+ * Rejects a data request based on the request ID.
+ *
+ * @param int $requestid The request identifier
+ * @return bool
+ * @throws coding_exception
+ * @throws dml_exception
+ * @throws invalid_persistent_exception
+ * @throws required_capability_exception
+ * @throws moodle_exception
+ */
+ public static function deny_data_request($requestid) {
+ global $USER;
+
+ if (!self::can_manage_data_requests($USER->id)) {
+ $context = context_system::instance();
+ throw new required_capability_exception($context, 'tool/dataprivacy:managedatarequests', 'nopermissions', '');
+ }
+
+ // Check if request is already awaiting for approval.
+ $request = new data_request($requestid);
+ if ($request->get('status') != self::DATAREQUEST_STATUS_AWAITING_APPROVAL) {
+ throw new moodle_exception('errorrequestnotwaitingforapproval', 'tool_dataprivacy');
+ }
+
+ // Update the status and the DPO.
+ return self::update_request_status($requestid, self::DATAREQUEST_STATUS_REJECTED, $USER->id);
+ }
+
+ /**
+ * Sends a message to the site's Data Protection Officer about a request.
+ *
+ * @param stdClass $dpo The DPO user record
+ * @param data_request $request The data request
+ * @return int|false
+ * @throws coding_exception
+ * @throws dml_exception
+ * @throws moodle_exception
+ */
+ public static function notify_dpo($dpo, data_request $request) {
+ global $PAGE, $SITE;
+
+ $output = $PAGE->get_renderer('tool_dataprivacy');
+
+ $usercontext = \context_user::instance($request->get('requestedby'));
+ $requestexporter = new data_request_exporter($request, ['context' => $usercontext]);
+ $requestdata = $requestexporter->export($output);
+
+ // Create message to send to the Data Protection Officer(s).
+ $typetext = null;
+ $typetext = $requestdata->typename;
+ $subject = get_string('datarequestemailsubject', 'tool_dataprivacy', $typetext);
+
+ $requestedby = $requestdata->requestedbyuser;
+ $datarequestsurl = new moodle_url('/admin/tool/dataprivacy/datarequests.php');
+ $message = new message();
+ $message->courseid = $SITE->id;
+ $message->component = 'tool_dataprivacy';
+ $message->name = 'contactdataprotectionofficer';
+ $message->userfrom = $requestedby;
+ $message->replyto = $requestedby->email;
+ $message->replytoname = $requestedby->fullname;
+ $message->subject = $subject;
+ $message->fullmessageformat = FORMAT_HTML;
+ $message->notification = 1;
+ $message->contexturl = $datarequestsurl;
+ $message->contexturlname = get_string('datarequests', 'tool_dataprivacy');
+
+ // Prepare the context data for the email message body.
+ $messagetextdata = [
+ 'requestedby' => $requestedby->fullname,
+ 'requesttype' => $typetext,
+ 'requestdate' => userdate($requestdata->timecreated),
+ 'requestcomments' => $requestdata->messagehtml,
+ 'datarequestsurl' => $datarequestsurl
+ ];
+ $requestingfor = $requestdata->foruser;
+ if ($requestedby->id == $requestingfor->id) {
+ $messagetextdata['requestfor'] = $messagetextdata['requestedby'];
+ } else {
+ $messagetextdata['requestfor'] = $requestingfor->fullname;
+ }
+
+ // Email the data request to the Data Protection Officer(s)/Admin(s).
+ $messagetextdata['dponame'] = fullname($dpo);
+ // Render message email body.
+ $messagehtml = $output->render_from_template('tool_dataprivacy/data_request_email', $messagetextdata);
+ $message->userto = $dpo;
+ $message->fullmessage = html_to_text($messagehtml);
+ $message->fullmessagehtml = $messagehtml;
+
+ // Send message.
+ return message_send($message);
+ }
+
+ /**
+ * Creates a new data purpose.
+ *
+ * @param stdClass $record
+ * @return \tool_dataprivacy\purpose.
+ */
+ public static function create_purpose(stdClass $record) {
+ self::check_can_manage_data_registry();
+
+ $purpose = new purpose(0, $record);
+ $purpose->create();
+
+ return $purpose;
+ }
+
+ /**
+ * Updates an existing data purpose.
+ *
+ * @param stdClass $record
+ * @return \tool_dataprivacy\purpose.
+ */
+ public static function update_purpose(stdClass $record) {
+ self::check_can_manage_data_registry();
+
+ $purpose = new purpose($record->id);
+ $purpose->from_record($record);
+
+ $result = $purpose->update();
+
+ return $purpose;
+ }
+
+ /**
+ * Deletes a data purpose.
+ *
+ * @param int $id
+ * @return bool
+ */
+ public static function delete_purpose($id) {
+ self::check_can_manage_data_registry();
+
+ $purpose = new purpose($id);
+ if ($purpose->is_used()) {
+ throw new \moodle_exception('Purpose with id ' . $id . ' can not be deleted because it is used.');
+ }
+ return $purpose->delete();
+ }
+
+ /**
+ * Get all system data purposes.
+ *
+ * @return \tool_dataprivacy\purpose[]
+ */
+ public static function get_purposes() {
+ self::check_can_manage_data_registry();
+
+ return purpose::get_records([], 'name', 'ASC');
+ }
+
+ /**
+ * Creates a new data category.
+ *
+ * @param stdClass $record
+ * @return \tool_dataprivacy\category.
+ */
+ public static function create_category(stdClass $record) {
+ self::check_can_manage_data_registry();
+
+ $category = new category(0, $record);
+ $category->create();
+
+ return $category;
+ }
+
+ /**
+ * Updates an existing data category.
+ *
+ * @param stdClass $record
+ * @return \tool_dataprivacy\category.
+ */
+ public static function update_category(stdClass $record) {
+ self::check_can_manage_data_registry();
+
+ $category = new category($record->id);
+ $category->from_record($record);
+
+ $result = $category->update();
+
+ return $category;
+ }
+
+ /**
+ * Deletes a data category.
+ *
+ * @param int $id
+ * @return bool
+ */
+ public static function delete_category($id) {
+ self::check_can_manage_data_registry();
+
+ $category = new category($id);
+ if ($category->is_used()) {
+ throw new \moodle_exception('Category with id ' . $id . ' can not be deleted because it is used.');
+ }
+ return $category->delete();
+ }
+
+ /**
+ * Get all system data categories.
+ *
+ * @return \tool_dataprivacy\category[]
+ */
+ public static function get_categories() {
+ self::check_can_manage_data_registry();
+
+ return category::get_records([], 'name', 'ASC');
+ }
+
+ /**
+ * Sets the context instance purpose and category.
+ *
+ * @param \stdClass $record
+ * @return \tool_dataprivacy\context_instance
+ */
+ public static function set_context_instance($record) {
+ self::check_can_manage_data_registry($record->contextid);
+
+ if ($instance = context_instance::get_record_by_contextid($record->contextid, false)) {
+ // Update.
+ $instance->from_record($record);
+
+ if (empty($record->purposeid) && empty($record->categoryid)) {
+ // We accept one of them to be null but we delete it if both are null.
+ self::unset_context_instance($instance);
+ return;
+ }
+
+ } else {
+ // Add.
+ $instance = new context_instance(0, $record);
+ }
+ $instance->save();
+
+ return $instance;
+ }
+
+ /**
+ * Unsets the context instance record.
+ *
+ * @param \tool_dataprivacy\context_instance $instance
+ * @return null
+ */
+ public static function unset_context_instance(context_instance $instance) {
+ self::check_can_manage_data_registry($instance->get('contextid'));
+ $instance->delete();
+ }
+
+ /**
+ * Sets the context level purpose and category.
+ *
+ * @throws \coding_exception
+ * @param \stdClass $record
+ * @return contextlevel
+ */
+ public static function set_contextlevel($record) {
+ global $DB;
+
+ // Only manager at system level can set this.
+ self::check_can_manage_data_registry();
+
+ if ($record->contextlevel != CONTEXT_SYSTEM && $record->contextlevel != CONTEXT_USER) {
+ throw new \coding_exception('Only context system and context user can set a contextlevel ' .
+ 'purpose and retention');
+ }
+
+ if ($contextlevel = contextlevel::get_record_by_contextlevel($record->contextlevel, false)) {
+ // Update.
+ $contextlevel->from_record($record);
+ } else {
+ // Add.
+ $contextlevel = new contextlevel(0, $record);
+ }
+ $contextlevel->save();
+
+ // We sync with their defaults as we removed these options from the defaults page.
+ $classname = \context_helper::get_class_for_level($record->contextlevel);
+ list($purposevar, $categoryvar) = data_registry::var_names_from_context($classname);
+ set_config($purposevar, $record->purposeid, 'tool_dataprivacy');
+ set_config($categoryvar, $record->categoryid, 'tool_dataprivacy');
+
+ return $contextlevel;
+ }
+
+ /**
+ * Returns the effective category given a context instance.
+ *
+ * @param \context $context
+ * @param int $forcedvalue Use this categoryid value as if this was this context instance category.
+ * @return category|false
+ */
+ public static function get_effective_context_category(\context $context, $forcedvalue=false) {
+ self::check_can_manage_data_registry($context->id);
+ if (!data_registry::defaults_set()) {
+ return false;
+ }
+
+ return data_registry::get_effective_context_value($context, 'category', $forcedvalue);
+ }
+
+ /**
+ * Returns the effective purpose given a context instance.
+ *
+ * @param \context $context
+ * @param int $forcedvalue Use this purposeid value as if this was this context instance purpose.
+ * @return purpose|false
+ */
+ public static function get_effective_context_purpose(\context $context, $forcedvalue=false) {
+ self::check_can_manage_data_registry($context->id);
+ if (!data_registry::defaults_set()) {
+ return false;
+ }
+
+ return data_registry::get_effective_context_value($context, 'purpose', $forcedvalue);
+ }
+
+ /**
+ * Returns the effective category given a context level.
+ *
+ * @param int $contextlevel
+ * @param int $forcedvalue Use this categoryid value as if this was this context level category.
+ * @return category|false
+ */
+ public static function get_effective_contextlevel_category($contextlevel, $forcedvalue=false) {
+ self::check_can_manage_data_registry(\context_system::instance()->id);
+ if (!data_registry::defaults_set()) {
+ return false;
+ }
+
+ return data_registry::get_effective_contextlevel_value($contextlevel, 'category', $forcedvalue);
+ }
+
+ /**
+ * Returns the effective purpose given a context level.
+ *
+ * @param int $contextlevel
+ * @param int $forcedvalue Use this purposeid value as if this was this context level purpose.
+ * @return purpose|false
+ */
+ public static function get_effective_contextlevel_purpose($contextlevel, $forcedvalue=false) {
+ self::check_can_manage_data_registry(\context_system::instance()->id);
+ if (!data_registry::defaults_set()) {
+ return false;
+ }
+
+ return data_registry::get_effective_contextlevel_value($contextlevel, 'purpose', $forcedvalue);
+ }
+
+ /**
+ * Creates an expired&nb