MDL-62026 tool_dataprivacy: Allow manual completion of general enquiries
This commit is contained in:
@@ -1 +1 @@
|
||||
define(["jquery","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","tool_dataprivacy/events"],function(a,b,c,d,e,f){var g=!1,h={APPROVE_BUTTON:'[data-action="approve"]',DENY_BUTTON:'[data-action="deny"]'},i=function(a){d.call(this,a),this.getFooter().find(h.APPROVE_BUTTON).length||b.exception({message:"No approve button found"}),this.getFooter().find(h.DENY_BUTTON).length||b.exception({message:"No deny button found"})};return i.TYPE="tool_dataprivacy-data_request",i.prototype=Object.create(d.prototype),i.prototype.constructor=i,i.prototype.registerEventListeners=function(){d.prototype.registerEventListeners.call(this),this.getModal().on(c.events.activate,h.APPROVE_BUTTON,function(b,c){var d=a.Event(f.approve);this.getRoot().trigger(d,this),d.isDefaultPrevented()||(this.hide(),c.originalEvent.preventDefault())}.bind(this)),this.getModal().on(c.events.activate,h.DENY_BUTTON,function(b,c){var d=a.Event(f.deny);this.getRoot().trigger(d,this),d.isDefaultPrevented()||(this.hide(),c.originalEvent.preventDefault())}.bind(this))},g||(e.register(i.TYPE,i,"tool_dataprivacy/data_request_modal"),g=!0),i});
|
||||
define(["jquery","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","tool_dataprivacy/events"],function(a,b,c,d,e,f){var g=!1,h={APPROVE_BUTTON:'[data-action="approve"]',DENY_BUTTON:'[data-action="deny"]',COMPLETE_BUTTON:'[data-action="complete"]'},i=function(a){d.call(this,a)};return i.TYPE="tool_dataprivacy-data_request",i.prototype=Object.create(d.prototype),i.prototype.constructor=i,i.prototype.registerEventListeners=function(){d.prototype.registerEventListeners.call(this),this.getModal().on(c.events.activate,h.APPROVE_BUTTON,function(b,c){var d=a.Event(f.approve);this.getRoot().trigger(d,this),d.isDefaultPrevented()||(this.hide(),c.originalEvent.preventDefault())}.bind(this)),this.getModal().on(c.events.activate,h.DENY_BUTTON,function(b,c){var d=a.Event(f.deny);this.getRoot().trigger(d,this),d.isDefaultPrevented()||(this.hide(),c.originalEvent.preventDefault())}.bind(this)),this.getModal().on(c.events.activate,h.COMPLETE_BUTTON,function(b,c){var d=a.Event(f.complete);this.getRoot().trigger(d,this),d.isDefaultPrevented()||(this.hide(),c.originalEvent.preventDefault())}.bind(this))},g||(e.register(i.TYPE,i,"tool_dataprivacy/data_request_modal"),g=!0),i});
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define([],function(){return{approve:"tool_dataprivacy-data_request:approve",deny:"tool_dataprivacy-data_request:deny"}});
|
||||
define([],function(){return{approve:"tool_dataprivacy-data_request:approve",deny:"tool_dataprivacy-data_request:deny",complete:"tool_dataprivacy-data_request:complete"}});
|
||||
+1
-1
@@ -1 +1 @@
|
||||
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(a,b,c,d,e,f,g,h,i){function j(a,g){var h=[],j="";switch(a){case i.approve:h=[{key:"approverequest",component:"tool_dataprivacy"},{key:"confirmapproval",component:"tool_dataprivacy"}],j="tool_dataprivacy_approve_data_request";break;case i.deny:h=[{key:"denyrequest",component:"tool_dataprivacy"},{key:"confirmdenial",component:"tool_dataprivacy"}],j="tool_dataprivacy_deny_data_request"}var k="";d.get_strings(h).then(function(a){k=a[0];var b=a[1];return e.create({title:k,body:b,type:e.types.SAVE_CANCEL})}).then(function(a){return a.setSaveButtonText(k),a.getRoot().on(f.save,function(){var a={requestid:g},d={methodname:j,args:a};b.call([d])[0].done(function(a){a.result?window.location.reload():c.addNotification({message:a.warnings[0].message,type:"error"})}).fail(c.exception)}),a.getRoot().on(f.hidden,function(){a.destroy()}),a}).done(function(a){a.show()}).fail(c.exception)}var k={APPROVE_REQUEST:'[data-action="approve"]',DENY_REQUEST:'[data-action="deny"]',VIEW_REQUEST:'[data-action="view"]'},l=function(){this.registerEvents()};return l.prototype.registerEvents=function(){a(k.VIEW_REQUEST).click(function(d){d.preventDefault();var k=a(this).data("requestid"),l={requestid:k},m={methodname:"tool_dataprivacy_get_data_request",args:l},n=b.call([m]),o="",p=e.types.DEFAULT;a.when(n[0]).then(function(a){return a.result?(2==a.result.status&&(p=h.TYPE),o=a.result.typename,g.render("tool_dataprivacy/request_details",a.result)):(c.addNotification({message:a.warnings[0].message,type:"error"}),!1)}).then(function(a){return e.create({title:o,body:a,type:p,large:!0}).then(function(a){return a.getRoot().on(i.approve,function(){j(i.approve,k)}),a.getRoot().on(i.deny,function(){j(i.deny,k)}),a.getRoot().on(f.hidden,function(){a.destroy()}),a})}).done(function(a){a.show()}).fail(c.exception)}),a(k.APPROVE_REQUEST).click(function(b){b.preventDefault();var c=a(this).data("requestid");j(i.approve,c)}),a(k.DENY_REQUEST).click(function(b){b.preventDefault();var c=a(this).data("requestid");j(i.deny,c)})},l});
|
||||
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(a,b,c,d,e,f,g,h,i){function j(a,b){var g=[],h="",j={requestid:b};switch(a){case i.approve:g=[{key:"approverequest",component:"tool_dataprivacy"},{key:"confirmapproval",component:"tool_dataprivacy"}],h="tool_dataprivacy_approve_data_request";break;case i.deny:g=[{key:"denyrequest",component:"tool_dataprivacy"},{key:"confirmdenial",component:"tool_dataprivacy"}],h="tool_dataprivacy_deny_data_request";break;case i.complete:g=[{key:"markcomplete",component:"tool_dataprivacy"},{key:"confirmcompletion",component:"tool_dataprivacy"}],h="tool_dataprivacy_mark_complete"}var l="";d.get_strings(g).then(function(a){l=a[0];var b=a[1];return e.create({title:l,body:b,type:e.types.SAVE_CANCEL})}).then(function(a){a.setSaveButtonText(l),a.getRoot().on(f.save,function(){k(h,j)}),a.getRoot().on(f.hidden,function(){a.destroy()}),a.show()})["catch"](c.exception)}function k(a,d){var e={methodname:a,args:d};b.call([e])[0].done(function(a){a.result?window.location.reload():c.addNotification({message:a.warnings[0].message,type:"error"})}).fail(c.exception)}var l={APPROVE_REQUEST:'[data-action="approve"]',DENY_REQUEST:'[data-action="deny"]',VIEW_REQUEST:'[data-action="view"]',MARK_COMPLETE:'[data-action="complete"]'},m=function(){this.registerEvents()};return m.prototype.registerEvents=function(){a(l.VIEW_REQUEST).click(function(d){d.preventDefault();var l=a(this).data("requestid"),m={requestid:l},n={methodname:"tool_dataprivacy_get_data_request",args:m},o=b.call([n]);a.when(o[0]).then(function(a){return a.result?a.result:(c.addNotification({message:a.warnings[0].message,type:"error"}),!1)}).then(function(a){var b=g.render("tool_dataprivacy/request_details",a),c={approvedeny:a.approvedeny,canmarkcomplete:a.canmarkcomplete};return e.create({title:a.typename,body:b,type:h.TYPE,large:!0,templateContext:c})}).then(function(a){a.getRoot().on(i.approve,function(){j(i.approve,l)}),a.getRoot().on(i.deny,function(){j(i.deny,l)}),a.getRoot().on(i.complete,function(){var a={requestid:l};k("tool_dataprivacy_mark_complete",a)}),a.getRoot().on(f.hidden,function(){a.destroy()}),a.show()})["catch"](c.exception)}),a(l.APPROVE_REQUEST).click(function(b){b.preventDefault();var c=a(this).data("requestid");j(i.approve,c)}),a(l.DENY_REQUEST).click(function(b){b.preventDefault();var c=a(this).data("requestid");j(i.deny,c)}),a(l.MARK_COMPLETE).click(function(b){b.preventDefault(),j(i.complete,a(this).data("requestid"))})},m});
|
||||
@@ -29,6 +29,7 @@ define(['jquery', 'core/notification', 'core/custom_interaction_events', 'core/m
|
||||
var SELECTORS = {
|
||||
APPROVE_BUTTON: '[data-action="approve"]',
|
||||
DENY_BUTTON: '[data-action="deny"]',
|
||||
COMPLETE_BUTTON: '[data-action="complete"]'
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -38,14 +39,6 @@ define(['jquery', 'core/notification', 'core/custom_interaction_events', 'core/m
|
||||
*/
|
||||
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';
|
||||
@@ -80,6 +73,16 @@ define(['jquery', 'core/notification', 'core/custom_interaction_events', 'core/m
|
||||
data.originalEvent.preventDefault();
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this.getModal().on(CustomEvents.events.activate, SELECTORS.COMPLETE_BUTTON, function(e, data) {
|
||||
var completeEvent = $.Event(DataPrivacyEvents.complete);
|
||||
this.getRoot().trigger(completeEvent, this);
|
||||
|
||||
if (!completeEvent.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
|
||||
|
||||
@@ -26,5 +26,6 @@ define([], function() {
|
||||
return {
|
||||
approve: 'tool_dataprivacy-data_request:approve',
|
||||
deny: 'tool_dataprivacy-data_request:deny',
|
||||
complete: 'tool_dataprivacy-data_request:complete'
|
||||
};
|
||||
});
|
||||
|
||||
@@ -39,11 +39,13 @@ function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, Modal
|
||||
* @type {{APPROVE_REQUEST: string}}
|
||||
* @type {{DENY_REQUEST: string}}
|
||||
* @type {{VIEW_REQUEST: string}}
|
||||
* @type {{MARK_COMPLETE: string}}
|
||||
*/
|
||||
var ACTIONS = {
|
||||
APPROVE_REQUEST: '[data-action="approve"]',
|
||||
DENY_REQUEST: '[data-action="deny"]',
|
||||
VIEW_REQUEST: '[data-action="view"]'
|
||||
VIEW_REQUEST: '[data-action="view"]',
|
||||
MARK_COMPLETE: '[data-action="complete"]'
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -73,16 +75,9 @@ function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, Modal
|
||||
};
|
||||
|
||||
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);
|
||||
return data.result;
|
||||
}
|
||||
// Fail.
|
||||
Notification.addNotification({
|
||||
@@ -91,35 +86,51 @@ function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, Modal
|
||||
});
|
||||
return false;
|
||||
|
||||
}).then(function(html) {
|
||||
}).then(function(data) {
|
||||
var body = Templates.render('tool_dataprivacy/request_details', data);
|
||||
var templateContext = {
|
||||
approvedeny: data.approvedeny,
|
||||
canmarkcomplete: data.canmarkcomplete
|
||||
};
|
||||
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;
|
||||
title: data.typename,
|
||||
body: body,
|
||||
type: ModalDataRequest.TYPE,
|
||||
large: true,
|
||||
templateContext: templateContext
|
||||
});
|
||||
}).done(function(modal) {
|
||||
|
||||
}).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 send event.
|
||||
modal.getRoot().on(DataPrivacyEvents.complete, function() {
|
||||
var params = {
|
||||
'requestid': requestId
|
||||
};
|
||||
handleSave('tool_dataprivacy_mark_complete', params);
|
||||
});
|
||||
|
||||
// Handle hidden event.
|
||||
modal.getRoot().on(ModalEvents.hidden, function() {
|
||||
// Destroy when hidden.
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
// Show the modal!
|
||||
modal.show();
|
||||
}).fail(Notification.exception);
|
||||
|
||||
return;
|
||||
|
||||
}).catch(Notification.exception);
|
||||
});
|
||||
|
||||
$(ACTIONS.APPROVE_REQUEST).click(function(e) {
|
||||
@@ -135,6 +146,11 @@ function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, Modal
|
||||
var requestId = $(this).data('requestid');
|
||||
showConfirmation(DataPrivacyEvents.deny, requestId);
|
||||
});
|
||||
|
||||
$(ACTIONS.MARK_COMPLETE).click(function(e) {
|
||||
e.preventDefault();
|
||||
showConfirmation(DataPrivacyEvents.complete, $(this).data('requestid'));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -146,6 +162,9 @@ function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, Modal
|
||||
function showConfirmation(action, requestId) {
|
||||
var keys = [];
|
||||
var wsfunction = '';
|
||||
var params = {
|
||||
'requestid': requestId
|
||||
};
|
||||
switch (action) {
|
||||
case DataPrivacyEvents.approve:
|
||||
keys = [
|
||||
@@ -173,6 +192,19 @@ function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, Modal
|
||||
];
|
||||
wsfunction = 'tool_dataprivacy_deny_data_request';
|
||||
break;
|
||||
case DataPrivacyEvents.complete:
|
||||
keys = [
|
||||
{
|
||||
key: 'markcomplete',
|
||||
component: 'tool_dataprivacy'
|
||||
},
|
||||
{
|
||||
key: 'confirmcompletion',
|
||||
component: 'tool_dataprivacy'
|
||||
}
|
||||
];
|
||||
wsfunction = 'tool_dataprivacy_mark_complete';
|
||||
break;
|
||||
}
|
||||
|
||||
var modalTitle = '';
|
||||
@@ -189,26 +221,7 @@ function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, Modal
|
||||
|
||||
// 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);
|
||||
handleSave(wsfunction, params);
|
||||
});
|
||||
|
||||
// Handle hidden event.
|
||||
@@ -217,9 +230,39 @@ function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, Modal
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
return modal;
|
||||
}).done(function(modal) {
|
||||
modal.show();
|
||||
|
||||
return;
|
||||
|
||||
}).catch(Notification.exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a web service function and reloads the page on success and shows a notification.
|
||||
* Displays an error notification, otherwise.
|
||||
*
|
||||
* @param {String} wsfunction The web service function to call.
|
||||
* @param {Object} params The parameters for the web service functoon.
|
||||
*/
|
||||
function handleSave(wsfunction, params) {
|
||||
// Confirm the request.
|
||||
var request = {
|
||||
methodname: wsfunction,
|
||||
args: params
|
||||
};
|
||||
|
||||
Ajax.call([request])[0].done(function(data) {
|
||||
if (data.result) {
|
||||
// On success, reload the page so that the data request table will be updated.
|
||||
// TODO: Probably in the future, better to reload the table or the target data request via AJAX.
|
||||
window.location.reload();
|
||||
} else {
|
||||
// Add the notification.
|
||||
Notification.addNotification({
|
||||
message: data.warnings[0].message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}).fail(Notification.exception);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
namespace tool_dataprivacy;
|
||||
|
||||
use coding_exception;
|
||||
use context_course;
|
||||
use context_system;
|
||||
use core\invalid_persistent_exception;
|
||||
use core\message\message;
|
||||
@@ -426,7 +427,22 @@ class api {
|
||||
if ($dpoid) {
|
||||
$datarequest->set('dpo', $dpoid);
|
||||
}
|
||||
$datarequest->set('dpocomment', $comment);
|
||||
// Update the comment if necessary.
|
||||
if (!empty(trim($comment))) {
|
||||
$params = [
|
||||
'date' => userdate(time()),
|
||||
'comment' => $comment
|
||||
];
|
||||
$commenttosave = get_string('datecomment', 'tool_dataprivacy', $params);
|
||||
// Check if there's an existing DPO comment.
|
||||
$currentcomment = trim($datarequest->get('dpocomment'));
|
||||
if ($currentcomment) {
|
||||
// Append the new comment to the current comment and give them 1 line space in between.
|
||||
$commenttosave = $currentcomment . PHP_EOL . PHP_EOL . $commenttosave;
|
||||
}
|
||||
$datarequest->set('dpocomment', $commenttosave);
|
||||
}
|
||||
|
||||
return $datarequest->update();
|
||||
}
|
||||
|
||||
@@ -521,7 +537,6 @@ class api {
|
||||
* @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) {
|
||||
|
||||
@@ -31,6 +31,7 @@ use context_helper;
|
||||
use context_system;
|
||||
use context_user;
|
||||
use core\invalid_persistent_exception;
|
||||
use core\notification;
|
||||
use core_user;
|
||||
use dml_exception;
|
||||
use external_api;
|
||||
@@ -144,7 +145,7 @@ class external extends external_api {
|
||||
}
|
||||
|
||||
/**
|
||||
* Deny a data request.
|
||||
* Make a general enquiry to a DPO.
|
||||
*
|
||||
* @since Moodle 3.5
|
||||
* @param string $message The message to be sent to the DPO.
|
||||
@@ -210,7 +211,7 @@ class external extends external_api {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameter description for deny_data_request().
|
||||
* Parameter description for contact_dpo().
|
||||
*
|
||||
* @since Moodle 3.5
|
||||
* @return external_description
|
||||
@@ -222,6 +223,70 @@ class external extends external_api {
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameter description for mark_complete().
|
||||
*
|
||||
* @since Moodle 3.5.2
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function mark_complete_parameters() {
|
||||
return new external_function_parameters([
|
||||
'requestid' => new external_value(PARAM_INT, 'The request ID', VALUE_REQUIRED)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a user's general enquiry's status as complete.
|
||||
*
|
||||
* @since Moodle 3.5.2
|
||||
* @param int $requestid The request ID of the general enquiry.
|
||||
* @return array
|
||||
* @throws coding_exception
|
||||
* @throws invalid_parameter_exception
|
||||
* @throws invalid_persistent_exception
|
||||
* @throws restricted_context_exception
|
||||
* @throws dml_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function mark_complete($requestid) {
|
||||
global $USER;
|
||||
|
||||
$warnings = [];
|
||||
$params = external_api::validate_parameters(self::mark_complete_parameters(), [
|
||||
'requestid' => $requestid,
|
||||
]);
|
||||
$requestid = $params['requestid'];
|
||||
|
||||
// Validate context.
|
||||
$context = context_system::instance();
|
||||
self::validate_context($context);
|
||||
|
||||
$message = get_string('markedcomplete', 'tool_dataprivacy');
|
||||
// Update the data request record.
|
||||
if ($result = api::update_request_status($requestid, api::DATAREQUEST_STATUS_COMPLETE, $USER->id, $message)) {
|
||||
// Add notification in the session to be shown when the page is reloaded on the JS side.
|
||||
notification::success(get_string('requestmarkedcomplete', 'tool_dataprivacy'));
|
||||
}
|
||||
|
||||
return [
|
||||
'result' => $result,
|
||||
'warnings' => $warnings
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameter description for mark_complete().
|
||||
*
|
||||
* @since Moodle 3.5.2
|
||||
* @return external_description
|
||||
*/
|
||||
public static function mark_complete_returns() {
|
||||
return new external_single_structure([
|
||||
'result' => new external_value(PARAM_BOOL, 'The processing result'),
|
||||
'warnings' => new external_warnings()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameter description for get_data_request().
|
||||
*
|
||||
@@ -258,9 +323,9 @@ class external extends external_api {
|
||||
// Validate context.
|
||||
$context = context_system::instance();
|
||||
self::validate_context($context);
|
||||
$requestpersistent = new data_request($requestid);
|
||||
require_capability('tool/dataprivacy:managedatarequests', $context);
|
||||
|
||||
$requestpersistent = new data_request($requestid);
|
||||
$exporter = new data_request_exporter($requestpersistent, ['context' => $context]);
|
||||
$renderer = $PAGE->get_renderer('tool_dataprivacy');
|
||||
$result = $exporter->export($renderer);
|
||||
@@ -326,6 +391,9 @@ class external extends external_api {
|
||||
$result = false;
|
||||
if ($requestexists) {
|
||||
$result = api::approve_data_request($requestid);
|
||||
|
||||
// Add notification in the session to be shown when the page is reloaded on the JS side.
|
||||
notification::success(get_string('requestapproved', 'tool_dataprivacy'));
|
||||
} else {
|
||||
$warnings[] = [
|
||||
'item' => $requestid,
|
||||
@@ -395,6 +463,9 @@ class external extends external_api {
|
||||
$result = false;
|
||||
if ($requestexists) {
|
||||
$result = api::deny_data_request($requestid);
|
||||
|
||||
// Add notification in the session to be shown when the page is reloaded on the JS side.
|
||||
notification::success(get_string('requestdenied', 'tool_dataprivacy'));
|
||||
} else {
|
||||
$warnings[] = [
|
||||
'item' => $requestid,
|
||||
|
||||
@@ -102,6 +102,16 @@ class data_request_exporter extends persistent_exporter {
|
||||
'optional' => true,
|
||||
'default' => false
|
||||
],
|
||||
'approvedeny' => [
|
||||
'type' => PARAM_BOOL,
|
||||
'optional' => true,
|
||||
'default' => false
|
||||
],
|
||||
'canmarkcomplete' => [
|
||||
'type' => PARAM_BOOL,
|
||||
'optional' => true,
|
||||
'default' => false
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -140,14 +150,19 @@ class data_request_exporter extends persistent_exporter {
|
||||
|
||||
$values['messagehtml'] = text_to_html($this->persistent->get('comments'));
|
||||
|
||||
$values['typename'] = helper::get_request_type_string($this->persistent->get('type'));
|
||||
$values['typenameshort'] = helper::get_shortened_request_type_string($this->persistent->get('type'));
|
||||
$requesttype = $this->persistent->get('type');
|
||||
$values['typename'] = helper::get_request_type_string($requesttype);
|
||||
$values['typenameshort'] = helper::get_shortened_request_type_string($requesttype);
|
||||
|
||||
$values['canreview'] = false;
|
||||
$values['approvedeny'] = false;
|
||||
$values['statuslabel'] = helper::get_request_status_string($this->persistent->get('status'));
|
||||
|
||||
switch ($this->persistent->get('status')) {
|
||||
case api::DATAREQUEST_STATUS_PENDING:
|
||||
$values['statuslabelclass'] = 'label-default';
|
||||
// Request can be manually completed for general enquiry requests.
|
||||
$values['canmarkcomplete'] = $requesttype == api::DATAREQUEST_TYPE_OTHERS;
|
||||
break;
|
||||
case api::DATAREQUEST_STATUS_PREPROCESSING:
|
||||
$values['statuslabelclass'] = 'label-default';
|
||||
@@ -156,6 +171,8 @@ class data_request_exporter extends persistent_exporter {
|
||||
$values['statuslabelclass'] = 'label-info';
|
||||
// DPO can review the request once it's ready.
|
||||
$values['canreview'] = true;
|
||||
// Whether the DPO can approve or deny the request.
|
||||
$values['approvedeny'] = in_array($requesttype, [api::DATAREQUEST_TYPE_EXPORT, api::DATAREQUEST_TYPE_DELETE]);
|
||||
break;
|
||||
case api::DATAREQUEST_STATUS_APPROVED:
|
||||
$values['statuslabelclass'] = 'label-info';
|
||||
|
||||
@@ -180,16 +180,32 @@ class data_requests_table extends table_sql {
|
||||
$actiontext = get_string('viewrequest', 'tool_dataprivacy');
|
||||
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
|
||||
|
||||
if ($status == api::DATAREQUEST_STATUS_AWAITING_APPROVAL) {
|
||||
// Approve.
|
||||
$actiondata['data-action'] = 'approve';
|
||||
$actiontext = get_string('approverequest', 'tool_dataprivacy');
|
||||
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
|
||||
switch ($status) {
|
||||
case api::DATAREQUEST_STATUS_PENDING:
|
||||
// Add action to mark a general enquiry request as complete.
|
||||
if ($data->type == api::DATAREQUEST_TYPE_OTHERS) {
|
||||
$actiondata['data-action'] = 'complete';
|
||||
$nameemail = (object)[
|
||||
'name' => $data->foruser->fullname,
|
||||
'email' => $data->foruser->email
|
||||
];
|
||||
$actiondata['data-requestid'] = $data->id;
|
||||
$actiondata['data-replytoemail'] = get_string('nameemail', 'tool_dataprivacy', $nameemail);
|
||||
$actiontext = get_string('markcomplete', 'tool_dataprivacy');
|
||||
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
|
||||
}
|
||||
break;
|
||||
case api::DATAREQUEST_STATUS_AWAITING_APPROVAL:
|
||||
// Approve.
|
||||
$actiondata['data-action'] = 'approve';
|
||||
$actiontext = get_string('approverequest', 'tool_dataprivacy');
|
||||
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
|
||||
|
||||
// Deny.
|
||||
$actiondata['data-action'] = 'deny';
|
||||
$actiontext = get_string('denyrequest', 'tool_dataprivacy');
|
||||
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
|
||||
// Deny.
|
||||
$actiondata['data-action'] = 'deny';
|
||||
$actiontext = get_string('denyrequest', 'tool_dataprivacy');
|
||||
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
|
||||
break;
|
||||
}
|
||||
|
||||
$actionsmenu = new action_menu($actions);
|
||||
|
||||
@@ -82,6 +82,7 @@ class my_data_requests_page implements renderable, templatable {
|
||||
$requestid = $request->get('id');
|
||||
$status = $request->get('status');
|
||||
$userid = $request->get('userid');
|
||||
$type = $request->get('type');
|
||||
|
||||
$usercontext = context_user::instance($userid, IGNORE_MISSING);
|
||||
if (!$usercontext) {
|
||||
@@ -107,7 +108,8 @@ class my_data_requests_page implements renderable, templatable {
|
||||
$item->statuslabelclass = 'label-success';
|
||||
$item->statuslabel = get_string('statuscomplete', 'tool_dataprivacy');
|
||||
$cancancel = false;
|
||||
$candownload = true;
|
||||
// Show download links only for export-type data requests.
|
||||
$candownload = $type == api::DATAREQUEST_TYPE_EXPORT;
|
||||
break;
|
||||
case api::DATAREQUEST_STATUS_CANCELLED:
|
||||
case api::DATAREQUEST_STATUS_REJECTED:
|
||||
|
||||
@@ -43,6 +43,16 @@ $functions = [
|
||||
'ajax' => true,
|
||||
'loginrequired' => true,
|
||||
],
|
||||
'tool_dataprivacy_mark_complete' => [
|
||||
'classname' => 'tool_dataprivacy\external',
|
||||
'methodname' => 'mark_complete',
|
||||
'classpath' => '',
|
||||
'description' => 'Mark a user\'s general enquiry as complete',
|
||||
'type' => 'write',
|
||||
'capabilities' => 'tool/dataprivacy:managedatarequests',
|
||||
'ajax' => true,
|
||||
'loginrequired' => true,
|
||||
],
|
||||
'tool_dataprivacy_get_data_request' => [
|
||||
'classname' => 'tool_dataprivacy\external',
|
||||
'methodname' => 'get_data_request',
|
||||
|
||||
@@ -46,6 +46,7 @@ $string['categoryupdated'] = 'Category updated';
|
||||
$string['close'] = 'Close';
|
||||
$string['compliant'] = 'Compliant';
|
||||
$string['confirmapproval'] = 'Do you really want to approve this data request?';
|
||||
$string['confirmcompletion'] = 'Do you really want to mark this user enquiry as complete?';
|
||||
$string['confirmcontextdeletion'] = 'Do you really want to confirm the deletion of the selected contexts? This will also delete all of the user data for their respective sub-contexts.';
|
||||
$string['confirmdenial'] = 'Do you really want deny this data request?';
|
||||
$string['contactdataprotectionofficer'] = 'Contact the privacy officer';
|
||||
@@ -70,6 +71,7 @@ $string['dataregistryinfo'] = 'The data registry enables categories (types of da
|
||||
$string['datarequestcreatedforuser'] = 'Data request created for {$a}';
|
||||
$string['datarequestemailsubject'] = 'Data request: {$a}';
|
||||
$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.';
|
||||
@@ -151,6 +153,8 @@ $string['httpwarning'] = 'Any data downloaded from this site may not be encrypte
|
||||
$string['inherit'] = 'Inherit';
|
||||
$string['lawfulbases'] = 'Lawful bases';
|
||||
$string['lawfulbases_help'] = 'Select at least one option that will serve as the lawful basis for processing personal data. For details on these lawful bases, please see <a href="https://gdpr-info.eu/art-6-gdpr/" target="_blank">GDPR Art. 6.1</a>';
|
||||
$string['markcomplete'] = 'Mark as complete';
|
||||
$string['markedcomplete'] = 'Your enquiry has been marked as complete by the privacy officer.';
|
||||
$string['messageprovider:contactdataprotectionofficer'] = 'Data requests';
|
||||
$string['messageprovider:datarequestprocessingresults'] = 'Data request processing results';
|
||||
$string['messageprovider:notifyexceptions'] = 'Data requests exceptions notifications';
|
||||
@@ -197,12 +201,15 @@ $string['purposeslist'] = 'List of data purposes';
|
||||
$string['purposeupdated'] = 'Purpose updated';
|
||||
$string['replyto'] = 'Reply to';
|
||||
$string['requestactions'] = 'Actions';
|
||||
$string['requestapproved'] = 'The request has been approved';
|
||||
$string['requestby'] = 'Requested by';
|
||||
$string['requestbydetail'] = 'Requested by:';
|
||||
$string['requestcomments'] = 'Comments';
|
||||
$string['requestcomments_help'] = 'This box enables you to enter any further details about your data request.';
|
||||
$string['requestdenied'] = 'The request has been denied';
|
||||
$string['requestemailintro'] = 'You have received a data request:';
|
||||
$string['requestfor'] = 'Requesting for';
|
||||
$string['requestmarkedcomplete'] = 'The request has been marked as complete';
|
||||
$string['requeststatus'] = 'Status';
|
||||
$string['requestsubmitted'] = 'Your request has been submitted to the privacy officer';
|
||||
$string['requesttype'] = 'Type';
|
||||
|
||||
@@ -35,7 +35,12 @@
|
||||
}}
|
||||
{{< core/modal }}
|
||||
{{$footer}}
|
||||
<button type="button" class="btn btn-primary" data-action="approve">{{#str}} approve, tool_dataprivacy {{/str}}</button>
|
||||
<button type="button" class="btn btn-secondary" data-action="deny">{{#str}} deny, tool_dataprivacy {{/str}}</button>
|
||||
{{#approvedeny}}
|
||||
<button type="button" class="btn btn-primary" data-action="approve">{{#str}} approve, tool_dataprivacy {{/str}}</button>
|
||||
<button type="button" class="btn btn-secondary" data-action="deny">{{#str}} deny, tool_dataprivacy {{/str}}</button>
|
||||
{{/approvedeny}}
|
||||
{{#canmarkcomplete}}
|
||||
<button type="button" class="btn btn-primary" data-action="complete">{{#str}} markcomplete, tool_dataprivacy {{/str}}</button>
|
||||
{{/canmarkcomplete}}
|
||||
{{/footer}}
|
||||
{{/ core/modal }}
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$plugin->version = 2018051401;
|
||||
$plugin->version = 2018051402;
|
||||
$plugin->requires = 2018050800; // Moodle 3.5dev (Build 2018031600) and upwards.
|
||||
$plugin->component = 'tool_dataprivacy';
|
||||
|
||||
Reference in New Issue
Block a user