diff --git a/admin/tool/dataprivacy/amd/build/defaultsactions.min.js b/admin/tool/dataprivacy/amd/build/defaultsactions.min.js new file mode 100644 index 00000000000..b8f0bc3738d --- /dev/null +++ b/admin/tool/dataprivacy/amd/build/defaultsactions.min.js @@ -0,0 +1 @@ +define(["jquery","core/ajax","core/notification","core/str","core/modal_factory","core/modal_events","core/templates"],function(a,b,c,d,e,f,g){function h(b,d,h,j,k,l,m,n){null!==h&&l.forEach(function(a){a.id===h&&(a.selected=!0)}),null!==j&&m.forEach(function(a){a.id===j&&(a.selected=!0)});var o={contextlevel:d,categoryoptions:l,purposeoptions:m};null!==n&&n.length&&(null===k?o.newactivitydefaults=!0:n.forEach(function(a){k===a.name&&(a.selected=!0)}),o.modemodule=!0,o.activityoptions=n),e.create({title:b,body:g.render("tool_dataprivacy/category_purpose_form",o),type:e.types.SAVE_CANCEL,large:!0}).then(function(b){return b.getRoot().on(f.save,function(){var b=a("#activity"),c="undefined"!=typeof b?b.val():null,d=a("#override"),e="undefined"!=typeof d&&d.is(":checked");i(a("#contextlevel").val(),a("#category").val(),a("#purpose").val(),c,e)}),b.getRoot().on(f.hidden,function(){b.destroy()}),b.show(),b})["catch"](c.exception)}function i(a,c,d,e,f){var g={methodname:"tool_dataprivacy_set_context_defaults",args:{contextlevel:a,category:c,purpose:d,override:f,activity:e}};b.call([g])[0].done(function(a){a.result&&window.location.reload()})}var j={EDIT_LEVEL_DEFAULTS:'[data-action="edit-level-defaults"]',NEW_ACTIVITY_DEFAULTS:'[data-action="new-activity-defaults"]',EDIT_ACTIVITY_DEFAULTS:'[data-action="edit-activity-defaults"]',DELETE_ACTIVITY_DEFAULTS:'[data-action="delete-activity-defaults"]'},k=-1,l=function(){this.registerEvents()};return l.prototype.registerEvents=function(){a(j.EDIT_LEVEL_DEFAULTS).click(function(e){e.preventDefault();var f=a(this),g=f.data("contextlevel"),i=f.data("category"),j=f.data("purpose"),k=[{methodname:"tool_dataprivacy_get_category_options",args:{}},{methodname:"tool_dataprivacy_get_purpose_options",args:{}}],l=b.call(k),m=d.get_string("editdefaults","tool_dataprivacy",a("#defaults-header").text());a.when(l[0],l[1],m).then(function(a,b,c){var d=a.options,e=b.options;return h(c,g,i,j,null,d,e,null),!0})["catch"](c.exception)}),a(j.NEW_ACTIVITY_DEFAULTS).click(function(e){e.preventDefault();var f=a(this),g=f.data("contextlevel"),i=[{methodname:"tool_dataprivacy_get_category_options",args:{}},{methodname:"tool_dataprivacy_get_purpose_options",args:{}},{methodname:"tool_dataprivacy_get_activity_options",args:{nodefaults:!0}}],j=b.call(i),k=d.get_string("addnewdefaults","tool_dataprivacy");a.when(j[0],j[1],j[2],k).then(function(a,b,c,d){var e=a.options,f=b.options,i=c.options;return h(d,g,null,null,null,e,f,i),!0})["catch"](c.exception)}),a(j.EDIT_ACTIVITY_DEFAULTS).click(function(e){e.preventDefault();var f=a(this),g=f.data("contextlevel"),i=f.data("category"),j=f.data("purpose"),k=f.data("activityname"),l=[{methodname:"tool_dataprivacy_get_category_options",args:{}},{methodname:"tool_dataprivacy_get_purpose_options",args:{}},{methodname:"tool_dataprivacy_get_activity_options",args:{}}],m=b.call(l),n=d.get_string("editmoduledefaults","tool_dataprivacy");a.when(m[0],m[1],m[2],n).then(function(a,b,c,d){var e=a.options,f=b.options,l=c.options;return h(d,g,i,j,k,e,f,l),!0})["catch"](c.exception)}),a(j.DELETE_ACTIVITY_DEFAULTS).click(function(b){b.preventDefault();var h=a(this),j=h.data("contextlevel"),l=h.data("activityname"),m=h.data("activitydisplayname"),n=k,o=k;e.create({title:d.get_string("deletedefaults","tool_dataprivacy",m),body:g.render("tool_dataprivacy/delete_activity_defaults",{activityname:m}),type:e.types.SAVE_CANCEL,large:!0}).then(function(a){return a.setSaveButtonText(d.get_string("delete")),a.getRoot().on(f.save,function(){i(j,n,o,l,!1)}),a.getRoot().on(f.hidden,function(){a.destroy()}),a.show(),!0})["catch"](c.exception)})},{init:function(){return new l}}}); \ No newline at end of file diff --git a/admin/tool/dataprivacy/amd/src/defaultsactions.js b/admin/tool/dataprivacy/amd/src/defaultsactions.js new file mode 100644 index 00000000000..76cbb89ae91 --- /dev/null +++ b/admin/tool/dataprivacy/amd/src/defaultsactions.js @@ -0,0 +1,315 @@ +// 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 . + +/** + * AMD module for data registry defaults actions. + * + * @module tool_dataprivacy/defaultsactions + * @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 {{EDIT_LEVEL_DEFAULTS: string}} + * @type {{NEW_ACTIVITY_DEFAULTS: string}} + * @type {{EDIT_ACTIVITY_DEFAULTS: string}} + * @type {{DELETE_ACTIVITY_DEFAULTS: string}} + */ + var ACTIONS = { + EDIT_LEVEL_DEFAULTS: '[data-action="edit-level-defaults"]', + NEW_ACTIVITY_DEFAULTS: '[data-action="new-activity-defaults"]', + EDIT_ACTIVITY_DEFAULTS: '[data-action="edit-activity-defaults"]', + DELETE_ACTIVITY_DEFAULTS: '[data-action="delete-activity-defaults"]' + }; + + /** @type {{INHERIT: Number}} **/ + var INHERIT = -1; + + /** + * DefaultsActions class. + */ + var DefaultsActions = function() { + this.registerEvents(); + }; + + /** + * Register event listeners. + */ + DefaultsActions.prototype.registerEvents = function() { + $(ACTIONS.EDIT_LEVEL_DEFAULTS).click(function(e) { + e.preventDefault(); + + var button = $(this); + var contextLevel = button.data('contextlevel'); + var category = button.data('category'); + var purpose = button.data('purpose'); + + // Get options. + var requests = [ + {methodname: 'tool_dataprivacy_get_category_options', args: {}}, + {methodname: 'tool_dataprivacy_get_purpose_options', args: {}} + ]; + + var promises = Ajax.call(requests); + var titlePromise = Str.get_string('editdefaults', 'tool_dataprivacy', $('#defaults-header').text()); + $.when(promises[0], promises[1], titlePromise).then(function(categoryResponse, purposeResponse, title) { + var categories = categoryResponse.options; + var purposes = purposeResponse.options; + showDefaultsFormModal(title, contextLevel, category, purpose, null, categories, purposes, null); + + return true; + }).catch(Notification.exception); + }); + + $(ACTIONS.NEW_ACTIVITY_DEFAULTS).click(function(e) { + e.preventDefault(); + + var button = $(this); + var contextLevel = button.data('contextlevel'); + + // Get options. + var requests = [ + {methodname: 'tool_dataprivacy_get_category_options', args: {}}, + {methodname: 'tool_dataprivacy_get_purpose_options', args: {}}, + {methodname: 'tool_dataprivacy_get_activity_options', args: {'nodefaults': true}} + ]; + + var promises = Ajax.call(requests); + var titlePromise = Str.get_string('addnewdefaults', 'tool_dataprivacy'); + + $.when(promises[0], promises[1], promises[2], titlePromise).then( + function(categoryResponse, purposeResponse, activityResponse, title) { + var categories = categoryResponse.options; + var purposes = purposeResponse.options; + var activities = activityResponse.options; + + showDefaultsFormModal(title, contextLevel, null, null, null, categories, purposes, activities); + + return true; + + }).catch(Notification.exception); + } + ); + + $(ACTIONS.EDIT_ACTIVITY_DEFAULTS).click(function(e) { + e.preventDefault(); + + var button = $(this); + var contextLevel = button.data('contextlevel'); + var category = button.data('category'); + var purpose = button.data('purpose'); + var activity = button.data('activityname'); + + // Get options. + var requests = [ + {methodname: 'tool_dataprivacy_get_category_options', args: {}}, + {methodname: 'tool_dataprivacy_get_purpose_options', args: {}}, + {methodname: 'tool_dataprivacy_get_activity_options', args: {}} + ]; + + var promises = Ajax.call(requests); + var titlePromise = Str.get_string('editmoduledefaults', 'tool_dataprivacy'); + + $.when(promises[0], promises[1], promises[2], titlePromise).then( + function(categoryResponse, purposeResponse, activityResponse, title) { + var categories = categoryResponse.options; + var purposes = purposeResponse.options; + var activities = activityResponse.options; + + showDefaultsFormModal(title, contextLevel, category, purpose, activity, categories, purposes, activities); + + return true; + + }).catch(Notification.exception); + } + ); + + $(ACTIONS.DELETE_ACTIVITY_DEFAULTS).click(function(e) { + e.preventDefault(); + + var button = $(this); + var contextLevel = button.data('contextlevel'); + var activity = button.data('activityname'); + var activityDisplayName = button.data('activitydisplayname'); + // Set category and purpose to inherit (-1). + var category = INHERIT; + var purpose = INHERIT; + + ModalFactory.create({ + title: Str.get_string('deletedefaults', 'tool_dataprivacy', activityDisplayName), + body: Templates.render('tool_dataprivacy/delete_activity_defaults', {"activityname": activityDisplayName}), + type: ModalFactory.types.SAVE_CANCEL, + large: true + }).then(function(modal) { + modal.setSaveButtonText(Str.get_string('delete')); + + // Handle save event. + modal.getRoot().on(ModalEvents.save, function() { + setContextDefaults(contextLevel, category, purpose, activity, false); + }); + + // Handle hidden event. + modal.getRoot().on(ModalEvents.hidden, function() { + // Destroy when hidden. + modal.destroy(); + }); + + modal.show(); + + return true; + }).catch(Notification.exception); + }); + }; + + /** + * Prepares and renders the modal for setting the defaults for the given context level/plugin. + * + * @param {String} title The modal's title. + * @param {Number} contextLevel The context level to set defaults for. + * @param {Number} category The current category ID. + * @param {Number} purpose The current purpose ID. + * @param {String} activity The plugin name of the activity. Optional. + * @param {Array} categoryOptions The list of category options. + * @param {Array} purposeOptions The list of purpose options. + * @param {Array} activityOptions The list of activity options. Optional. + */ + function showDefaultsFormModal(title, contextLevel, category, purpose, activity, + categoryOptions, purposeOptions, activityOptions) { + + if (category !== null) { + categoryOptions.forEach(function(currentValue) { + if (currentValue.id === category) { + currentValue.selected = true; + } + }); + } + + if (purpose !== null) { + purposeOptions.forEach(function(currentValue) { + if (currentValue.id === purpose) { + currentValue.selected = true; + } + }); + } + + var templateContext = { + "contextlevel": contextLevel, + "categoryoptions": categoryOptions, + "purposeoptions": purposeOptions + }; + + // Check the activityOptions parameter that was passed. + if (activityOptions !== null && activityOptions.length) { + // Check the activity parameter that was passed. + if (activity === null) { + // We're setting a new defaults for a module. + templateContext.newactivitydefaults = true; + + } else { + // Edit mode. Set selection. + activityOptions.forEach(function(currentValue) { + if (activity === currentValue.name) { + currentValue.selected = true; + } + }); + } + + templateContext.modemodule = true; + templateContext.activityoptions = activityOptions; + } + + ModalFactory.create({ + title: title, + body: Templates.render('tool_dataprivacy/category_purpose_form', templateContext), + type: ModalFactory.types.SAVE_CANCEL, + large: true + }).then(function(modal) { + + // Handle save event. + modal.getRoot().on(ModalEvents.save, function() { + var activity = $('#activity'); + var activityVal = typeof activity !== 'undefined' ? activity.val() : null; + var override = $('#override'); + var overrideVal = typeof override !== 'undefined' ? override.is(':checked') : false; + + setContextDefaults($('#contextlevel').val(), $('#category').val(), $('#purpose').val(), activityVal, overrideVal); + }); + + // Handle hidden event. + modal.getRoot().on(ModalEvents.hidden, function() { + // Destroy when hidden. + modal.destroy(); + }); + + modal.show(); + + return modal; + }).catch(Notification.exception); + } + + /** + * Calls a the tool_dataprivacy_set_context_defaults WS function. + * + * @param {Number} contextLevel The context level. + * @param {Number} category The category ID. + * @param {Number} purpose The purpose ID. + * @param {String} activity The plugin name of the activity module. + * @param {Boolean} override Whether to override custom instances. + */ + function setContextDefaults(contextLevel, category, purpose, activity, override) { + var request = { + methodname: 'tool_dataprivacy_set_context_defaults', + args: { + 'contextlevel': contextLevel, + 'category': category, + 'purpose': purpose, + 'override': override, + 'activity': activity + } + }; + + Ajax.call([request])[0].done(function(data) { + if (data.result) { + window.location.reload(); + } + }); + } + + return /** @alias module:tool_dataprivacy/defaultsactions */ { + // Public variables and functions. + + /** + * Initialise the module. + * + * @method init + * @return {DefaultsActions} + */ + 'init': function() { + return new DefaultsActions(); + } + }; +}); diff --git a/admin/tool/dataprivacy/classes/api.php b/admin/tool/dataprivacy/classes/api.php index 6ee970733d7..8e89c23a308 100644 --- a/admin/tool/dataprivacy/classes/api.php +++ b/admin/tool/dataprivacy/classes/api.php @@ -24,7 +24,7 @@ namespace tool_dataprivacy; use coding_exception; -use context_course; +use context_helper; use context_system; use core\invalid_persistent_exception; use core\message\message; @@ -1141,4 +1141,89 @@ class api { return $approvedcollection; } + + /** + * Updates the default category and purpose for a given context level (and optionally, a plugin). + * + * @param int $contextlevel The context level. + * @param int $categoryid The ID matching the category. + * @param int $purposeid The ID matching the purpose record. + * @param int $activity The name of the activity that we're making a defaults configuration for. + * @param bool $override Whether to override the purpose/categories of existing instances to these defaults. + * @return boolean True if set/unset config succeeds. Otherwise, it throws an exception. + */ + public static function set_context_defaults($contextlevel, $categoryid, $purposeid, $activity = null, $override = false) { + global $DB; + + self::check_can_manage_data_registry(); + + // Get the class name associated with this context level. + $classname = context_helper::get_class_for_level($contextlevel); + list($purposevar, $categoryvar) = data_registry::var_names_from_context($classname, $activity); + + // Check the default category to be set. + if ($categoryid == context_instance::INHERIT) { + unset_config($categoryvar, 'tool_dataprivacy'); + + } else { + // Make sure the given category ID exists first. + $categorypersistent = new category($categoryid); + $categorypersistent->read(); + + // Then set the new default value. + set_config($categoryvar, $categoryid, 'tool_dataprivacy'); + } + + // Check the default purpose to be set. + if ($purposeid == context_instance::INHERIT) { + // If the defaults is set to inherit, just unset the config value. + unset_config($purposevar, 'tool_dataprivacy'); + + } else { + // Make sure the given purpose ID exists first. + $purposepersistent = new purpose($purposeid); + $purposepersistent->read(); + + // Then set the new default value. + set_config($purposevar, $purposeid, 'tool_dataprivacy'); + } + + // Unset instances that have been assigned with custom purpose and category, if override was specified. + if ($override) { + // We'd like to find context IDs that we want to unset. + $statements = ["SELECT c.id as contextid FROM {context} c"]; + // Based on this context level. + $params = ['contextlevel' => $contextlevel]; + + if ($contextlevel == CONTEXT_MODULE) { + // If we're deleting module context instances, we need to make sure the instance ID is in the course modules table. + $statements[] = "JOIN {course_modules} cm ON cm.id = c.instanceid"; + // And that the module is listed on the modules table. + $statements[] = "JOIN {modules} m ON m.id = cm.module"; + + if ($activity) { + // If we're overriding for an activity module, make sure that the context instance matches that activity. + $statements[] = "AND m.name = :modname"; + $params['modname'] = $activity; + } + } + // Make sure this context instance exists in the tool_dataprivacy_ctxinstance table. + $statements[] = "JOIN {tool_dataprivacy_ctxinstance} tdc ON tdc.contextid = c.id"; + // And that the context level of this instance matches the given context level. + $statements[] = "WHERE c.contextlevel = :contextlevel"; + + // Build our SQL query by gluing the statements. + $sql = implode("\n", $statements); + + // Get the context records matching our query. + $contextinstances = $DB->get_records_sql($sql, $params); + + // Delete the matching context instances by passing an instance record without the purpose and instance. + foreach ($contextinstances as $instance) { + self::set_context_instance($instance); + } + } + + return true; + } } diff --git a/admin/tool/dataprivacy/classes/data_registry.php b/admin/tool/dataprivacy/classes/data_registry.php index 1fac2f5756c..b8613ff8f00 100644 --- a/admin/tool/dataprivacy/classes/data_registry.php +++ b/admin/tool/dataprivacy/classes/data_registry.php @@ -57,13 +57,22 @@ class data_registry { /** * Returns purpose and category var names from a context class name * - * @param string $classname + * @param string $classname The context level's class. + * @param string $pluginname The name of the plugin associated with the context level. * @return string[] */ - public static function var_names_from_context($classname) { + public static function var_names_from_context($classname, $pluginname = '') { + $pluginname = trim($pluginname); + if (!empty($pluginname)) { + $categoryvar = $classname . '_' . $pluginname . '_category'; + $purposevar = $classname . '_' . $pluginname . '_purpose'; + } else { + $categoryvar = $classname . '_category'; + $purposevar = $classname . '_purpose'; + } return [ - $classname . '_purpose', - $classname . '_category', + $purposevar, + $categoryvar ]; } @@ -72,13 +81,14 @@ class data_registry { * * The caller code is responsible of checking that $contextlevel is an integer. * - * @param int $contextlevel - * @return int|false[] + * @param int $contextlevel The context level. + * @param string $pluginname The name of the plugin associated with the context level. + * @return int[]|false[] */ - public static function get_defaults($contextlevel) { + public static function get_defaults($contextlevel, $pluginname = '') { $classname = \context_helper::get_class_for_level($contextlevel); - list($purposevar, $categoryvar) = self::var_names_from_context($classname); + list($purposevar, $categoryvar) = self::var_names_from_context($classname, $pluginname); $purposeid = get_config('tool_dataprivacy', $purposevar); $categoryid = get_config('tool_dataprivacy', $categoryvar); diff --git a/admin/tool/dataprivacy/classes/external.php b/admin/tool/dataprivacy/classes/external.php index e14e0726900..2b6ee23f8e5 100644 --- a/admin/tool/dataprivacy/classes/external.php +++ b/admin/tool/dataprivacy/classes/external.php @@ -1110,6 +1110,304 @@ class external extends external_api { ]); } + /** + * Parameters for set_context_defaults(). + * + * @return external_function_parameters + */ + public static function set_context_defaults_parameters() { + return new external_function_parameters([ + 'contextlevel' => new external_value(PARAM_INT, 'Expired context record ID', VALUE_REQUIRED), + 'category' => new external_value(PARAM_INT, 'The default category for the given context level', VALUE_REQUIRED), + 'purpose' => new external_value(PARAM_INT, 'The default purpose for the given context level', VALUE_REQUIRED), + 'activity' => new external_value(PARAM_PLUGIN, 'The plugin name of the activity', VALUE_DEFAULT, null), + 'override' => new external_value(PARAM_BOOL, 'Whether to override existing instances with the defaults', VALUE_DEFAULT, + false), + ]); + } + + /** + * Updates the default category and purpose for a given context level (and optionally, a plugin). + * + * @param int $contextlevel The context level. + * @param int $category The ID matching the category. + * @param int $purpose The ID matching the purpose record. + * @param int $activity The name of the activity that we're making a defaults configuration for. + * @param bool $override Whether to override the purpose/categories of existing instances to these defaults. + * @return array + */ + public static function set_context_defaults($contextlevel, $category, $purpose, $activity, $override) { + $warnings = []; + + $params = external_api::validate_parameters(self::set_context_defaults_parameters(), [ + 'contextlevel' => $contextlevel, + 'category' => $category, + 'purpose' => $purpose, + 'activity' => $activity, + 'override' => $override, + ]); + $contextlevel = $params['contextlevel']; + $category = $params['category']; + $purpose = $params['purpose']; + $activity = $params['activity']; + $override = $params['override']; + + // Validate context. + $context = context_system::instance(); + self::validate_context($context); + + // Set the context defaults. + $result = api::set_context_defaults($contextlevel, $category, $purpose, $activity, $override); + + return [ + 'result' => $result, + 'warnings' => $warnings + ]; + } + + /** + * Returns for set_context_defaults(). + * + * @return external_single_structure + */ + public static function set_context_defaults_returns() { + return new external_single_structure([ + 'result' => new external_value(PARAM_BOOL, 'Whether the context defaults were successfully set or not'), + 'warnings' => new external_warnings() + ]); + } + + /** + * Parameters for get_category_options(). + * + * @return external_function_parameters + */ + public static function get_category_options_parameters() { + return new external_function_parameters([ + 'includeinherit' => new external_value(PARAM_BOOL, 'Include option "Inherit"', VALUE_DEFAULT, true), + 'includenotset' => new external_value(PARAM_BOOL, 'Include option "Not set"', VALUE_DEFAULT, false), + ]); + } + + /** + * Fetches a list of data category options containing category IDs as keys and the category name for the value. + * + * @param bool $includeinherit Whether to include the "Inherit" option. + * @param bool $includenotset Whether to include the "Not set" option. + * @return array + */ + public static function get_category_options($includeinherit, $includenotset) { + $warnings = []; + + $params = self::validate_parameters(self::get_category_options_parameters(), [ + 'includeinherit' => $includeinherit, + 'includenotset' => $includenotset + ]); + $includeinherit = $params['includeinherit']; + $includenotset = $params['includenotset']; + + $context = context_system::instance(); + self::validate_context($context); + + $categories = api::get_categories(); + $categoryoptions = []; + if ($includeinherit) { + $categoryoptions[] = [ + 'id' => context_instance::INHERIT, + 'name' => get_string('inherit', 'tool_dataprivacy'), + ]; + } + if ($includenotset) { + $categoryoptions[] = [ + 'id' => context_instance::NOTSET, + 'name' => get_string('notset', 'tool_dataprivacy'), + ]; + } + foreach ($categories as $category) { + $categoryoptions[] = [ + 'id' => $category->get('id'), + 'name' => $category->get('name'), + ]; + } + + return [ + 'options' => $categoryoptions, + 'warnings' => $warnings + ]; + } + + /** + * Returns for get_category_options(). + * + * @return external_single_structure + */ + public static function get_category_options_returns() { + $optiondefinition = new external_single_structure( + [ + 'id' => new external_value(PARAM_INT, 'The category ID'), + 'name' => new external_value(PARAM_TEXT, 'The category name'), + ] + ); + + return new external_single_structure([ + 'options' => new external_multiple_structure($optiondefinition), + 'warnings' => new external_warnings() + ]); + } + + /** + * Parameters for get_purpose_options(). + * + * @return external_function_parameters + */ + public static function get_purpose_options_parameters() { + return new external_function_parameters([ + 'includeinherit' => new external_value(PARAM_BOOL, 'Include option "Inherit"', VALUE_DEFAULT, true), + 'includenotset' => new external_value(PARAM_BOOL, 'Include option "Not set"', VALUE_DEFAULT, false), + ]); + } + + /** + * Fetches a list of data storage purposes containing purpose IDs as keys and the purpose name for the value. + * + * @param bool $includeinherit Whether to include the "Inherit" option. + * @param bool $includenotset Whether to include the "Not set" option. + * @return array + */ + public static function get_purpose_options($includeinherit, $includenotset) { + $warnings = []; + + $params = self::validate_parameters(self::get_category_options_parameters(), [ + 'includeinherit' => $includeinherit, + 'includenotset' => $includenotset + ]); + $includeinherit = $params['includeinherit']; + $includenotset = $params['includenotset']; + + $context = context_system::instance(); + self::validate_context($context); + + $purposes = api::get_purposes(); + $purposeoptions = []; + if ($includeinherit) { + $purposeoptions[] = [ + 'id' => context_instance::INHERIT, + 'name' => get_string('inherit', 'tool_dataprivacy'), + ]; + } + if ($includenotset) { + $purposeoptions[] = [ + 'id' => context_instance::NOTSET, + 'name' => get_string('notset', 'tool_dataprivacy'), + ]; + } + foreach ($purposes as $purpose) { + $purposeoptions[] = [ + 'id' => $purpose->get('id'), + 'name' => $purpose->get('name'), + ]; + } + + return [ + 'options' => $purposeoptions, + 'warnings' => $warnings + ]; + } + + /** + * Returns for get_purpose_options(). + * + * @return external_single_structure + */ + public static function get_purpose_options_returns() { + $optiondefinition = new external_single_structure( + [ + 'id' => new external_value(PARAM_INT, 'The purpose ID'), + 'name' => new external_value(PARAM_TEXT, 'The purpose name'), + ] + ); + + return new external_single_structure([ + 'options' => new external_multiple_structure($optiondefinition), + 'warnings' => new external_warnings() + ]); + } + + /** + * Parameters for get_activity_options(). + * + * @return external_function_parameters + */ + public static function get_activity_options_parameters() { + return new external_function_parameters([ + 'nodefaults' => new external_value(PARAM_BOOL, 'Whether to fetch all activities or only those without defaults', + VALUE_DEFAULT, false), + ]); + } + + /** + * Fetches a list of activity options for setting data registry defaults. + * + * @param boolean $nodefaults If false, it will fetch all of the activities. Otherwise, it will only fetch the activities + * that don't have defaults yet (e.g. when adding a new activity module defaults). + * @return array + */ + public static function get_activity_options($nodefaults) { + $warnings = []; + + $params = self::validate_parameters(self::get_activity_options_parameters(), [ + 'nodefaults' => $nodefaults, + ]); + $nodefaults = $params['nodefaults']; + + $context = context_system::instance(); + self::validate_context($context); + + // Get activity module plugin info. + $pluginmanager = \core_plugin_manager::instance(); + $modplugins = $pluginmanager->get_plugins_of_type('mod'); + $modoptions = []; + foreach ($modplugins as $plugin) { + // Check if we have default purpose and category for this module if we want don't want to fetch everything. + if ($nodefaults) { + list($purpose, $category) = data_registry::get_defaults(CONTEXT_MODULE, $plugin->name); + if ($purpose !== false || $category !== false) { + // If so, skip it. + continue; + } + } + + $modoptions[] = (object)[ + 'name' => $plugin->name, + 'displayname' => $plugin->displayname + ]; + } + + return [ + 'options' => $modoptions, + 'warnings' => $warnings + ]; + } + + /** + * Returns for get_category_options(). + * + * @return external_single_structure + */ + public static function get_activity_options_returns() { + $optionsdefinition = new external_single_structure( + [ + 'name' => new external_value(PARAM_TEXT, 'The plugin name of the activity'), + 'displayname' => new external_value(PARAM_TEXT, 'The display name of the activity'), + ] + ); + + return new external_single_structure([ + 'options' => new external_multiple_structure($optionsdefinition), + 'warnings' => new external_warnings() + ]); + } + /** * Gets the structure of a tree node (link + child branches). * diff --git a/admin/tool/dataprivacy/classes/local/helper.php b/admin/tool/dataprivacy/classes/local/helper.php index 36dd93aac2b..d1960082f9b 100644 --- a/admin/tool/dataprivacy/classes/local/helper.php +++ b/admin/tool/dataprivacy/classes/local/helper.php @@ -27,6 +27,9 @@ defined('MOODLE_INTERNAL') || die(); use coding_exception; use moodle_exception; use tool_dataprivacy\api; +use tool_dataprivacy\category; +use tool_dataprivacy\context_instance; +use tool_dataprivacy\purpose; /** * Class containing helper functions for the data privacy tool. @@ -209,4 +212,38 @@ class helper { } return $options; } + + /** + * Fetches a category name from the given ID. + * + * @param int $category The category ID. Could be INHERIT (false, -1), NOT_SET (0), or the actual ID. + * @return string The category name. + */ + public static function get_category_name($category) { + if ($category === false || $category == context_instance::INHERIT) { + return get_string('inherit', 'tool_dataprivacy'); + } else if ($category == context_instance::NOTSET) { + return get_string('notset', 'tool_dataprivacy'); + } else { + $category = new category($category); + return format_string($category->get('name')); + } + } + + /** + * Fetches a purpose name from the given ID. + * + * @param int $purpose The purpose ID. Could be INHERIT (false, -1), NOT_SET (0), or the actual ID. + * @return string The purpose name. + */ + public static function get_purpose_name($purpose) { + if ($purpose === false || $purpose == context_instance::INHERIT) { + return get_string('inherit', 'tool_dataprivacy'); + } else if ($purpose == context_instance::NOTSET) { + return get_string('notset', 'tool_dataprivacy'); + } else { + $purpose = new purpose($purpose); + return format_string($purpose->get('name')); + } + } } diff --git a/admin/tool/dataprivacy/classes/output/defaults_page.php b/admin/tool/dataprivacy/classes/output/defaults_page.php new file mode 100644 index 00000000000..16907dd54c9 --- /dev/null +++ b/admin/tool/dataprivacy/classes/output/defaults_page.php @@ -0,0 +1,151 @@ +. + +/** + * Class containing data for the data registry defaults. + * + * @package tool_dataprivacy + * @copyright 2018 Jun Pataleta + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace tool_dataprivacy\output; +defined('MOODLE_INTERNAL') || die(); + +use coding_exception; +use moodle_exception; +use moodle_url; +use renderable; +use renderer_base; +use stdClass; +use templatable; +use tool_dataprivacy\data_registry; +use tool_dataprivacy\local\helper; + +/** + * Class containing data for the data registry defaults. + * + * @copyright 2018 Jun Pataleta + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class defaults_page implements renderable, templatable { + + /** @var int $mode The display mode. */ + protected $mode = null; + + /** @var int $category The default category for the given mode. */ + protected $category = null; + + /** @var int $purpose The default purpose for the given mode. */ + protected $purpose = null; + + /** @var stdClass[] $otherdefaults Other defaults for the given mode. */ + protected $otherdefaults = []; + + /** @var bool $canedit Whether editing is allowed. */ + protected $canedit = false; + + /** + * Construct this renderable. + * + * @param int $mode The display mode. + * @param int $category The default category for the given mode. + * @param int $purpose The default purpose for the given mode. + * @param stdClass[] $otherdefaults Other defaults for the given mode. + * @param bool $canedit Whether editing is allowed. + */ + public function __construct($mode, $category, $purpose, $otherdefaults = [], $canedit = false) { + $this->mode = $mode; + $this->category = $category; + $this->purpose = $purpose; + $this->otherdefaults = $otherdefaults; + $this->canedit = $canedit; + } + + /** + * Export this data so it can be used as the context for a mustache template. + * + * @param renderer_base $output + * @return stdClass + * @throws coding_exception + * @throws moodle_exception + */ + public function export_for_template(renderer_base $output) { + $data = new stdClass(); + + // Set tab URLs. + $coursecaturl = new moodle_url('/admin/tool/dataprivacy/defaults.php', ['mode' => CONTEXT_COURSECAT]); + $courseurl = new moodle_url('/admin/tool/dataprivacy/defaults.php', ['mode' => CONTEXT_COURSE]); + $moduleurl = new moodle_url('/admin/tool/dataprivacy/defaults.php', ['mode' => CONTEXT_MODULE]); + $blockurl = new moodle_url('/admin/tool/dataprivacy/defaults.php', ['mode' => CONTEXT_BLOCK]); + $data->coursecaturl = $coursecaturl; + $data->courseurl = $courseurl; + $data->moduleurl = $moduleurl; + $data->blockurl = $blockurl; + + // Set display mode. + switch ($this->mode) { + case CONTEXT_COURSECAT: + $data->modecoursecat = true; + break; + case CONTEXT_COURSE: + $data->modecourse = true; + break; + case CONTEXT_MODULE: + $data->modemodule = true; + break; + case CONTEXT_BLOCK: + $data->modeblock = true; + break; + default: + $data->modecoursecat = true; + break; + } + + // Set config variables. + $configname = \context_helper::get_class_for_level($this->mode); + list($purposevar, $categoryvar) = data_registry::var_names_from_context($configname); + $data->categoryvar = $categoryvar; + $data->purposevar = $purposevar; + + // Set default category. + $data->categoryid = $this->category; + $data->category = helper::get_category_name($this->category); + + // Set default purpose. + $data->purposeid = $this->purpose; + $data->purpose = helper::get_purpose_name($this->purpose); + + // Set other defaults. + $otherdefaults = []; + foreach ($this->otherdefaults as $pluginname => $values) { + $defaults = [ + 'activityname' => $pluginname, + 'name' => $values->name, + 'categoryid' => $values->category, + 'category' => helper::get_category_name($values->category), + 'purposeid' => $values->purpose, + 'purpose' => helper::get_purpose_name($values->purpose), + ]; + $otherdefaults[] = (object)$defaults; + } + $data->otherdefaults = $otherdefaults; + + $data->canedit = $this->canedit; + $data->contextlevel = $this->mode; + + return $data; + } +} diff --git a/admin/tool/dataprivacy/db/services.php b/admin/tool/dataprivacy/db/services.php index 9c71e8c4dc2..b9e60774b14 100644 --- a/admin/tool/dataprivacy/db/services.php +++ b/admin/tool/dataprivacy/db/services.php @@ -173,4 +173,44 @@ $functions = [ 'ajax' => true, 'loginrequired' => true, ], + 'tool_dataprivacy_set_context_defaults' => [ + 'classname' => 'tool_dataprivacy\external', + 'methodname' => 'set_context_defaults', + 'classpath' => '', + 'description' => 'Updates the default category and purpose for a given context level (and optionally, a plugin)', + 'type' => 'write', + 'capabilities' => 'tool/dataprivacy:managedataregistry', + 'ajax' => true, + 'loginrequired' => true, + ], + 'tool_dataprivacy_get_category_options' => [ + 'classname' => 'tool_dataprivacy\external', + 'methodname' => 'get_category_options', + 'classpath' => '', + 'description' => 'Fetches a list of data category options', + 'type' => 'read', + 'capabilities' => 'tool/dataprivacy:managedataregistry', + 'ajax' => true, + 'loginrequired' => true, + ], + 'tool_dataprivacy_get_purpose_options' => [ + 'classname' => 'tool_dataprivacy\external', + 'methodname' => 'get_purpose_options', + 'classpath' => '', + 'description' => 'Fetches a list of data storage purpose options', + 'type' => 'read', + 'capabilities' => 'tool/dataprivacy:managedataregistry', + 'ajax' => true, + 'loginrequired' => true, + ], + 'tool_dataprivacy_get_activity_options' => [ + 'classname' => 'tool_dataprivacy\external', + 'methodname' => 'get_activity_options', + 'classpath' => '', + 'description' => 'Fetches a list of activity options', + 'type' => 'read', + 'capabilities' => 'tool/dataprivacy:managedataregistry', + 'ajax' => true, + 'loginrequired' => true, + ], ]; diff --git a/admin/tool/dataprivacy/defaults.php b/admin/tool/dataprivacy/defaults.php index d936ba221d6..24b2862b5b1 100644 --- a/admin/tool/dataprivacy/defaults.php +++ b/admin/tool/dataprivacy/defaults.php @@ -32,47 +32,37 @@ $title = get_string('setdefaults', 'tool_dataprivacy'); \tool_dataprivacy\page_helper::setup($url, $title, 'dataregistry'); -$levels = \context_helper::get_all_levels(); -// They are set through the context level site and user. -unset($levels[CONTEXT_SYSTEM]); -unset($levels[CONTEXT_USER]); +$mode = optional_param('mode', CONTEXT_COURSECAT, PARAM_INT); +$classname = context_helper::get_class_for_level($mode); +list($purposevar, $categoryvar) = \tool_dataprivacy\data_registry::var_names_from_context($classname); +$purpose = get_config('tool_dataprivacy', $purposevar); +$category = get_config('tool_dataprivacy', $categoryvar); -$customdata = [ - 'levels' => $levels, - 'purposes' => \tool_dataprivacy\api::get_purposes(), - 'categories' => \tool_dataprivacy\api::get_categories(), -]; -$form = new \tool_dataprivacy\form\defaults($PAGE->url->out(false), $customdata); +$otherdefaults = []; +if ($mode == CONTEXT_MODULE) { + // Get activity module plugin info. + $pluginmanager = core_plugin_manager::instance(); + $modplugins = $pluginmanager->get_plugins_of_type('mod'); -$toform = new stdClass(); -foreach ($levels as $level => $classname) { - list($purposevar, $categoryvar) = \tool_dataprivacy\data_registry::var_names_from_context($classname); - $toform->{$purposevar} = get_config('tool_dataprivacy', $purposevar); - $toform->{$categoryvar} = get_config('tool_dataprivacy', $categoryvar); -} -$form->set_data($toform); - -$returnurl = new \moodle_url('/admin/tool/dataprivacy/dataregistry.php'); -if ($form->is_cancelled()) { - redirect($returnurl); -} else if ($data = $form->get_data()) { - - foreach ($levels as $level => $classname) { - - list($purposevar, $categoryvar) = \tool_dataprivacy\data_registry::var_names_from_context($classname); - - if (isset($data->{$purposevar})) { - set_config($purposevar, $data->{$purposevar}, 'tool_dataprivacy'); - } - if (isset($data->{$categoryvar})) { - set_config($categoryvar, $data->{$categoryvar}, 'tool_dataprivacy'); + foreach ($modplugins as $plugin) { + list($purposevar, $categoryvar) = \tool_dataprivacy\data_registry::var_names_from_context($classname, $plugin->name); + $plugincategory = get_config('tool_dataprivacy', $categoryvar); + $pluginpurpose = get_config('tool_dataprivacy', $purposevar); + if ($plugincategory === false && $pluginpurpose === false) { + // If no purpose and category has been set for this plugin, then there's no need to show this on the list. + continue; } + $otherdefaults[$plugin->name] = (object)[ + 'name' => $plugin->displayname, + 'category' => $plugincategory, + 'purpose' => $pluginpurpose, + ]; } - redirect($returnurl, get_string('defaultssaved', 'tool_dataprivacy'), - 0, \core\output\notification::NOTIFY_SUCCESS); } +$defaultspage = new \tool_dataprivacy\output\defaults_page($mode, $category, $purpose, $otherdefaults, true); + $output = $PAGE->get_renderer('tool_dataprivacy'); echo $output->header(); -$form->display(); +echo $output->render_from_template('tool_dataprivacy/defaults_page', $defaultspage->export_for_template($output)); echo $output->footer(); diff --git a/admin/tool/dataprivacy/lang/en/tool_dataprivacy.php b/admin/tool/dataprivacy/lang/en/tool_dataprivacy.php index eb2c97fc83c..682dc99ac14 100644 --- a/admin/tool/dataprivacy/lang/en/tool_dataprivacy.php +++ b/admin/tool/dataprivacy/lang/en/tool_dataprivacy.php @@ -28,6 +28,7 @@ $string['pluginname'] = 'Data privacy'; $string['pluginname_help'] = 'Data privacy plugin'; $string['activitiesandresources'] = 'Activities and resources'; $string['addcategory'] = 'Add category'; +$string['addnewdefaults'] = 'Add a new module default'; $string['addpurpose'] = 'Add purpose'; $string['approve'] = 'Approve'; $string['approverequest'] = 'Approve request'; @@ -76,9 +77,12 @@ $string['datarequests'] = 'Data requests'; $string['datecomment'] = '[{$a->date}]: ' . PHP_EOL . ' {$a->comment}'; $string['daterequested'] = 'Date requested'; $string['daterequesteddetail'] = 'Date requested:'; -$string['defaultsinfo'] = 'Default categories and purposes are applied to all newly created instances.'; +$string['defaultsinfo'] = 'Default categories and purposes are applied to all new and existing instances where a value is not set.'; +$string['defaultswarninginfo'] = 'Warning: Changing these defaults may affect the retention period of existing instances.'; $string['deletecategory'] = 'Delete category'; $string['deletecategorytext'] = 'Are you sure you want to delete the category \'{$a}\'?'; +$string['deletedefaults'] = 'Delete defaults: {$a}'; +$string['deletedefaultsconfirmation'] = 'Are you sure you want to delete the default category and purpose for {$a} modules?'; $string['deleteexpiredcontextstask'] = 'Delete expired contexts'; $string['deleteexpireddatarequeststask'] = 'Delete files from completed data requests that have expired'; $string['deletepurpose'] = 'Delete purpose'; @@ -95,6 +99,8 @@ $string['dporolemapping_desc'] = 'The privacy officer can manage data requests. $string['editcategories'] = 'Edit categories'; $string['editcategory'] = 'Edit category'; $string['editcategories'] = 'Edit categories'; +$string['editdefaults'] = 'Edit defaults: {$a}'; +$string['editmoduledefaults'] = 'Edit module defaults'; $string['editpurpose'] = 'Edit purpose'; $string['editpurposes'] = 'Edit purposes'; $string['effectiveretentionperiodcourse'] = '{$a} (after the course end date)'; @@ -185,6 +191,7 @@ $string['nopurposes'] = 'There are no purposes yet'; $string['nosubjectaccessrequests'] = 'There are no data requests that you need to act on'; $string['nosystemdefaults'] = 'Site purpose and category have not yet been defined.'; $string['notset'] = 'Not set (use the default value)'; +$string['overrideinstances'] = 'Reset instances with custom values'; $string['pluginregistry'] = 'Plugin privacy registry'; $string['pluginregistrytitle'] = 'Plugin privacy compliance registry'; $string['privacy'] = 'Privacy'; diff --git a/admin/tool/dataprivacy/templates/category_purpose_form.mustache b/admin/tool/dataprivacy/templates/category_purpose_form.mustache new file mode 100644 index 00000000000..c5d0ed4bea6 --- /dev/null +++ b/admin/tool/dataprivacy/templates/category_purpose_form.mustache @@ -0,0 +1,99 @@ +{{! + 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 . +}} +{{! + @template tool_dataprivacy/category_purpose_form + + Manage data registry defaults. + + Classes required for JS: + + Data attributes required for JS: + + Context variables required for this template: + * actionurl String The action URL. + * contextlevel Number - The context level. + * modemodule Boolean - Whether to display the activity select element. + * activityoptions Array - An array of objects for the activity select element. + * categoryoptions Array - An array of objects for the category select element. + * purposeoptions Array - An array of objects for the purpose select element. + + Example context (json): + { + "actionurl": "#", + "contextlevel": 70, + "newactivitydefaults": true, + "modemodule": true, + "activityoptions": [ + { "name": "assign", "displayname": "Assignment" }, + { "name": "forum", "displayname": "Forum", "selected": true }, + { "name": "lesson", "displayname": "Lesson" }, + { "name": "quiz", "displayname": "Quiz" } + ], + "categoryoptions": [ + { "id": 1, "name": "Category 1" }, + { "id": 2, "name": "Category 2", "selected": true }, + { "id": 3, "name": "Category 3" } + ], + "purposeoptions": [ + { "id": 1, "name": "Purpose 1" }, + { "id": 2, "name": "Purpose 2" }, + { "id": 3, "name": "Purpose 3", "selected": true } + ] + } +}} + + +
+ + {{#modemodule}} +
+ + {{^newactivitydefaults}} + + {{/newactivitydefaults}} + +
+ {{/modemodule}} +
+ + +
+
+ + +
+
+ + +
+
diff --git a/admin/tool/dataprivacy/templates/defaults_display.mustache b/admin/tool/dataprivacy/templates/defaults_display.mustache new file mode 100644 index 00000000000..1abce61f68b --- /dev/null +++ b/admin/tool/dataprivacy/templates/defaults_display.mustache @@ -0,0 +1,74 @@ +{{! + 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 . +}} +{{! + @template tool_dataprivacy/defaults_display + + Renders the current default category and purpose. + + Classes required for JS: + + Data attributes required for JS: + + Context variables required for this template: + * contextlevel Number - The context level. + * categoryid Number - The category ID. + * purposeid Number - The purpose ID. + * category String - The category name. + * purpose String - The purpose name. + * canedit Boolean - Whether this is being rendered for editing purposes. + + Example context (json): + { + "category": "Awesome default category", + "categoryid": 1, + "purpose": "Awesome default purpose", + "purposeid": 2, + "canedit": true, + "contextlevel": 70 + } +}} +
+
+
+
+ {{#str}}category, tool_dataprivacy{{/str}} +
+
+ {{category}} +
+
+
+
+ {{#str}}purpose, tool_dataprivacy{{/str}} +
+
+ {{purpose}} +
+
+
+
+ {{#canedit}} + + {{/canedit}} +
+
diff --git a/admin/tool/dataprivacy/templates/defaults_page.mustache b/admin/tool/dataprivacy/templates/defaults_page.mustache new file mode 100644 index 00000000000..c7777c1428c --- /dev/null +++ b/admin/tool/dataprivacy/templates/defaults_page.mustache @@ -0,0 +1,152 @@ +{{! + 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 . +}} +{{! + @template tool_dataprivacy/defaults_page + + Manage data registry defaults. + + Classes required for JS: + + Data attributes required for JS: + + Context variables required for this template: + * contextlevel Number - The context level. + * modecoursecat Boolean - Whether we're displaying defaults for course categories. + * modecourse Boolean - Whether we're displaying defaults for courses. + * modemodule Boolean - Whether we're displaying defaults for activity modules. + * modeblock Boolean - Whether we're displaying defaults for blocks. + * coursecaturl String - The URL for the course category defaults tab. + * courseurl String - The URL for the course defaults tab. + * moduleurl String - The URL for the activity module defaults tab. + * blockurl String - The URL for the block defaults tab. + * purposeid Number - The purpose ID for this context level. + * canedit Boolean - Whether this is being rendered for editing purposes. + * categoryid Number - The ID of the default category for this context level. + * purposeid Number - The ID of the default purpose for this context level. + * category String - The category name. + * purpose String - The purpose name. + * otherdefaults Array - An array containing the defaults for the activity modules. + + Example context (json): + { + "contextlevel": 70, + "modecoursecat": false, + "modecourse": false, + "modemodule": true, + "modeblock": false, + "coursecaturl": "#", + "courseurl": "#", + "moduleurl": "#", + "blockurl": "#", + "category": "Awesome default category", + "purpose": "Awesome default purpose", + "canedit": true, + "otherdefaults": [ + { + "name": "Assignment", + "category": "Category for activity modules", + "purpose": "Assessments" + }, + { + "name": "Forum", + "category": "Category for activity modules", + "purpose": "Social interactions" + } + ] + } +}} + + +
+ +
+ +

+ {{#modecoursecat}}{{#str}}categories{{/str}}{{/modecoursecat}} + {{#modecourse}}{{#str}}courses{{/str}}{{/modecourse}} + {{#modemodule}}{{#str}}activitymodules{{/str}}{{/modemodule}} + {{#modeblock}}{{#str}}blocks{{/str}}{{/modeblock}} +

+
+ {{> tool_dataprivacy/defaults_display}} + {{#canedit}} + {{#modemodule}} + + {{/modemodule}} + {{/canedit}} + {{#modemodule}} + + + + + + + {{#canedit}} + + {{/canedit}} + + + + {{#otherdefaults}} + + + + + {{#canedit}} + + {{/canedit}} + + {{/otherdefaults}} + +
 {{#str}}category, tool_dataprivacy{{/str}}{{#str}}purpose, tool_dataprivacy{{/str}}{{#str}}actions{{/str}}
{{name}}{{category}}{{purpose}} + + {{#pix}}t/edit, core, {{#str}}edit{{/str}}{{/pix}} + + + {{#pix}}t/delete, core, {{#str}}delete{{/str}}{{/pix}} + +
+ {{/modemodule}} +
+
+
+ +{{#js}} +// Initialise the JS. +require(['tool_dataprivacy/defaultsactions'], function(ActionsMod) { + ActionsMod.init(); +}); +{{/js}} diff --git a/admin/tool/dataprivacy/templates/delete_activity_defaults.mustache b/admin/tool/dataprivacy/templates/delete_activity_defaults.mustache new file mode 100644 index 00000000000..c0220d3164d --- /dev/null +++ b/admin/tool/dataprivacy/templates/delete_activity_defaults.mustache @@ -0,0 +1,41 @@ +{{! + 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 . +}} +{{! + @template tool_dataprivacy/delete_activity_defaults + + Renders the confirmation. + + Classes required for JS: + + Data attributes required for JS: + * none + + Context variables required for this template: + * activityname String - The activity name. + + Example context (json): + { + "activityname": "Assignment" + } +}} + + +
+ {{#str}}deletedefaultsconfirmation, tool_dataprivacy, {{activityname}}{{/str}} +