Compare commits
273 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b78584fdb | |||
| ce390c4725 | |||
| 8675c2c908 | |||
| 70ffd8bb2b | |||
| c2fea8ae43 | |||
| 0a520053cb | |||
| e154204728 | |||
| 2d90a7d91a | |||
| 8ac7c623d3 | |||
| caa9881c0d | |||
| f0188d7b66 | |||
| ab94fee038 | |||
| 2d64d63373 | |||
| 6cb165d681 | |||
| 55fc303a5f | |||
| c453ea21e1 | |||
| 24e4ec9d60 | |||
| 25e9be8ea9 | |||
| 9ffcaa7db7 | |||
| 936d21d2dd | |||
| d20b4b2f23 | |||
| d587c4bd90 | |||
| 3828033e98 | |||
| 4892662238 | |||
| ccbd142a63 | |||
| 3b52f52351 | |||
| 173968b25d | |||
| 4cdd9e77dc | |||
| 483d8fefa8 | |||
| 654eac4f13 | |||
| 86699bedb0 | |||
| 914e26d916 | |||
| ced6691934 | |||
| ff1fbf2992 | |||
| 49e435c00a | |||
| b67ae5a0ad | |||
| b2b1bb2d29 | |||
| 5a13884a0f | |||
| d42328e223 | |||
| 3879b520b7 | |||
| 6a3cffd3c6 | |||
| 40790755ce | |||
| 310a1f0895 | |||
| ed3468e40b | |||
| 9bedb84419 | |||
| c843630aee | |||
| 233847bdb7 | |||
| f29d518ba8 | |||
| 749c7cc5a9 | |||
| bb3ce79c7e | |||
| 4b52a5b2c6 | |||
| af344834e1 | |||
| cdc9e56198 | |||
| 0e0fd72d98 | |||
| 543005a342 | |||
| 5c33665a74 | |||
| 362518ef34 | |||
| ff246f2260 | |||
| fc1b13ede9 | |||
| 458b0fb900 | |||
| 5df53c769e | |||
| 380a437428 | |||
| 5f6539e379 | |||
| b44ddd4a7f | |||
| 833e2398ef | |||
| df9443db71 | |||
| 4d23edfb37 | |||
| 11489b20a5 | |||
| b3483e7ede | |||
| dd05e0d29e | |||
| 1b37fe78f9 | |||
| 82f21031a3 | |||
| 6545e50f62 | |||
| 3d7630a158 | |||
| 2588126d66 | |||
| de71bef7cb | |||
| e310f10818 | |||
| 9b6938f99b | |||
| 9c42327dd1 | |||
| 28631a1455 | |||
| aa7deee9cc | |||
| 009a9ca500 | |||
| 7de4759799 | |||
| 33433bb8a1 | |||
| 9efe4b84ce | |||
| 8793e120b9 | |||
| 43f742e1e5 | |||
| bea7b33840 | |||
| 4900f375d7 | |||
| a94b1fed30 | |||
| 9337d85eb3 | |||
| 2a8f28520d | |||
| e972b9e909 | |||
| 74c35a9133 | |||
| b4b5124c73 | |||
| af963ad2ae | |||
| 2341e8695e | |||
| f55516fd46 | |||
| 36e337b176 | |||
| 23e9fea9a5 | |||
| 1408aa8bc8 | |||
| 2f86cad04e | |||
| fd77e38fc9 | |||
| 0191113d24 | |||
| 8bc0bbf1ee | |||
| 1dc4c34d07 | |||
| 5339d26259 | |||
| bc7a379947 | |||
| e9553d973a | |||
| 4f7fa18201 | |||
| 697a28aadf | |||
| 26f5750319 | |||
| d4fea9b20f | |||
| af280e5929 | |||
| fa8d508f36 | |||
| e7bf43ba0d | |||
| 793da897d1 | |||
| e1aa99e99d | |||
| 06b8272791 | |||
| 15703fb9f1 | |||
| 20d8c489d8 | |||
| 1d97a7818e | |||
| 7c64406d4d | |||
| f71b39b960 | |||
| 44dd58756b | |||
| 1dbbcaf9ba | |||
| d0e1b2921d | |||
| 72107631d7 | |||
| 888cfdc75f | |||
| 9886790075 | |||
| f3323c22fe | |||
| d8e011f721 | |||
| 3b63cb559e | |||
| f5189e3aa3 | |||
| 2c643bb988 | |||
| c5fd53af5d | |||
| e275b0c4a6 | |||
| 9482a77a04 | |||
| fab105578b | |||
| b4dcfe9a06 | |||
| a31a20a5c6 | |||
| e6e7a85087 | |||
| 612c718696 | |||
| 180988c999 | |||
| 59380a9a64 | |||
| d2593fadb6 | |||
| 904aec69a0 | |||
| d851bb5c2a | |||
| 6fa61e69a7 | |||
| c0e65fbbf8 | |||
| cc0b367755 | |||
| 1dc3e88cca | |||
| d04e88b78f | |||
| d77209e8c5 | |||
| 3f27598353 | |||
| 334ced71a1 | |||
| f3a817e959 | |||
| 9712975595 | |||
| 4656db12e1 | |||
| a1ec9e389e | |||
| 3714b3d104 | |||
| 251358b364 | |||
| d2b6ce1555 | |||
| 3c22418e0a | |||
| 94269e4505 | |||
| 0f272f06ea | |||
| 08ca997c76 | |||
| 7d52baba45 | |||
| 72f73d595d | |||
| bf67536031 | |||
| 7a78ae6c10 | |||
| 2cdbfd1dca | |||
| d8924ce166 | |||
| 9ccbf4386c | |||
| fe9d8a7274 | |||
| 9ec5fa2208 | |||
| bffc50ae9d | |||
| a634c88ced | |||
| 2eb7c6d6cb | |||
| 8ae9044ef4 | |||
| eef776a0b4 | |||
| ef57748905 | |||
| 9903e25871 | |||
| 557a9b5674 | |||
| 0ff993ffe3 | |||
| a8a9876c1d | |||
| fd308528cd | |||
| b0f0c2af7e | |||
| ba5dde0347 | |||
| 22cab09558 | |||
| b02f319b78 | |||
| e03ef397fc | |||
| cddd2b461b | |||
| ec41f1ce1b | |||
| 7ce29a90af | |||
| 4dda9579b3 | |||
| 9b77f93fa0 | |||
| 4fc3adb35b | |||
| d3e3f0f109 | |||
| 8bc2ee1a4a | |||
| 93e51e74b4 | |||
| 148ef43015 | |||
| 8b5dfd0b06 | |||
| 1ebd063f4e | |||
| 40579dc143 | |||
| f26629e665 | |||
| ab96a452c6 | |||
| 50e1bbb583 | |||
| 09b6fbdaca | |||
| 8f9c8a5826 | |||
| 9c607c0b8c | |||
| 6dc178e448 | |||
| 187d2a26c0 | |||
| c6755d6f34 | |||
| bea2c40f53 | |||
| 363622d03e | |||
| 649c96c259 | |||
| ac32f752d9 | |||
| db79038541 | |||
| c390ae4f8d | |||
| e0a8cfdb6d | |||
| 52f03c6fc6 | |||
| 9f2c4a9277 | |||
| 575e209b93 | |||
| 0b363e6c4b | |||
| 5d8821ae05 | |||
| 512a916207 | |||
| 86a7cf0909 | |||
| f93f81a9f6 | |||
| bf6f0f03cf | |||
| 903182e4a3 | |||
| e7554e4b67 | |||
| 167420bd2e | |||
| f479887c77 | |||
| 5441c8fef4 | |||
| b104cede75 | |||
| c3c8223ca2 | |||
| d4773c32d6 | |||
| c8511e2808 | |||
| b23e6519fe | |||
| dc7c48a9d7 | |||
| a210233bfb | |||
| 9dcf28af7d | |||
| c030a94b68 | |||
| bd8b3a91e0 | |||
| 582eb6e6e2 | |||
| 9b127bcb70 | |||
| 171a764049 | |||
| 0da8ef69a8 | |||
| 815be4e003 | |||
| d897846115 | |||
| 5427855d50 | |||
| 6d7093e370 | |||
| d7c805a9f0 | |||
| 2032603ae9 | |||
| e7e91b8bae | |||
| 30cbfbdd61 | |||
| ab7694d82e | |||
| 61ee9a304e | |||
| f59bc1b152 | |||
| 3aa57f20af | |||
| b875ad18b5 | |||
| 2d69b0fbe7 | |||
| c8663da1a9 | |||
| 1de2735b16 | |||
| 4a5adabe95 | |||
| b566d8002a | |||
| 2e404995a0 | |||
| 654ad71526 | |||
| 2b50975563 | |||
| 81fb99ed66 | |||
| deb358f16c | |||
| 881098f39c |
@@ -2,6 +2,7 @@
|
||||
theme/bootstrapbase/style/
|
||||
theme/clean/style/custom.css
|
||||
theme/more/style/custom.css
|
||||
theme/boost/style/moodle.css
|
||||
node_modules/
|
||||
vendor/
|
||||
admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ matrix:
|
||||
include:
|
||||
# Run grunt/npm install on highest version ('node' is an alias for the latest node.js version.)
|
||||
- php: 7.2
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='8.9'
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='lts/carbon'
|
||||
|
||||
exclude:
|
||||
# MySQL - it's just too slow.
|
||||
|
||||
+14
-2
@@ -137,6 +137,16 @@ module.exports = function(grunt) {
|
||||
}
|
||||
}
|
||||
},
|
||||
sass: {
|
||||
dist: {
|
||||
files: {
|
||||
"theme/boost/style/moodle.css": "theme/boost/scss/preset/default.scss"
|
||||
}
|
||||
},
|
||||
options: {
|
||||
includePaths: ["theme/boost/scss/"]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
options: {
|
||||
nospawn: true // We need not to spawn so config can be changed dynamically.
|
||||
@@ -214,7 +224,8 @@ module.exports = function(grunt) {
|
||||
'# Generated by "grunt ignorefiles"',
|
||||
'theme/bootstrapbase/style/',
|
||||
'theme/clean/style/custom.css',
|
||||
'theme/more/style/custom.css'
|
||||
'theme/more/style/custom.css',
|
||||
'theme/boost/style/moodle.css'
|
||||
].concat(thirdPartyPaths);
|
||||
grunt.file.write('.stylelintignore', stylelintIgnores.join('\n'));
|
||||
};
|
||||
@@ -362,6 +373,7 @@ module.exports = function(grunt) {
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-less');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-sass');
|
||||
grunt.loadNpmTasks('grunt-eslint');
|
||||
grunt.loadNpmTasks('grunt-stylelint');
|
||||
|
||||
@@ -374,7 +386,7 @@ module.exports = function(grunt) {
|
||||
grunt.registerTask('js', ['amd', 'yui']);
|
||||
|
||||
// Register CSS taks.
|
||||
grunt.registerTask('css', ['stylelint:scss', 'stylelint:less', 'less:bootstrapbase', 'stylelint:css']);
|
||||
grunt.registerTask('css', ['stylelint:scss', 'sass', 'stylelint:less', 'less:bootstrapbase', 'stylelint:css']);
|
||||
|
||||
// Register the startup task.
|
||||
grunt.registerTask('startup', 'Run the correct tasks for the current directory', tasks.startup);
|
||||
|
||||
+1
-1
@@ -1990,7 +1990,7 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
if (empty($CFG->docroot) or $environment_result->plugin) {
|
||||
$report = get_string($stringtouse, 'admin', $rec);
|
||||
} else {
|
||||
$report = $this->doc_link(join($linkparts, '/'), get_string($stringtouse, 'admin', $rec));
|
||||
$report = $this->doc_link(join($linkparts, '/'), get_string($stringtouse, 'admin', $rec), true);
|
||||
}
|
||||
// Enclose report text in div so feedback text will be displayed underneath it.
|
||||
$report = html_writer::div($report);
|
||||
|
||||
+111
-84
@@ -28,113 +28,139 @@ admin_externalpage_setup('searchareas');
|
||||
|
||||
$areaid = optional_param('areaid', null, PARAM_ALPHAEXT);
|
||||
$action = optional_param('action', null, PARAM_ALPHA);
|
||||
$indexingenabled = \core_search\manager::is_indexing_enabled(); // This restricts many of the actions on this page.
|
||||
|
||||
// Get a search manager instance, which we'll need for display and to handle some actions.
|
||||
try {
|
||||
$searchmanager = \core_search\manager::instance();
|
||||
} catch (core_search\engine_exception $searchmanagererror) {
|
||||
// Continue, we return an error later depending on the requested action.
|
||||
// In action cases, we'll throw this exception below. In non-action cases, we produce a lang string error.
|
||||
}
|
||||
|
||||
// Handle all the actions.
|
||||
if ($action) {
|
||||
|
||||
// If dealing with an areaid, we need to check that the area exists.
|
||||
if ($areaid) {
|
||||
// We need to check that the area exists.
|
||||
$area = \core_search\manager::get_search_area($areaid);
|
||||
if ($area === false) {
|
||||
throw new moodle_exception('invalidrequest');
|
||||
}
|
||||
}
|
||||
|
||||
if ($action !== 'enable' && $action !== 'disable') {
|
||||
// All actions but enable/disable need the search engine to be ready.
|
||||
if (!empty($searchmanagererror)) {
|
||||
throw $searchmanagererror;
|
||||
}
|
||||
// All the indexing actions.
|
||||
if (in_array($action, ['delete', 'indexall', 'reindexall', 'deleteall'])) {
|
||||
|
||||
// Show confirm prompt for all these actions as they may be inadvisable, or may cause
|
||||
// an interruption in search functionality, on production systems.
|
||||
if (!optional_param('confirm', 0, PARAM_INT)) {
|
||||
// Display confirmation prompt.
|
||||
$a = null;
|
||||
if ($areaid) {
|
||||
$a = html_writer::tag('strong', $area->get_visible_name());
|
||||
// All of these actions require that indexing is enabled.
|
||||
if ($indexingenabled) {
|
||||
|
||||
// For all of these actions, we strictly need a manager instance.
|
||||
if (isset($searchmanagererror)) {
|
||||
throw $searchmanagererror;
|
||||
}
|
||||
|
||||
$actionparams = ['sesskey' => sesskey(), 'action' => $action, 'confirm' => 1];
|
||||
if ($areaid) {
|
||||
$actionparams['areaid'] = $areaid;
|
||||
}
|
||||
$actionurl = new moodle_url('/admin/searchareas.php', $actionparams);
|
||||
$cancelurl = new moodle_url('/admin/searchareas.php');
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->confirm(get_string('confirm_' . $action, 'search', $a),
|
||||
// Show confirm prompt for all these actions as they may be inadvisable, or may cause
|
||||
// an interruption in search functionality, on production systems.
|
||||
if (!optional_param('confirm', 0, PARAM_INT)) {
|
||||
// Display confirmation prompt.
|
||||
$a = null;
|
||||
if ($areaid) {
|
||||
$a = html_writer::tag('strong', $area->get_visible_name());
|
||||
}
|
||||
|
||||
$actionparams = ['sesskey' => sesskey(), 'action' => $action, 'confirm' => 1];
|
||||
if ($areaid) {
|
||||
$actionparams['areaid'] = $areaid;
|
||||
}
|
||||
$actionurl = new moodle_url('/admin/searchareas.php', $actionparams);
|
||||
$cancelurl = new moodle_url('/admin/searchareas.php');
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->confirm(get_string('confirm_' . $action, 'search', $a),
|
||||
new single_button($actionurl, get_string('continue'), 'post', true),
|
||||
new single_button($cancelurl, get_string('cancel'), 'get'));
|
||||
echo $OUTPUT->footer();
|
||||
exit;
|
||||
echo $OUTPUT->footer();
|
||||
exit;
|
||||
} else {
|
||||
// Confirmed, so run the required action.
|
||||
require_sesskey();
|
||||
|
||||
switch ($action) {
|
||||
case 'delete':
|
||||
$searchmanager->delete_index($areaid);
|
||||
\core\notification::success(get_string('searchindexdeleted', 'admin'));
|
||||
break;
|
||||
case 'indexall':
|
||||
$searchmanager->index();
|
||||
\core\notification::success(get_string('searchindexupdated', 'admin'));
|
||||
break;
|
||||
case 'reindexall':
|
||||
$searchmanager->index(true);
|
||||
\core\notification::success(get_string('searchreindexed', 'admin'));
|
||||
break;
|
||||
case 'deleteall':
|
||||
$searchmanager->delete_index();
|
||||
\core\notification::success(get_string('searchalldeleted', 'admin'));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Redirect back to the main page after taking action.
|
||||
redirect(new moodle_url('/admin/searchareas.php'));
|
||||
}
|
||||
}
|
||||
} else if (in_array($action, ['enable', 'disable'])) {
|
||||
// Toggling search areas requires no confirmation.
|
||||
require_sesskey();
|
||||
|
||||
switch ($action) {
|
||||
case 'enable':
|
||||
$area->set_enabled(true);
|
||||
\core\notification::success(get_string('searchareaenabled', 'admin'));
|
||||
break;
|
||||
case 'disable':
|
||||
$area->set_enabled(false);
|
||||
core\notification::success(get_string('searchareadisabled', 'admin'));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
redirect(new moodle_url('/admin/searchareas.php'));
|
||||
} else {
|
||||
// Invalid action.
|
||||
throw new moodle_exception('invalidaction');
|
||||
}
|
||||
}
|
||||
|
||||
// We are now taking an actual action, so require sesskey.
|
||||
require_sesskey();
|
||||
|
||||
switch ($action) {
|
||||
case 'enable':
|
||||
$area->set_enabled(true);
|
||||
\core\notification::add(get_string('searchareaenabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
break;
|
||||
case 'disable':
|
||||
$area->set_enabled(false);
|
||||
\core\notification::add(get_string('searchareadisabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
break;
|
||||
case 'delete':
|
||||
$search = \core_search\manager::instance();
|
||||
$search->delete_index($areaid);
|
||||
\core\notification::add(get_string('searchindexdeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
break;
|
||||
case 'indexall':
|
||||
$searchmanager->index();
|
||||
\core\notification::add(get_string('searchindexupdated', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
break;
|
||||
case 'reindexall':
|
||||
$searchmanager->index(true);
|
||||
\core\notification::add(get_string('searchreindexed', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
break;
|
||||
case 'deleteall':
|
||||
$searchmanager->delete_index();
|
||||
\core\notification::add(get_string('searchalldeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
break;
|
||||
default:
|
||||
throw new moodle_exception('invalidaction');
|
||||
break;
|
||||
}
|
||||
|
||||
// Redirect back to the main page after taking action.
|
||||
redirect(new moodle_url('/admin/searchareas.php'));
|
||||
// Display.
|
||||
if (isset($searchmanager) && $indexingenabled) {
|
||||
\core\notification::info(get_string('indexinginfo', 'admin'));
|
||||
} else if (isset($searchmanager)) {
|
||||
$params = (object) [
|
||||
'url' => (new moodle_url("/admin/settings.php?section=manageglobalsearch#admin-searchindexwhendisabled"))->out(false)
|
||||
];
|
||||
\core\notification::error(get_string('indexwhendisabledfullnotice', 'search', $params));
|
||||
} else {
|
||||
// In non-action cases, init errors are translated and displayed to the user as error notifications.
|
||||
$errorstr = get_string($searchmanagererror->errorcode, $searchmanagererror->module, $searchmanagererror->a);
|
||||
\core\notification::error($errorstr);
|
||||
}
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
$searchareas = \core_search\manager::get_search_areas_list();
|
||||
if (empty($searchmanagererror)) {
|
||||
$areasconfig = $searchmanager->get_areas_config($searchareas);
|
||||
} else {
|
||||
$areasconfig = false;
|
||||
}
|
||||
|
||||
if (!empty($searchmanagererror)) {
|
||||
$errorstr = get_string($searchmanagererror->errorcode, $searchmanagererror->module, $searchmanagererror->a);
|
||||
echo $OUTPUT->notification($errorstr, \core\output\notification::NOTIFY_ERROR);
|
||||
} else {
|
||||
echo $OUTPUT->notification(get_string('indexinginfo', 'admin'), \core\output\notification::NOTIFY_INFO);
|
||||
}
|
||||
|
||||
$table = new html_table();
|
||||
$table->id = 'core-search-areas';
|
||||
$table->head = [
|
||||
get_string('searcharea', 'search'),
|
||||
get_string('enable'),
|
||||
get_string('newestdocindexed', 'admin'),
|
||||
get_string('searchlastrun', 'admin'),
|
||||
get_string('searchindexactions', 'admin')
|
||||
];
|
||||
|
||||
$table->head = array(get_string('searcharea', 'search'), get_string('enable'), get_string('newestdocindexed', 'admin'),
|
||||
get_string('searchlastrun', 'admin'), get_string('searchindexactions', 'admin'));
|
||||
|
||||
$searchareas = \core_search\manager::get_search_areas_list();
|
||||
$areasconfig = isset($searchmanager) ? $searchmanager->get_areas_config($searchareas) : false;
|
||||
foreach ($searchareas as $area) {
|
||||
$areaid = $area->get_area_id();
|
||||
$columns = array(new html_table_cell($area->get_visible_name()));
|
||||
@@ -144,7 +170,7 @@ foreach ($searchareas as $area) {
|
||||
new pix_icon('t/hide', get_string('disable'), 'moodle', array('title' => '', 'class' => 'iconsmall')),
|
||||
null, array('title' => get_string('disable')));
|
||||
|
||||
if ($areasconfig) {
|
||||
if ($areasconfig && $indexingenabled) {
|
||||
$columns[] = $areasconfig[$areaid]->lastindexrun;
|
||||
|
||||
if ($areasconfig[$areaid]->indexingstart) {
|
||||
@@ -173,7 +199,11 @@ foreach ($searchareas as $area) {
|
||||
$columns[] = html_writer::alist($actions, ['class' => 'unstyled list-unstyled']);
|
||||
|
||||
} else {
|
||||
$blankrow = new html_table_cell(get_string('searchnotavailable', 'admin'));
|
||||
if (!$areasconfig) {
|
||||
$blankrow = new html_table_cell(get_string('searchnotavailable', 'admin'));
|
||||
} else {
|
||||
$blankrow = new html_table_cell(get_string('indexwhendisabledshortnotice', 'search'));
|
||||
}
|
||||
$blankrow->colspan = 3;
|
||||
$columns[] = $blankrow;
|
||||
}
|
||||
@@ -192,10 +222,7 @@ foreach ($searchareas as $area) {
|
||||
}
|
||||
|
||||
// Cross-search area tasks.
|
||||
$options = array();
|
||||
if (!empty($searchmanagererror)) {
|
||||
$options['disabled'] = true;
|
||||
}
|
||||
$options = (isset($searchmanager) && $indexingenabled) ? [] : ['disabled' => true];
|
||||
echo $OUTPUT->box_start('search-areas-actions');
|
||||
echo $OUTPUT->single_button(admin_searcharea_action_url('indexall'), get_string('searchupdateindex', 'admin'), 'get', $options);
|
||||
echo $OUTPUT->single_button(admin_searcharea_action_url('reindexall'), get_string('searchreindexindex', 'admin'), 'get', $options);
|
||||
@@ -204,7 +231,7 @@ echo $OUTPUT->box_end();
|
||||
|
||||
echo html_writer::table($table);
|
||||
|
||||
if (empty($searchmanagererror)) {
|
||||
if (isset($searchmanager)) {
|
||||
// Show information about queued index requests for specific contexts.
|
||||
$searchrenderer = $PAGE->get_renderer('core_search');
|
||||
echo $searchrenderer->render_index_requests_info($searchmanager->get_index_requests_info());
|
||||
|
||||
+2
-1
@@ -38,7 +38,8 @@ if (!($settingspage->check_access())) {
|
||||
$statusmsg = '';
|
||||
$errormsg = '';
|
||||
|
||||
if ($data = data_submitted() and confirm_sesskey()) {
|
||||
// Form is submitted with changed settings. Do not want to execute when modifying a block.
|
||||
if ($data = data_submitted() and confirm_sesskey() and isset($data->action) and $data->action == 'save-settings') {
|
||||
|
||||
$count = admin_write_settings($data);
|
||||
// Regardless of whether any setting change was written (a positive count), check validation errors for those that didn't.
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
<div class="settingsform clearfix">
|
||||
{{#params}}
|
||||
<input type="hidden" name="{{name}}" value="{{value}}">
|
||||
<input type="hidden" name="action" value="save-settings">
|
||||
{{/params}}
|
||||
<input type="hidden" name="sesskey" value="{{sesskey}}">
|
||||
<input type="hidden" name="return" value="{{return}}">
|
||||
|
||||
@@ -38,5 +38,8 @@ function xmldb_tool_customlang_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -134,11 +134,13 @@ class tool_customlang_renderer extends plugin_renderer_base {
|
||||
$output .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'translatorsubmitted', 'value'=>1));
|
||||
$output .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'sesskey', 'value'=>sesskey()));
|
||||
$output .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'p', 'value'=>$translator->currentpage));
|
||||
$save1 = html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'savecontinue', 'value'=>get_string('savecontinue', 'tool_customlang')));
|
||||
$save2 = html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'savecheckin', 'value'=>get_string('savecheckin', 'tool_customlang')));
|
||||
$output .= html_writer::tag('fieldset', $save1.$save2, array('class'=>'buttonsbar'));
|
||||
$save1 = html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'savecontinue',
|
||||
'value' => get_string('savecontinue', 'tool_customlang'), 'class' => 'btn btn-secondary'));
|
||||
$save2 = html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'savecheckin',
|
||||
'value' => get_string('savecheckin', 'tool_customlang'), 'class' => 'btn btn-secondary'));
|
||||
$output .= html_writer::tag('fieldset', $save1 . ' ' . $save2, array('class' => 'buttonsbar'));
|
||||
$output .= html_writer::table($table);
|
||||
$output .= html_writer::tag('fieldset', $save1.$save2, array('class'=>'buttonsbar'));
|
||||
$output .= html_writer::tag('fieldset', $save1 . ' ' . $save2, array('class' => 'buttonsbar'));
|
||||
$output .= html_writer::end_tag('div');
|
||||
$output .= html_writer::end_tag('form');
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/form-autocomplete","core/str","core/notification"],function(a,b,c,d){var e={REQUEST_FILTERS:"#request-filters"},f=function(){var f=[{key:"filter",component:"moodle"},{key:"nofiltersapplied",component:"moodle"}];c.get_strings(f).then(function(a){var c=a[0],d=a[1];return b.enhance(e.REQUEST_FILTERS,!1,"",c,!1,!0,d,!0)}).fail(d.exception);var g=a(e.REQUEST_FILTERS).val();a(e.REQUEST_FILTERS).on("change",function(){var b=a(this).val();g.join(",")!==b.join(",")&&(0===b.length&&a("#filters-cleared").val(1),a(this.form).submit())})};return{init:function(){f()}}});
|
||||
@@ -234,6 +234,7 @@ define(['jquery', 'core/str', 'core/ajax', 'core/notification', 'core/templates'
|
||||
},
|
||||
fail: Notification.exception
|
||||
}]);
|
||||
return;
|
||||
}).catch(Notification.exception);
|
||||
|
||||
};
|
||||
|
||||
@@ -34,7 +34,6 @@ define(['jquery', 'core/url', 'core/str'], function($, url, str) {
|
||||
*
|
||||
* @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')) {
|
||||
@@ -58,7 +57,6 @@ define(['jquery', 'core/url', 'core/str'], function($, url, str) {
|
||||
* 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';
|
||||
@@ -75,6 +73,7 @@ define(['jquery', 'core/url', 'core/str'], function($, url, str) {
|
||||
|
||||
str.get_string(currentstate, 'tool_dataprivacy').then(function(langString) {
|
||||
$('.tool_dataprivacy-expand-all').html(langString);
|
||||
return;
|
||||
}).catch(Notification.exception);
|
||||
|
||||
$(':header i.fa').each(function() {
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* JS module for the data requests filter.
|
||||
*
|
||||
* @module tool_dataprivacy/request_filter
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/form-autocomplete', 'core/str', 'core/notification'], function($, Autocomplete, Str, Notification) {
|
||||
|
||||
/**
|
||||
* Selectors.
|
||||
*
|
||||
* @access private
|
||||
* @type {{REQUEST_FILTERS: string}}
|
||||
*/
|
||||
var SELECTORS = {
|
||||
REQUEST_FILTERS: '#request-filters'
|
||||
};
|
||||
|
||||
/**
|
||||
* Init function.
|
||||
*
|
||||
* @method init
|
||||
* @private
|
||||
*/
|
||||
var init = function() {
|
||||
var stringkeys = [
|
||||
{
|
||||
key: 'filter',
|
||||
component: 'moodle'
|
||||
},
|
||||
{
|
||||
key: 'nofiltersapplied',
|
||||
component: 'moodle'
|
||||
}
|
||||
];
|
||||
|
||||
Str.get_strings(stringkeys).then(function(langstrings) {
|
||||
var placeholder = langstrings[0];
|
||||
var noSelectionString = langstrings[1];
|
||||
return Autocomplete.enhance(SELECTORS.REQUEST_FILTERS, false, '', placeholder, false, true, noSelectionString, true);
|
||||
}).fail(Notification.exception);
|
||||
|
||||
var last = $(SELECTORS.REQUEST_FILTERS).val();
|
||||
$(SELECTORS.REQUEST_FILTERS).on('change', function() {
|
||||
var current = $(this).val();
|
||||
// Prevent form from submitting unnecessarily, eg. on blur when no filter is selected.
|
||||
if (last.join(',') !== current.join(',')) {
|
||||
// If we're submitting without filters, set the hidden input 'filters-cleared' to 1.
|
||||
if (current.length === 0) {
|
||||
$('#filters-cleared').val(1);
|
||||
}
|
||||
$(this.form).submit();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return /** @alias module:core/form-autocomplete */ {
|
||||
/**
|
||||
* Initialise the unified user filter.
|
||||
*
|
||||
* @method init
|
||||
*/
|
||||
init: function() {
|
||||
init();
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
require_once(__DIR__ . '/../../../config.php');
|
||||
|
||||
require_login(null, false);
|
||||
|
||||
$url = new moodle_url("/admin/tool/dataprivacy/categories.php");
|
||||
$title = get_string('editcategories', 'tool_dataprivacy');
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ class api {
|
||||
} else {
|
||||
// If not a DPO, only users with the capability to make data requests for the user should be allowed.
|
||||
// (e.g. users with the Parent role, etc).
|
||||
if (!api::can_create_data_request_for_user($foruser)) {
|
||||
if (!self::can_create_data_request_for_user($foruser)) {
|
||||
$forusercontext = \context_user::instance($foruser);
|
||||
throw new required_capability_exception($forusercontext,
|
||||
'tool/dataprivacy:makedatarequestsforchildren', 'nopermissions', '');
|
||||
@@ -232,16 +232,42 @@ class api {
|
||||
* (e.g. Users with the Data Protection Officer roles)
|
||||
*
|
||||
* @param int $userid The User ID.
|
||||
* @param int[] $statuses The status filters.
|
||||
* @param int[] $types The request type filters.
|
||||
* @param string $sort The order by clause.
|
||||
* @param int $offset Amount of records to skip.
|
||||
* @param int $limit Amount of records to fetch.
|
||||
* @return data_request[]
|
||||
* @throws coding_exception
|
||||
* @throws dml_exception
|
||||
*/
|
||||
public static function get_data_requests($userid = 0) {
|
||||
public static function get_data_requests($userid = 0, $statuses = [], $types = [], $sort = '', $offset = 0, $limit = 0) {
|
||||
global $DB, $USER;
|
||||
$results = [];
|
||||
$sort = 'status ASC, timemodified ASC';
|
||||
$sqlparams = [];
|
||||
$sqlconditions = [];
|
||||
|
||||
// Set default sort.
|
||||
if (empty($sort)) {
|
||||
$sort = 'status ASC, timemodified ASC';
|
||||
}
|
||||
|
||||
// Set status filters.
|
||||
if (!empty($statuses)) {
|
||||
list($statusinsql, $sqlparams) = $DB->get_in_or_equal($statuses, SQL_PARAMS_NAMED);
|
||||
$sqlconditions[] = "status $statusinsql";
|
||||
}
|
||||
|
||||
// Set request type filter.
|
||||
if (!empty($types)) {
|
||||
list($typeinsql, $typeparams) = $DB->get_in_or_equal($types, SQL_PARAMS_NAMED);
|
||||
$sqlconditions[] = "type $typeinsql";
|
||||
$sqlparams = array_merge($sqlparams, $typeparams);
|
||||
}
|
||||
|
||||
if ($userid) {
|
||||
// Get the data requests for the user or data requests made by the user.
|
||||
$select = "(userid = :userid OR requestedby = :requestedby)";
|
||||
$sqlconditions[] = "(userid = :userid OR requestedby = :requestedby)";
|
||||
$params = [
|
||||
'userid' => $userid,
|
||||
'requestedby' => $userid
|
||||
@@ -256,20 +282,87 @@ class api {
|
||||
$alloweduserids = array_merge($alloweduserids, array_keys($children));
|
||||
}
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($alloweduserids, SQL_PARAMS_NAMED);
|
||||
$select .= " AND userid $insql";
|
||||
$params = array_merge($params, $inparams);
|
||||
$sqlconditions[] .= "userid $insql";
|
||||
$select = implode(' AND ', $sqlconditions);
|
||||
$params = array_merge($params, $inparams, $sqlparams);
|
||||
|
||||
$results = data_request::get_records_select($select, $params, $sort);
|
||||
$results = data_request::get_records_select($select, $params, $sort, '*', $offset, $limit);
|
||||
} 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, $sort, '');
|
||||
if (!empty($sqlconditions)) {
|
||||
$select = implode(' AND ', $sqlconditions);
|
||||
$results = data_request::get_records_select($select, $sqlparams, $sort, '*', $offset, $limit);
|
||||
} else {
|
||||
$results = data_request::get_records(null, $sort, '', $offset, $limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the count of data request records based on the given parameters.
|
||||
*
|
||||
* @param int $userid The User ID.
|
||||
* @param int[] $statuses The status filters.
|
||||
* @param int[] $types The request type filters.
|
||||
* @return int
|
||||
* @throws coding_exception
|
||||
* @throws dml_exception
|
||||
*/
|
||||
public static function get_data_requests_count($userid = 0, $statuses = [], $types = []) {
|
||||
global $DB, $USER;
|
||||
$count = 0;
|
||||
$sqlparams = [];
|
||||
$sqlconditions = [];
|
||||
if (!empty($statuses)) {
|
||||
list($statusinsql, $sqlparams) = $DB->get_in_or_equal($statuses, SQL_PARAMS_NAMED);
|
||||
$sqlconditions[] = "status $statusinsql";
|
||||
}
|
||||
if (!empty($types)) {
|
||||
list($typeinsql, $typeparams) = $DB->get_in_or_equal($types, SQL_PARAMS_NAMED);
|
||||
$sqlconditions[] = "type $typeinsql";
|
||||
$sqlparams = array_merge($sqlparams, $typeparams);
|
||||
}
|
||||
if ($userid) {
|
||||
// Get the data requests for the user or data requests made by the user.
|
||||
$sqlconditions[] = "(userid = :userid OR requestedby = :requestedby)";
|
||||
$params = [
|
||||
'userid' => $userid,
|
||||
'requestedby' => $userid
|
||||
];
|
||||
|
||||
// Build a list of user IDs that the user is allowed to make data requests for.
|
||||
// Of course, the user should be included in this list.
|
||||
$alloweduserids = [$userid];
|
||||
// Get any users that the user can make data requests for.
|
||||
if ($children = helper::get_children_of_user($userid)) {
|
||||
// Get the list of user IDs of the children and merge to the allowed user IDs.
|
||||
$alloweduserids = array_merge($alloweduserids, array_keys($children));
|
||||
}
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($alloweduserids, SQL_PARAMS_NAMED);
|
||||
$sqlconditions[] .= "userid $insql";
|
||||
$select = implode(' AND ', $sqlconditions);
|
||||
$params = array_merge($params, $inparams, $sqlparams);
|
||||
|
||||
$count = data_request::count_records_select($select, $params);
|
||||
} 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)) {
|
||||
if (!empty($sqlconditions)) {
|
||||
$select = implode(' AND ', $sqlconditions);
|
||||
$count = data_request::count_records_select($select, $sqlparams);
|
||||
} else {
|
||||
$count = data_request::count_records();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether there is already an existing pending/in-progress data request for a user for a given request type.
|
||||
*
|
||||
|
||||
@@ -289,7 +289,8 @@ class data_registry {
|
||||
* @param int $forcedcategoryvalue Use this value as if this was this context level category.
|
||||
* @return int[]
|
||||
*/
|
||||
public static function get_effective_default_contextlevel_purpose_and_category($contextlevel, $forcedpurposevalue = false, $forcedcategoryvalue = false) {
|
||||
public static function get_effective_default_contextlevel_purpose_and_category($contextlevel, $forcedpurposevalue = false,
|
||||
$forcedcategoryvalue = false) {
|
||||
|
||||
list($purposeid, $categoryid) = self::get_defaults($contextlevel);
|
||||
|
||||
|
||||
@@ -23,10 +23,7 @@
|
||||
*/
|
||||
namespace tool_dataprivacy;
|
||||
|
||||
use tool_dataprivacy\api;
|
||||
use tool_dataprivacy\purpose;
|
||||
use tool_dataprivacy\context_instance;
|
||||
use tool_dataprivacy\data_registry;
|
||||
use core_privacy\manager;
|
||||
use tool_dataprivacy\expired_context;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
@@ -90,7 +87,7 @@ abstract class expired_contexts_manager {
|
||||
return $numprocessed;
|
||||
}
|
||||
|
||||
$privacymanager = new \core_privacy\manager();
|
||||
$privacymanager = new manager();
|
||||
$privacymanager->set_observer(new \tool_dataprivacy\manager_observer());
|
||||
|
||||
foreach ($this->get_context_levels() as $level) {
|
||||
@@ -118,11 +115,11 @@ abstract class expired_contexts_manager {
|
||||
/**
|
||||
* Deletes user data from the provided context.
|
||||
*
|
||||
* @param \core_privacy\manager $privacymanager
|
||||
* @param \tool_dataprivacy\expired_context $expiredctx
|
||||
* @param manager $privacymanager
|
||||
* @param expired_context $expiredctx
|
||||
* @return \context|false
|
||||
*/
|
||||
protected function delete_expired_context(\core_privacy\manager $privacymanager, \tool_dataprivacy\expired_context $expiredctx) {
|
||||
protected function delete_expired_context(manager $privacymanager, expired_context $expiredctx) {
|
||||
|
||||
$context = \context::instance_by_id($expiredctx->get('contextid'), IGNORE_MISSING);
|
||||
if (!$context) {
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
*/
|
||||
namespace tool_dataprivacy;
|
||||
|
||||
use tool_dataprivacy\purpose;
|
||||
use tool_dataprivacy\context_instance;
|
||||
use core_privacy\manager;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
@@ -110,11 +109,11 @@ class expired_user_contexts extends \tool_dataprivacy\expired_contexts_manager {
|
||||
*
|
||||
* Overwritten to delete the user.
|
||||
*
|
||||
* @param \core_privacy\manager $privacymanager
|
||||
* @param \tool_dataprivacy\expired_context $expiredctx
|
||||
* @param manager $privacymanager
|
||||
* @param expired_context $expiredctx
|
||||
* @return \context|false
|
||||
*/
|
||||
protected function delete_expired_context(\core_privacy\manager $privacymanager, \tool_dataprivacy\expired_context $expiredctx) {
|
||||
protected function delete_expired_context(manager $privacymanager, expired_context $expiredctx) {
|
||||
$context = \context::instance_by_id($expiredctx->get('contextid'), IGNORE_MISSING);
|
||||
if (!$context) {
|
||||
api::delete_expired_context($expiredctx->get('contextid'));
|
||||
|
||||
@@ -27,6 +27,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use tool_dataprivacy\api;
|
||||
use tool_dataprivacy\data_registry;
|
||||
use tool_dataprivacy\purpose;
|
||||
|
||||
/**
|
||||
* Context instance data form.
|
||||
@@ -186,12 +187,12 @@ class context_instance extends \core\form\persistent {
|
||||
/**
|
||||
* Returns the purpose display text.
|
||||
*
|
||||
* @param \tool_dataprivacy\purpose $effectivepurpose
|
||||
* @param purpose $effectivepurpose
|
||||
* @param int $retentioncontextlevel
|
||||
* @param \context $context The context, just for displaying (filters) purposes.
|
||||
* @return string
|
||||
*/
|
||||
protected static function get_retention_display_text(\tool_dataprivacy\purpose $effectivepurpose, $retentioncontextlevel, \context $context) {
|
||||
protected static function get_retention_display_text(purpose $effectivepurpose, $retentioncontextlevel, \context $context) {
|
||||
global $PAGE;
|
||||
|
||||
$renderer = $PAGE->get_renderer('tool_dataprivacy');
|
||||
|
||||
@@ -35,6 +35,17 @@ use tool_dataprivacy\api;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class helper {
|
||||
/** The default number of results to be shown per page. */
|
||||
const DEFAULT_PAGE_SIZE = 20;
|
||||
|
||||
/** Filter constant associated with the request type filter. */
|
||||
const FILTER_TYPE = 1;
|
||||
|
||||
/** Filter constant associated with the request status filter. */
|
||||
const FILTER_STATUS = 2;
|
||||
|
||||
/** The request filters preference key. */
|
||||
const PREF_REQUEST_FILTERS = 'tool_dataprivacy_request-filters';
|
||||
|
||||
/**
|
||||
* Retrieves the human-readable text value of a data request type.
|
||||
@@ -45,16 +56,11 @@ class helper {
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function get_request_type_string($requesttype) {
|
||||
switch ($requesttype) {
|
||||
case api::DATAREQUEST_TYPE_EXPORT:
|
||||
return get_string('requesttypeexport', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_TYPE_DELETE:
|
||||
return get_string('requesttypedelete', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_TYPE_OTHERS:
|
||||
return get_string('requesttypeothers', 'tool_dataprivacy');
|
||||
default:
|
||||
throw new moodle_exception('errorinvalidrequesttype', 'tool_dataprivacy');
|
||||
$types = self::get_request_types();
|
||||
if (!isset($types[$requesttype])) {
|
||||
throw new moodle_exception('errorinvalidrequesttype', 'tool_dataprivacy');
|
||||
}
|
||||
return $types[$requesttype];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,16 +72,37 @@ class helper {
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function get_shortened_request_type_string($requesttype) {
|
||||
switch ($requesttype) {
|
||||
case api::DATAREQUEST_TYPE_EXPORT:
|
||||
return get_string('requesttypeexportshort', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_TYPE_DELETE:
|
||||
return get_string('requesttypedeleteshort', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_TYPE_OTHERS:
|
||||
return get_string('requesttypeothersshort', 'tool_dataprivacy');
|
||||
default:
|
||||
throw new moodle_exception('errorinvalidrequesttype', 'tool_dataprivacy');
|
||||
$types = self::get_request_types_short();
|
||||
if (!isset($types[$requesttype])) {
|
||||
throw new moodle_exception('errorinvalidrequesttype', 'tool_dataprivacy');
|
||||
}
|
||||
return $types[$requesttype];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key value-pairs of request type code and their string value.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_request_types() {
|
||||
return [
|
||||
api::DATAREQUEST_TYPE_EXPORT => get_string('requesttypeexport', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_TYPE_DELETE => get_string('requesttypedelete', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_TYPE_OTHERS => get_string('requesttypeothers', 'tool_dataprivacy'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key value-pairs of request type code and their shortened string value.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_request_types_short() {
|
||||
return [
|
||||
api::DATAREQUEST_TYPE_EXPORT => get_string('requesttypeexportshort', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_TYPE_DELETE => get_string('requesttypedeleteshort', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_TYPE_OTHERS => get_string('requesttypeothersshort', 'tool_dataprivacy'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,30 +110,32 @@ class helper {
|
||||
*
|
||||
* @param int $status The request status.
|
||||
* @return string
|
||||
* @throws coding_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function get_request_status_string($status) {
|
||||
switch ($status) {
|
||||
case api::DATAREQUEST_STATUS_PENDING:
|
||||
return get_string('statuspending', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_STATUS_PREPROCESSING:
|
||||
return get_string('statuspreprocessing', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_STATUS_AWAITING_APPROVAL:
|
||||
return get_string('statusawaitingapproval', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_STATUS_APPROVED:
|
||||
return get_string('statusapproved', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_STATUS_PROCESSING:
|
||||
return get_string('statusprocessing', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_STATUS_COMPLETE:
|
||||
return get_string('statuscomplete', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_STATUS_CANCELLED:
|
||||
return get_string('statuscancelled', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_STATUS_REJECTED:
|
||||
return get_string('statusrejected', 'tool_dataprivacy');
|
||||
default:
|
||||
throw new moodle_exception('errorinvalidrequeststatus', 'tool_dataprivacy');
|
||||
$statuses = self::get_request_statuses();
|
||||
if (!isset($statuses[$status])) {
|
||||
throw new moodle_exception('errorinvalidrequeststatus', 'tool_dataprivacy');
|
||||
}
|
||||
return $statuses[$status];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key value-pairs of request status code and string value.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_request_statuses() {
|
||||
return [
|
||||
api::DATAREQUEST_STATUS_PENDING => get_string('statuspending', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_PREPROCESSING => get_string('statuspreprocessing', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_AWAITING_APPROVAL => get_string('statusawaitingapproval', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_APPROVED => get_string('statusapproved', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_PROCESSING => get_string('statusprocessing', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_COMPLETE => get_string('statuscomplete', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_CANCELLED => get_string('statuscancelled', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_REJECTED => get_string('statusrejected', 'tool_dataprivacy'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +161,7 @@ class helper {
|
||||
'contextlevel' => CONTEXT_USER
|
||||
];
|
||||
|
||||
// The final list of users that we will return;
|
||||
// The final list of users that we will return.
|
||||
$finalresults = [];
|
||||
|
||||
// Our prospective list of users.
|
||||
@@ -146,4 +175,34 @@ class helper {
|
||||
}
|
||||
return $finalresults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get options for the data requests filter.
|
||||
*
|
||||
* @return array
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public static function get_request_filter_options() {
|
||||
$filters = [
|
||||
self::FILTER_TYPE => (object)[
|
||||
'name' => get_string('requesttype', 'tool_dataprivacy'),
|
||||
'options' => self::get_request_types_short()
|
||||
],
|
||||
self::FILTER_STATUS => (object)[
|
||||
'name' => get_string('requeststatus', 'tool_dataprivacy'),
|
||||
'options' => self::get_request_statuses()
|
||||
],
|
||||
];
|
||||
$options = [];
|
||||
foreach ($filters as $category => $filtercategory) {
|
||||
foreach ($filtercategory->options as $key => $name) {
|
||||
$option = (object)[
|
||||
'category' => $filtercategory->name,
|
||||
'name' => $name
|
||||
];
|
||||
$options["{$category}:{$key}"] = get_string('filteroption', 'tool_dataprivacy', $option);
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace tool_dataprivacy\output;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use coding_exception;
|
||||
use dml_exception;
|
||||
use moodle_exception;
|
||||
use moodle_url;
|
||||
use renderable;
|
||||
@@ -34,7 +33,7 @@ use single_select;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
use tool_dataprivacy\data_request;
|
||||
use tool_dataprivacy\output\expired_contexts_table;
|
||||
use tool_dataprivacy\local\helper;
|
||||
|
||||
/**
|
||||
* Class containing data for a user's data requests.
|
||||
@@ -44,9 +43,6 @@ use tool_dataprivacy\output\expired_contexts_table;
|
||||
*/
|
||||
class data_deletion_page implements renderable, templatable {
|
||||
|
||||
/** The default number of results to be shown per page. */
|
||||
const DEFAULT_PAGE_SIZE = 20;
|
||||
|
||||
/** @var data_request[] $requests List of data requests. */
|
||||
protected $filter = null;
|
||||
|
||||
@@ -57,7 +53,7 @@ class data_deletion_page implements renderable, templatable {
|
||||
* Construct this renderable.
|
||||
*
|
||||
* @param \tool_dataprivacy\data_request[] $filter
|
||||
* @param \tool_dataprivacy\expired_contexts_table $expiredcontextstable
|
||||
* @param expired_contexts_table $expiredcontextstable
|
||||
*/
|
||||
public function __construct($filter, expired_contexts_table $expiredcontextstable) {
|
||||
$this->filter = $filter;
|
||||
@@ -70,7 +66,6 @@ class data_deletion_page implements renderable, templatable {
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
* @throws coding_exception
|
||||
* @throws dml_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
@@ -87,7 +82,7 @@ class data_deletion_page implements renderable, templatable {
|
||||
$data->filter = $filterselector->export_for_template($output);
|
||||
|
||||
ob_start();
|
||||
$this->expiredcontextstable->out(self::DEFAULT_PAGE_SIZE, true);
|
||||
$this->expiredcontextstable->out(helper::DEFAULT_PAGE_SIZE, true);
|
||||
$expiredcontexts = ob_get_contents();
|
||||
ob_end_clean();
|
||||
$data->expiredcontexts = $expiredcontexts;
|
||||
|
||||
@@ -24,20 +24,17 @@
|
||||
namespace tool_dataprivacy\output;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use action_menu;
|
||||
use action_menu_link_secondary;
|
||||
use coding_exception;
|
||||
use context_system;
|
||||
use dml_exception;
|
||||
use moodle_exception;
|
||||
use moodle_url;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use single_select;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
use tool_dataprivacy\api;
|
||||
use tool_dataprivacy\data_request;
|
||||
use tool_dataprivacy\external\data_request_exporter;
|
||||
use tool_dataprivacy\local\helper;
|
||||
|
||||
/**
|
||||
* Class containing data for a user's data requests.
|
||||
@@ -47,16 +44,21 @@ use tool_dataprivacy\external\data_request_exporter;
|
||||
*/
|
||||
class data_requests_page implements renderable, templatable {
|
||||
|
||||
/** @var data_request[] $requests List of data requests. */
|
||||
protected $requests = [];
|
||||
/** @var data_requests_table $table The data requests table. */
|
||||
protected $table;
|
||||
|
||||
/** @var int[] $filters The applied filters. */
|
||||
protected $filters = [];
|
||||
|
||||
/**
|
||||
* Construct this renderable.
|
||||
*
|
||||
* @param data_request[] $requests
|
||||
* @param data_requests_table $table The data requests table.
|
||||
* @param int[] $filters The applied filters.
|
||||
*/
|
||||
public function __construct($requests) {
|
||||
$this->requests = $requests;
|
||||
public function __construct($table, $filters) {
|
||||
$this->table = $table;
|
||||
$this->filters = $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,43 +80,17 @@ class data_requests_page implements renderable, templatable {
|
||||
$data->httpsite = array('message' => $httpwarningmessage, 'announce' => 1);
|
||||
}
|
||||
|
||||
$requests = [];
|
||||
foreach ($this->requests as $request) {
|
||||
$requestid = $request->get('id');
|
||||
$status = $request->get('status');
|
||||
$requestexporter = new data_request_exporter($request, ['context' => context_system::instance()]);
|
||||
$item = $requestexporter->export($output);
|
||||
$url = new moodle_url('/admin/tool/dataprivacy/datarequests.php');
|
||||
$filteroptions = helper::get_request_filter_options();
|
||||
$filter = new request_filter($filteroptions, $this->filters, $url);
|
||||
$data->filter = $filter->export_for_template($output);
|
||||
|
||||
// Prepare actions.
|
||||
$actions = [];
|
||||
ob_start();
|
||||
$this->table->out(helper::DEFAULT_PAGE_SIZE, true);
|
||||
$requests = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
// View action.
|
||||
$actionurl = new moodle_url('#');
|
||||
$actiondata = ['data-action' => 'view', 'data-requestid' => $requestid];
|
||||
$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);
|
||||
|
||||
// Deny.
|
||||
$actiondata['data-action'] = 'deny';
|
||||
$actiontext = get_string('denyrequest', 'tool_dataprivacy');
|
||||
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
|
||||
}
|
||||
|
||||
$actionsmenu = new action_menu($actions);
|
||||
$actionsmenu->set_menu_trigger(get_string('actions'));
|
||||
$actionsmenu->set_owner_selector('request-actions-' . $requestid);
|
||||
$actionsmenu->set_alignment(\action_menu::TL, \action_menu::BL);
|
||||
$item->actions = $actionsmenu->export_for_template($output);
|
||||
|
||||
$requests[] = $item;
|
||||
}
|
||||
$data->requests = $requests;
|
||||
$data->datarequests = $requests;
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Contains the class used for the displaying the data requests table.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta <jun@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy\output;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/tablelib.php');
|
||||
|
||||
use action_menu;
|
||||
use action_menu_link_secondary;
|
||||
use coding_exception;
|
||||
use dml_exception;
|
||||
use html_writer;
|
||||
use moodle_url;
|
||||
use stdClass;
|
||||
use table_sql;
|
||||
use tool_dataprivacy\api;
|
||||
use tool_dataprivacy\external\data_request_exporter;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
/**
|
||||
* The class for displaying the data requests table.
|
||||
*
|
||||
* @copyright 2018 Jun Pataleta <jun@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class data_requests_table extends table_sql {
|
||||
|
||||
/** @var int The user ID. */
|
||||
protected $userid = 0;
|
||||
|
||||
/** @var int[] The status filters. */
|
||||
protected $statuses = [];
|
||||
|
||||
/** @var int[] The request type filters. */
|
||||
protected $types = [];
|
||||
|
||||
/** @var bool Whether this table is being rendered for managing data requests. */
|
||||
protected $manage = false;
|
||||
|
||||
/** @var stdClass[] Array of data request persistents. */
|
||||
protected $datarequests = [];
|
||||
|
||||
/**
|
||||
* data_requests_table constructor.
|
||||
*
|
||||
* @param int $userid The user ID
|
||||
* @param int[] $statuses
|
||||
* @param int[] $types
|
||||
* @param bool $manage
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function __construct($userid = 0, $statuses = [], $types = [], $manage = false) {
|
||||
parent::__construct('data-requests-table');
|
||||
|
||||
$this->userid = $userid;
|
||||
$this->statuses = $statuses;
|
||||
$this->types = $types;
|
||||
$this->manage = $manage;
|
||||
|
||||
$columnheaders = [
|
||||
'type' => get_string('requesttype', 'tool_dataprivacy'),
|
||||
'userid' => get_string('user', 'tool_dataprivacy'),
|
||||
'timecreated' => get_string('daterequested', 'tool_dataprivacy'),
|
||||
'requestedby' => get_string('requestby', 'tool_dataprivacy'),
|
||||
'status' => get_string('requeststatus', 'tool_dataprivacy'),
|
||||
'comments' => get_string('message', 'tool_dataprivacy'),
|
||||
'actions' => '',
|
||||
];
|
||||
|
||||
$this->define_columns(array_keys($columnheaders));
|
||||
$this->define_headers(array_values($columnheaders));
|
||||
$this->no_sorting('actions');
|
||||
}
|
||||
|
||||
/**
|
||||
* The type column.
|
||||
*
|
||||
* @param stdClass $data The row data.
|
||||
* @return string
|
||||
*/
|
||||
public function col_type($data) {
|
||||
if ($this->manage) {
|
||||
return $data->typenameshort;
|
||||
}
|
||||
return $data->typename;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user column.
|
||||
*
|
||||
* @param stdClass $data The row data.
|
||||
* @return mixed
|
||||
*/
|
||||
public function col_userid($data) {
|
||||
$user = $data->foruser;
|
||||
return html_writer::link($user->profileurl, $user->fullname, ['title' => get_string('viewprofile')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The context information column.
|
||||
*
|
||||
* @param stdClass $data The row data.
|
||||
* @return string
|
||||
*/
|
||||
public function col_timecreated($data) {
|
||||
return userdate($data->timecreated);
|
||||
}
|
||||
|
||||
/**
|
||||
* The requesting user's column.
|
||||
*
|
||||
* @param stdClass $data The row data.
|
||||
* @return mixed
|
||||
*/
|
||||
public function col_requestedby($data) {
|
||||
$user = $data->requestedbyuser;
|
||||
return html_writer::link($user->profileurl, $user->fullname, ['title' => get_string('viewprofile')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The status column.
|
||||
*
|
||||
* @param stdClass $data The row data.
|
||||
* @return mixed
|
||||
*/
|
||||
public function col_status($data) {
|
||||
return html_writer::span($data->statuslabel, 'label ' . $data->statuslabelclass);
|
||||
}
|
||||
|
||||
/**
|
||||
* The comments column.
|
||||
*
|
||||
* @param stdClass $data The row data.
|
||||
* @return string
|
||||
*/
|
||||
public function col_comments($data) {
|
||||
return shorten_text($data->comments, 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* The actions column.
|
||||
*
|
||||
* @param stdClass $data The row data.
|
||||
* @return string
|
||||
*/
|
||||
public function col_actions($data) {
|
||||
global $OUTPUT;
|
||||
|
||||
$requestid = $data->id;
|
||||
$status = $data->status;
|
||||
|
||||
// Prepare actions.
|
||||
$actions = [];
|
||||
|
||||
// View action.
|
||||
$actionurl = new moodle_url('#');
|
||||
$actiondata = ['data-action' => 'view', 'data-requestid' => $requestid];
|
||||
$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);
|
||||
|
||||
// Deny.
|
||||
$actiondata['data-action'] = 'deny';
|
||||
$actiontext = get_string('denyrequest', 'tool_dataprivacy');
|
||||
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
|
||||
}
|
||||
|
||||
$actionsmenu = new action_menu($actions);
|
||||
$actionsmenu->set_menu_trigger(get_string('actions'));
|
||||
$actionsmenu->set_owner_selector('request-actions-' . $requestid);
|
||||
$actionsmenu->set_alignment(\action_menu::TL, \action_menu::BL);
|
||||
|
||||
return $OUTPUT->render($actionsmenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the database for results to display in the table.
|
||||
*
|
||||
* @param int $pagesize size of page for paginated displayed table.
|
||||
* @param bool $useinitialsbar do you want to use the initials bar.
|
||||
* @throws dml_exception
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function query_db($pagesize, $useinitialsbar = true) {
|
||||
global $PAGE;
|
||||
|
||||
// Count data requests from the given conditions.
|
||||
$total = api::get_data_requests_count($this->userid, $this->statuses, $this->types);
|
||||
$this->pagesize($pagesize, $total);
|
||||
|
||||
$sort = $this->get_sql_sort();
|
||||
|
||||
// Get data requests from the given conditions.
|
||||
$datarequests = api::get_data_requests($this->userid, $this->statuses, $this->types, $sort,
|
||||
$this->get_page_start(), $this->get_page_size());
|
||||
$this->rawdata = [];
|
||||
$context = \context_system::instance();
|
||||
$renderer = $PAGE->get_renderer('tool_dataprivacy');
|
||||
foreach ($datarequests as $persistent) {
|
||||
$exporter = new data_request_exporter($persistent, ['context' => $context]);
|
||||
$this->rawdata[] = $exporter->export($renderer);
|
||||
}
|
||||
|
||||
// Set initial bars.
|
||||
if ($useinitialsbar) {
|
||||
$this->initialbars($total > $pagesize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override default implementation to display a more meaningful information to the user.
|
||||
*/
|
||||
public function print_nothing_to_display() {
|
||||
global $OUTPUT;
|
||||
echo $this->render_reset_button();
|
||||
$this->print_initials_bar();
|
||||
if (!empty($this->statuses) || !empty($this->types)) {
|
||||
$message = get_string('nodatarequestsmatchingfilter', 'tool_dataprivacy');
|
||||
} else {
|
||||
$message = get_string('nodatarequests', 'tool_dataprivacy');
|
||||
}
|
||||
echo $OUTPUT->notification($message, 'warning');
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the table's show_hide_link method to prevent the show/hide links from rendering.
|
||||
*
|
||||
* @param string $column the column name, index into various names.
|
||||
* @param int $index numerical index of the column.
|
||||
* @return string HTML fragment.
|
||||
*/
|
||||
protected function show_hide_link($column, $index) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?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 the filter options data for rendering the autocomplete element for the data requests page.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy\output;
|
||||
|
||||
use moodle_url;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class containing the filter options data for rendering the autocomplete element for the data requests page.
|
||||
*
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class request_filter implements renderable, templatable {
|
||||
|
||||
/** @var array $filteroptions The filter options. */
|
||||
protected $filteroptions;
|
||||
|
||||
/** @var array $selectedoptions The list of selected filter option values. */
|
||||
protected $selectedoptions;
|
||||
|
||||
/** @var moodle_url|string $baseurl The url with params needed to call up this page. */
|
||||
protected $baseurl;
|
||||
|
||||
/**
|
||||
* request_filter constructor.
|
||||
*
|
||||
* @param array $filteroptions The filter options.
|
||||
* @param array $selectedoptions The list of selected filter option values.
|
||||
* @param string|moodle_url $baseurl The url with params needed to call up this page.
|
||||
*/
|
||||
public function __construct($filteroptions, $selectedoptions, $baseurl = null) {
|
||||
$this->filteroptions = $filteroptions;
|
||||
$this->selectedoptions = $selectedoptions;
|
||||
if (!empty($baseurl)) {
|
||||
$this->baseurl = new moodle_url($baseurl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to export the renderer data in a format that is suitable for a mustache template.
|
||||
*
|
||||
* @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
|
||||
* @return stdClass|array
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
global $PAGE;
|
||||
$data = new stdClass();
|
||||
if (empty($this->baseurl)) {
|
||||
$this->baseurl = $PAGE->url;
|
||||
}
|
||||
$data->action = $this->baseurl->out(false);
|
||||
|
||||
foreach ($this->selectedoptions as $option) {
|
||||
if (!isset($this->filteroptions[$option])) {
|
||||
$this->filteroptions[$option] = $option;
|
||||
}
|
||||
}
|
||||
|
||||
$data->filteroptions = [];
|
||||
foreach ($this->filteroptions as $value => $label) {
|
||||
$selected = in_array($value, $this->selectedoptions);
|
||||
$filteroption = (object)[
|
||||
'value' => $value,
|
||||
'label' => $label
|
||||
];
|
||||
$filteroption->selected = $selected;
|
||||
$data->filteroptions[] = $filteroption;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,10 @@ class provider implements
|
||||
\core_privacy\local\metadata\provider,
|
||||
|
||||
// This tool may provide access to and deletion of user data.
|
||||
\core_privacy\local\request\plugin\provider {
|
||||
\core_privacy\local\request\plugin\provider,
|
||||
|
||||
// This plugin has some sitewide user preferences to export.
|
||||
\core_privacy\local\request\user_preference_provider {
|
||||
/**
|
||||
* Returns meta data about this system.
|
||||
*
|
||||
@@ -70,6 +73,10 @@ class provider implements
|
||||
],
|
||||
'privacy:metadata:request'
|
||||
);
|
||||
|
||||
$collection->add_user_preference(tool_helper::PREF_REQUEST_FILTERS,
|
||||
'privacy:metadata:preference:tool_dataprivacy_request-filters');
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
@@ -162,4 +169,36 @@ class provider implements
|
||||
*/
|
||||
public static function delete_data_for_user(approved_contextlist $contextlist) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$preffilter = get_user_preferences(tool_helper::PREF_REQUEST_FILTERS, null, $userid);
|
||||
if ($preffilter !== null) {
|
||||
$filters = json_decode($preffilter);
|
||||
$descriptions = [];
|
||||
foreach ($filters as $filter) {
|
||||
list($category, $value) = explode(':', $filter);
|
||||
$option = new stdClass();
|
||||
switch($category) {
|
||||
case tool_helper::FILTER_TYPE:
|
||||
$option->category = get_string('requesttype', 'tool_dataprivacy');
|
||||
$option->name = tool_helper::get_shortened_request_type_string($value);
|
||||
break;
|
||||
case tool_helper::FILTER_STATUS:
|
||||
$option->category = get_string('requeststatus', 'tool_dataprivacy');
|
||||
$option->name = tool_helper::get_request_status_string($value);
|
||||
break;
|
||||
}
|
||||
$descriptions[] = get_string('filteroption', 'tool_dataprivacy', $option);
|
||||
}
|
||||
// Export the filter preference as comma-separated values and text descriptions.
|
||||
$values = implode(', ', $filters);
|
||||
$descriptionstext = implode(', ', $descriptions);
|
||||
writer::export_user_preference('tool_dataprivacy', tool_helper::PREF_REQUEST_FILTERS, $values, $descriptionstext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ class purpose extends \core\persistent {
|
||||
// Replicate self::read.
|
||||
$this->from_record($data);
|
||||
|
||||
// Using validate() as self::$validated is private.
|
||||
// Validate the purpose record.
|
||||
$this->validate();
|
||||
|
||||
// Now replicate the parent constructor.
|
||||
|
||||
@@ -54,7 +54,7 @@ class initiate_data_request_task extends adhoc_task {
|
||||
public function execute() {
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/admin/tool/dataprivacy/lib.php');
|
||||
require_once($CFG->dirroot . "/{$CFG->admin}/tool/dataprivacy/lib.php");
|
||||
|
||||
if (!isset($this->get_custom_data()->requestid)) {
|
||||
throw new coding_exception('The custom data \'requestid\' is required.');
|
||||
|
||||
@@ -57,7 +57,7 @@ class process_data_request_task extends adhoc_task {
|
||||
public function execute() {
|
||||
global $CFG, $PAGE, $SITE;
|
||||
|
||||
require_once($CFG->dirroot . '/admin/tool/dataprivacy/lib.php');
|
||||
require_once($CFG->dirroot . "/{$CFG->admin}/tool/dataprivacy/lib.php");
|
||||
|
||||
if (!isset($this->get_custom_data()->requestid)) {
|
||||
throw new coding_exception('The custom data \'requestid\' is required.');
|
||||
|
||||
@@ -53,8 +53,9 @@ if ($manage) {
|
||||
$PAGE->set_context($context);
|
||||
|
||||
// If contactdataprotectionofficer is disabled, send the user back to the profile page, or the privacy policy page.
|
||||
if (!\tool_dataprivacy\api::can_contact_dpo()) {
|
||||
redirect($returnurl, get_string('contactdpoviaprivacypolicy', 'tool_dataprivacy'), \core\output\notification::NOTIFY_ERROR);
|
||||
// That is, unless you have sufficient capabilities to perform this on behalf of a user.
|
||||
if (!$manage && !\tool_dataprivacy\api::can_contact_dpo()) {
|
||||
redirect($returnurl, get_string('contactdpoviaprivacypolicy', 'tool_dataprivacy'), 0, \core\output\notification::NOTIFY_ERROR);
|
||||
}
|
||||
|
||||
$mform = new tool_dataprivacy_data_request_form($url->out(false), ['manage' => !empty($manage)]);
|
||||
|
||||
@@ -44,19 +44,28 @@ class tool_dataprivacy_data_request_form extends moodleform {
|
||||
/**
|
||||
* Form definition.
|
||||
*
|
||||
* @throws HTML_QuickForm_Error
|
||||
* @throws coding_exception
|
||||
* @throws dml_exception
|
||||
*/
|
||||
public function definition() {
|
||||
global $DB, $USER;
|
||||
global $USER;
|
||||
$mform =& $this->_form;
|
||||
|
||||
$this->manage = $this->_customdata['manage'];
|
||||
if ($this->manage) {
|
||||
$options = [
|
||||
'ajax' => 'tool_dataprivacy/form-user-selector',
|
||||
'multiple' => false
|
||||
'valuehtmlcallback' => function($value) {
|
||||
global $OUTPUT;
|
||||
|
||||
$allusernames = get_all_user_name_fields(true);
|
||||
$fields = 'id, email, ' . $allusernames;
|
||||
$user = \core_user::get_user($value, $fields);
|
||||
$useroptiondata = [
|
||||
'fullname' => fullname($user),
|
||||
'email' => $user->email
|
||||
];
|
||||
return $OUTPUT->render_from_template('tool_dataprivacy/form-user-selector-suggestion', $useroptiondata);
|
||||
}
|
||||
];
|
||||
$mform->addElement('autocomplete', 'userid', get_string('requestfor', 'tool_dataprivacy'), [], $options);
|
||||
$mform->addRule('userid', null, 'required', null, 'client');
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
require_once(__DIR__ . '/../../../config.php');
|
||||
require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
|
||||
|
||||
require_login(null, false);
|
||||
|
||||
$filter = optional_param('filter', CONTEXT_COURSE, PARAM_INT);
|
||||
|
||||
$url = new moodle_url('/admin/tool/dataprivacy/datadeletion.php');
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
require_once(__DIR__ . '/../../../config.php');
|
||||
require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
|
||||
|
||||
require_login(null, false);
|
||||
|
||||
$contextlevel = optional_param('contextlevel', CONTEXT_SYSTEM, PARAM_INT);
|
||||
$contextid = optional_param('contextid', 0, PARAM_INT);
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
require_once("../../../config.php");
|
||||
require_once('lib.php');
|
||||
|
||||
require_login(null, false);
|
||||
|
||||
$url = new moodle_url('/admin/tool/dataprivacy/datarequests.php');
|
||||
|
||||
$title = get_string('datarequests', 'tool_dataprivacy');
|
||||
@@ -34,8 +36,38 @@ $title = get_string('datarequests', 'tool_dataprivacy');
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading($title);
|
||||
|
||||
$requests = tool_dataprivacy\api::get_data_requests();
|
||||
$requestlist = new tool_dataprivacy\output\data_requests_page($requests);
|
||||
$filtersapplied = optional_param_array('request-filters', [-1], PARAM_NOTAGS);
|
||||
$filterscleared = optional_param('filters-cleared', 0, PARAM_INT);
|
||||
if ($filtersapplied === [-1]) {
|
||||
// If there are no filters submitted, check if there is a saved filters from the user preferences.
|
||||
$filterprefs = get_user_preferences(\tool_dataprivacy\local\helper::PREF_REQUEST_FILTERS, null);
|
||||
if ($filterprefs && empty($filterscleared)) {
|
||||
$filtersapplied = json_decode($filterprefs);
|
||||
} else {
|
||||
$filtersapplied = [];
|
||||
}
|
||||
}
|
||||
// Save the current applied filters to the user preferences.
|
||||
set_user_preference(\tool_dataprivacy\local\helper::PREF_REQUEST_FILTERS, json_encode($filtersapplied));
|
||||
|
||||
$types = [];
|
||||
$statuses = [];
|
||||
foreach ($filtersapplied as $filter) {
|
||||
list($category, $value) = explode(':', $filter);
|
||||
switch($category) {
|
||||
case \tool_dataprivacy\local\helper::FILTER_TYPE:
|
||||
$types[] = $value;
|
||||
break;
|
||||
case \tool_dataprivacy\local\helper::FILTER_STATUS:
|
||||
$statuses[] = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$table = new \tool_dataprivacy\output\data_requests_table(0, $statuses, $types, true);
|
||||
$table->baseurl = $url;
|
||||
|
||||
$requestlist = new tool_dataprivacy\output\data_requests_page($table, $filtersapplied);
|
||||
$requestlistoutput = $PAGE->get_renderer('tool_dataprivacy');
|
||||
echo $requestlistoutput->render($requestlist);
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
require_once(__DIR__ . '/../../../config.php');
|
||||
require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
|
||||
|
||||
require_login(null, false);
|
||||
|
||||
$url = new \moodle_url('/admin/tool/dataprivacy/defaults.php');
|
||||
$title = get_string('setdefaults', 'tool_dataprivacy');
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
require_once(__DIR__ . '/../../../config.php');
|
||||
|
||||
require_login(null, false);
|
||||
|
||||
$id = optional_param('id', 0, PARAM_INT);
|
||||
|
||||
$url = new \moodle_url('/admin/tool/dataprivacy/editcategory.php', array('id' => $id));
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
require_once(__DIR__ . '/../../../config.php');
|
||||
|
||||
require_login(null, false);
|
||||
|
||||
$id = optional_param('id', 0, PARAM_INT);
|
||||
|
||||
$url = new \moodle_url('/admin/tool/dataprivacy/editpurpose.php', array('id' => $id));
|
||||
|
||||
@@ -48,8 +48,8 @@ $string['compliant'] = 'Compliant';
|
||||
$string['confirmapproval'] = 'Do you really want to approve this data request?';
|
||||
$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 Data Protection Officer';
|
||||
$string['contactdataprotectionofficer_desc'] = 'If enabled, users will be able to contact the Data Protection Officer and make a data request via a link on their profile page.';
|
||||
$string['contactdataprotectionofficer'] = 'Contact the privacy officer';
|
||||
$string['contactdataprotectionofficer_desc'] = 'If enabled, users will be able to contact the privacy officer and make a data request via a link on their profile page.';
|
||||
$string['contextlevelname10'] = 'Site';
|
||||
$string['contextlevelname30'] = 'Users';
|
||||
$string['contextlevelname40'] = 'Course categories';
|
||||
@@ -57,7 +57,7 @@ $string['contextlevelname50'] = 'Courses';
|
||||
$string['contextlevelname70'] = 'Activity modules';
|
||||
$string['contextlevelname80'] = 'Blocks';
|
||||
$string['contextpurposecategorysaved'] = 'Purpose and category saved.';
|
||||
$string['contactdpoviaprivacypolicy'] = 'Please contact the Data Protection Officer as described in the privacy policy.';
|
||||
$string['contactdpoviaprivacypolicy'] = 'Please contact the privacy officer as described in the privacy policy.';
|
||||
$string['createcategory'] = 'Create data category';
|
||||
$string['createpurpose'] = 'Create data purpose';
|
||||
$string['datadeletion'] = 'Data deletion';
|
||||
@@ -82,8 +82,8 @@ $string['defaultssaved'] = 'Defaults saved';
|
||||
$string['deny'] = 'Deny';
|
||||
$string['denyrequest'] = 'Deny request';
|
||||
$string['download'] = 'Download';
|
||||
$string['dporolemapping'] = 'Data Protection Officer role mapping';
|
||||
$string['dporolemapping_desc'] = 'The Data Protection Officer can manage data requests. The capability tool/dataprivacy:managedatarequests must be allowed for a role to be listed as a Data Protection Officer role mapping option.';
|
||||
$string['dporolemapping'] = 'Privacy officer role mapping';
|
||||
$string['dporolemapping_desc'] = 'The privacy officer can manage data requests. The capability tool/dataprivacy:managedatarequests must be allowed for a role to be listed as a privacy officer role mapping option.';
|
||||
$string['editcategories'] = 'Edit categories';
|
||||
$string['editcategory'] = 'Edit category';
|
||||
$string['editcategories'] = 'Edit categories';
|
||||
@@ -101,8 +101,10 @@ $string['errorrequestalreadyexists'] = 'You already have an ongoing request.';
|
||||
$string['errorrequestnotfound'] = 'Request not found';
|
||||
$string['errorrequestnotwaitingforapproval'] = 'The request is not awaiting approval. Either it is not yet ready or it has already been processed.';
|
||||
$string['errorsendingmessagetodpo'] = 'An error was encountered while trying to send a message to {$a}.';
|
||||
$string['exceptionnotificationsubject'] = "Exception occured while processing privacy data";
|
||||
$string['exceptionnotificationbody'] = "<p>Exception occured while calling <b>{\$a->fullmethodname}</b>.<br>This means that plugin <b>{\$a->component}</b> did not complete processing data. Below you can find exception information that can be passed to the plugin developer.</p><pre>{\$a->message}<br>\n\n{\$a->backtrace}</pre>";
|
||||
$string['exceptionnotificationsubject'] = 'Exception occurred while processing privacy data';
|
||||
$string['exceptionnotificationbody'] = '<p>Exception occurred while calling <b>{$a->fullmethodname}</b>.<br>This means that plugin <b>{$a->component}</b> did not complete the processing of data. The following exception information may be passed on to the plugin developer:</p><pre>{$a->message}<br>
|
||||
|
||||
{$a->backtrace}</pre>';
|
||||
$string['expiredretentionperiodtask'] = 'Expired retention period';
|
||||
$string['expiry'] = 'Expiry';
|
||||
$string['expandplugin'] = 'Expand and collapse plugin.';
|
||||
@@ -110,6 +112,7 @@ $string['expandplugintype'] = 'Expand and collapse plugin type.';
|
||||
$string['explanationtitle'] = 'Icons used on this page and what they mean.';
|
||||
$string['external'] = 'Additional';
|
||||
$string['externalexplanation'] = 'An additional plugin installed on this site.';
|
||||
$string['filteroption'] = '{$a->category}: {$a->name}';
|
||||
$string['frontpagecourse'] = 'Front page course';
|
||||
$string['gdpr_art_6_1_a_description'] = 'The data subject has given consent to the processing of his or her personal data for one or more specific purposes';
|
||||
$string['gdpr_art_6_1_a_name'] = 'Consent (GDPR Art. 6.1(a))';
|
||||
@@ -160,6 +163,7 @@ $string['nameemail'] = '{$a->name} ({$a->email})';
|
||||
$string['nchildren'] = '{$a} children';
|
||||
$string['newrequest'] = 'New request';
|
||||
$string['nodatarequests'] = 'There are no data requests';
|
||||
$string['nodatarequestsmatchingfilter'] = 'There are no data requests matching the given filter';
|
||||
$string['noactivitiestoload'] = 'No activities';
|
||||
$string['noassignedroles'] = 'No assigned roles in this context';
|
||||
$string['noblockstoload'] = 'No blocks';
|
||||
@@ -174,11 +178,12 @@ $string['notset'] = 'Not set (use the default value)';
|
||||
$string['pluginregistry'] = 'Plugin privacy registry';
|
||||
$string['pluginregistrytitle'] = 'Plugin privacy compliance registry';
|
||||
$string['privacy'] = 'Privacy';
|
||||
$string['privacy:metadata:preference:tool_dataprivacy_request-filters'] = 'The filters currently applied to the data requests page.';
|
||||
$string['privacy:metadata:request'] = 'Information from personal data requests (subject access and deletion requests) made for this site.';
|
||||
$string['privacy:metadata:request:comments'] = 'Any user comments accompanying the request.';
|
||||
$string['privacy:metadata:request:userid'] = 'The ID of the user to whom the request belongs';
|
||||
$string['privacy:metadata:request:requestedby'] = 'The ID of the user making the request, if made on behalf of another user.';
|
||||
$string['privacy:metadata:request:dpocomment'] = 'Any comments made by the site\'s Data Protection Officer regarding the request.';
|
||||
$string['privacy:metadata:request:dpocomment'] = 'Any comments made by the site\'s privacy officer regarding the request.';
|
||||
$string['privacy:metadata:request:timecreated'] = 'The timestamp indicating when the request was made by the user.';
|
||||
$string['protected'] = 'Protected';
|
||||
$string['protectedlabel'] = 'The retention of this data has a higher legal precedent over a user\'s request to be forgotten. This data will only be deleted after the retention period has expired.';
|
||||
@@ -193,21 +198,22 @@ $string['purposeupdated'] = 'Purpose updated';
|
||||
$string['replyto'] = 'Reply to';
|
||||
$string['requestactions'] = 'Actions';
|
||||
$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['requestemailintro'] = 'You have received a data request:';
|
||||
$string['requestfor'] = 'Requesting for';
|
||||
$string['requeststatus'] = 'Status';
|
||||
$string['requestsubmitted'] = 'Your request has been submitted to the site\'s Data Protection Officer';
|
||||
$string['requestsubmitted'] = 'Your request has been submitted to the privacy officer';
|
||||
$string['requesttype'] = 'Type';
|
||||
$string['requesttypeuser'] = '{$a->typename} ({$a->user})';
|
||||
$string['requesttype_help'] = 'Select the reason why you would like to contact the site\'s Data Protection Officer';
|
||||
$string['requesttype_help'] = 'Select the reason why you would like to contact the privacy officer';
|
||||
$string['requesttypedelete'] = 'Delete all of my personal data';
|
||||
$string['requesttypedeleteshort'] = 'Delete';
|
||||
$string['requesttypeexport'] = 'Export all of my personal data';
|
||||
$string['requesttypeexportshort'] = 'Export';
|
||||
$string['requesttypeothers'] = 'General inquiry';
|
||||
$string['requesttypeothersshort'] = 'Others';
|
||||
$string['requesttypeothersshort'] = 'Message';
|
||||
$string['requiresattention'] = 'Requires attention.';
|
||||
$string['requiresattentionexplanation'] = 'This plugin does not implement the Moodle privacy API. If this plugin stores any personal data it will not be able to be exported or deleted through Moodle\'s privacy system.';
|
||||
$string['resultdeleted'] = 'You recently requested to have your account and personal data in {$a} to be deleted. This process has been completed and you will no longer be able to log in.';
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
require_once(__DIR__ . '/../../../config.php');
|
||||
require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
|
||||
|
||||
require_login(null, false);
|
||||
|
||||
$contextlevel = optional_param('contextlevel', CONTEXT_SYSTEM, PARAM_INT);
|
||||
$contextid = optional_param('contextid', 0, PARAM_INT);
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
require_once(__DIR__ . '/../../../config.php');
|
||||
|
||||
require_login(null, false);
|
||||
|
||||
$url = new moodle_url("/admin/tool/dataprivacy/purposes.php");
|
||||
$title = get_string('editpurposes', 'tool_dataprivacy');
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
}
|
||||
}}
|
||||
|
||||
<div classs="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<hr />
|
||||
<div class="row">
|
||||
{{#compliant}}
|
||||
@@ -64,17 +64,17 @@
|
||||
</div>
|
||||
|
||||
{{#compliant}}
|
||||
<div class="hide" data-section="{{raw_component}}" aria-expanded="false">
|
||||
<div class="hide" data-section="{{raw_component}}" aria-expanded="false" role="contentinfo">
|
||||
{{#metadata}}
|
||||
<hr />
|
||||
<div class="p-l-3">
|
||||
<dl class="row">
|
||||
<dt class="span3 col-xs-3">
|
||||
{{#link}}
|
||||
<a href="#{{name}}"><h5 style="word-wrap:break-word">{{name}}</h5></a>
|
||||
<a href="#{{name}}"><strong style="word-wrap:break-word">{{name}}</strong></a>
|
||||
{{/link}}
|
||||
{{^link}}
|
||||
<h5 style="word-wrap:break-word">{{name}}</h5>
|
||||
<strong style="word-wrap:break-word">{{name}}</strong>
|
||||
{{/link}}
|
||||
<div class="small text-muted" style="word-wrap:break-word">{{type}}</div>
|
||||
</dt>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -55,7 +55,7 @@
|
||||
<h3 id="{{plugin_type_raw}}">{{#pix}}t/collapsed, moodle, {{#str}}expandplugintype, tool_dataprivacy{{/str}}{{/pix}}{{plugin_type}}</h3>
|
||||
</a>
|
||||
</div>
|
||||
<div class="hide p-b-1" data-plugintarget="{{plugin_type_raw}}" aria-expanded="false">
|
||||
<div class="hide p-b-1" data-plugintarget="{{plugin_type_raw}}" aria-expanded="false" role="contentinfo">
|
||||
{{#plugins}}
|
||||
{{> tool_dataprivacy/component_status}}
|
||||
{{/plugins}}
|
||||
|
||||
@@ -47,59 +47,68 @@
|
||||
"datarequestsurl": "#"
|
||||
}
|
||||
}}
|
||||
<style>
|
||||
table, th, td {
|
||||
border: 1px solid black;
|
||||
padding: 0.5em;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<p>{{#str}}emailsalutation, tool_dataprivacy, {{dponame}}{{/str}}</p>
|
||||
<p>{{#str}}requestemailintro, tool_dataprivacy{{/str}}</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
{{#str}}requesttype, tool_dataprivacy{{/str}}
|
||||
</th>
|
||||
<td>
|
||||
{{requesttype}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
{{#str}}requestfor, tool_dataprivacy{{/str}}
|
||||
</th>
|
||||
<td>
|
||||
{{requestfor}}
|
||||
</td>
|
||||
</tr>
|
||||
{{^forself}}
|
||||
<tr>
|
||||
<th scope="row">
|
||||
{{#str}}requestby, tool_dataprivacy{{/str}}
|
||||
</th>
|
||||
<td>
|
||||
{{requestedby}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/forself}}
|
||||
<tr>
|
||||
<th scope="row">
|
||||
{{#str}}requestcomments, tool_dataprivacy{{/str}}
|
||||
</th>
|
||||
<td>
|
||||
{{{requestcomments}}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
{{#str}}daterequested, tool_dataprivacy{{/str}}
|
||||
</th>
|
||||
<td>
|
||||
{{requestdate}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
<a href="{{datarequestsurl}}">{{#str}}viewrequest, tool_dataprivacy{{/str}}</a>
|
||||
</div>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
table, th, td {
|
||||
border: 1px solid black;
|
||||
padding: 0.5em;
|
||||
}
|
||||
</style>
|
||||
<title>{{#str}}datarequestemailsubject, tool_dataprivacy, {{requesttype}}{{/str}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<p>{{#str}}emailsalutation, tool_dataprivacy, {{dponame}}{{/str}}</p>
|
||||
<p>{{#str}}requestemailintro, tool_dataprivacy{{/str}}</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
{{#str}}requesttype, tool_dataprivacy{{/str}}
|
||||
</th>
|
||||
<td>
|
||||
{{requesttype}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
{{#str}}requestfor, tool_dataprivacy{{/str}}
|
||||
</th>
|
||||
<td>
|
||||
{{requestfor}}
|
||||
</td>
|
||||
</tr>
|
||||
{{^forself}}
|
||||
<tr>
|
||||
<th scope="row">
|
||||
{{#str}}requestby, tool_dataprivacy{{/str}}
|
||||
</th>
|
||||
<td>
|
||||
{{requestedby}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/forself}}
|
||||
<tr>
|
||||
<th scope="row">
|
||||
{{#str}}requestcomments, tool_dataprivacy{{/str}}
|
||||
</th>
|
||||
<td>
|
||||
{{{requestcomments}}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
{{#str}}daterequested, tool_dataprivacy{{/str}}
|
||||
</th>
|
||||
<td>
|
||||
{{requestdate}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
<a href="{{datarequestsurl}}">{{#str}}viewrequest, tool_dataprivacy{{/str}}</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"title": "Data request modal title"
|
||||
}
|
||||
}}
|
||||
{{< core/modal }}
|
||||
|
||||
@@ -26,72 +26,32 @@
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* requests - Array of data requests.
|
||||
* newdatarequesturl string The URL pointing to the data request creation page.
|
||||
* datarequests string The HTML of the data requests table.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"requests": [
|
||||
{
|
||||
"id": 1,
|
||||
"foruser" : {
|
||||
"fullname": "Oscar Olsen",
|
||||
"profileurl": "#"
|
||||
"newdatarequesturl": "#",
|
||||
"datarequests": "<table><tr><td>This is the table where the list of data requests will be rendered</td></tr></table>",
|
||||
"filter": {
|
||||
"action": "#",
|
||||
"filteroptions": [
|
||||
{
|
||||
"value": "1",
|
||||
"label": "Option 1"
|
||||
},
|
||||
"typenameshort" : "Export",
|
||||
"comments": "I would like to download all of my daughter's personal data",
|
||||
"statuslabelclass": "label-default",
|
||||
"statuslabel": "Pending",
|
||||
"timecreated" : 1517902435
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"foruser" : {
|
||||
"fullname": "Alexandre Denys",
|
||||
"profileurl": "#"
|
||||
{
|
||||
"value": "2",
|
||||
"label": "Option 2",
|
||||
"selected": true
|
||||
},
|
||||
"typenameshort" : "Export",
|
||||
"comments": "Please give me all of the information you have about me...",
|
||||
"statuslabelclass": "label-warning",
|
||||
"statuslabel": "Awaiting completion",
|
||||
"timecreated" : 1517902435
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"foruser" : {
|
||||
"fullname": "Hirondino Moura",
|
||||
"profileurl": "#"
|
||||
},
|
||||
"typenameshort" : "Delete",
|
||||
"comments": "Please delete all of my son's personal data.",
|
||||
"statuslabelclass": "label-success",
|
||||
"statuslabel": "Complete",
|
||||
"timecreated" : 1517902435
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"foruser" : {
|
||||
"fullname": "Florian Krause",
|
||||
"profileurl": "#"
|
||||
},
|
||||
"typenameshort" : "Delete",
|
||||
"comments": "I would like to request for my personal data to be deleted from your site. Thanks!",
|
||||
"statuslabelclass": "label-danger",
|
||||
"statuslabel": "Rejected",
|
||||
"timecreated" : 1517902435
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"foruser" : {
|
||||
"fullname": "Nicklas Sørensen",
|
||||
"profileurl": "#"
|
||||
},
|
||||
"typenameshort" : "Export",
|
||||
"comments": "Please let me download my data",
|
||||
"statuslabelclass": "label-info",
|
||||
"statuslabel": "Processing",
|
||||
"timecreated" : 1517902435
|
||||
}
|
||||
]
|
||||
{
|
||||
"value": "3",
|
||||
"label": "Option 3",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -101,51 +61,19 @@
|
||||
|
||||
<div data-region="datarequests">
|
||||
<div class="m-t-1 m-b-1">
|
||||
<a href="{{newdatarequesturl}}" class="btn btn-primary" data-action="new-request">
|
||||
{{#str}}newrequest, tool_dataprivacy{{/str}}
|
||||
</a>
|
||||
<div class="pull-right">
|
||||
<a href="{{newdatarequesturl}}" class="btn btn-primary" data-action="new-request">
|
||||
{{#str}}newrequest, tool_dataprivacy{{/str}}
|
||||
</a>
|
||||
</div>
|
||||
{{#filter}}
|
||||
{{>tool_dataprivacy/request_filter}}
|
||||
{{/filter}}
|
||||
</div>
|
||||
|
||||
<div class="m-t-1 m-b-1" data-region="data-requests-table">
|
||||
{{{datarequests}}}
|
||||
</div>
|
||||
<table class="generaltable fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{#str}}requesttype, tool_dataprivacy{{/str}}</th>
|
||||
<th scope="col">{{#str}}user, tool_dataprivacy{{/str}}</th>
|
||||
<th scope="col">{{#str}}daterequested, tool_dataprivacy{{/str}}</th>
|
||||
<th scope="col">{{#str}}requeststatus, tool_dataprivacy{{/str}}</th>
|
||||
<th scope="col" colspan="2">{{#str}}message, tool_dataprivacy{{/str}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#requests}}
|
||||
<tr {{!
|
||||
}} data-region="request-node"{{!
|
||||
}} data-id="{{id}}"{{!
|
||||
}} data-type="{{type}}"{{!
|
||||
}} data-status="{{status}}"{{!
|
||||
}}>
|
||||
<td>{{typenameshort}}</td>
|
||||
<td><a href="{{foruser.profileurl}}" title="{{#str}}viewprofile{{/str}}">{{foruser.fullname}}</a></td>
|
||||
<td>{{#userdate}} {{timecreated}}, {{#str}} strftimedate {{/str}} {{/userdate}}</td>
|
||||
<td>
|
||||
<span class="label {{statuslabelclass}}">{{statuslabel}}</span>
|
||||
</td>
|
||||
<td>{{#shortentext}}60, {{comments}}{{/shortentext}}</td>
|
||||
<td>
|
||||
{{#actions}}
|
||||
{{> core/action_menu}}
|
||||
{{/actions}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/requests}}
|
||||
{{^requests}}
|
||||
<tr>
|
||||
<td class="text-muted" colspan="6">
|
||||
{{#str}}nodatarequests, tool_dataprivacy{{/str}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/requests}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{#js}}
|
||||
|
||||
@@ -37,7 +37,11 @@
|
||||
"comments": "I would like to download all of my daughter's personal data",
|
||||
"statuslabelclass": "label-default",
|
||||
"statuslabel": "Pending",
|
||||
"timecreated" : 1517902087
|
||||
"timecreated" : 1517902087,
|
||||
"requestedbyuser" : {
|
||||
"fullname": "Martha Smith",
|
||||
"profileurl": "#"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
@@ -45,7 +49,11 @@
|
||||
"comments": "Give me all of the information you have about me, or else...",
|
||||
"statuslabelclass": "label-warning",
|
||||
"statuslabel": "Awaiting completion",
|
||||
"timecreated" : 1517902087
|
||||
"timecreated" : 1517902087,
|
||||
"requestedbyuser" : {
|
||||
"fullname": "Martha Smith",
|
||||
"profileurl": "#"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
@@ -53,7 +61,11 @@
|
||||
"comments": "Please delete all of my son's personal data.",
|
||||
"statuslabelclass": "label-success",
|
||||
"statuslabel": "Complete",
|
||||
"timecreated" : 1517902087
|
||||
"timecreated" : 1517902087,
|
||||
"requestedbyuser" : {
|
||||
"fullname": "Martha Smith",
|
||||
"profileurl": "#"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
@@ -61,7 +73,11 @@
|
||||
"comments": "Delete my data or I'm coming for you...",
|
||||
"statuslabelclass": "label-danger",
|
||||
"statuslabel": "Rejected",
|
||||
"timecreated" : 1517902087
|
||||
"timecreated" : 1517902087,
|
||||
"requestedbyuser" : {
|
||||
"fullname": "Martha Smith",
|
||||
"profileurl": "#"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
@@ -69,7 +85,11 @@
|
||||
"comments": "Please let me download my data",
|
||||
"statuslabelclass": "label-info",
|
||||
"statuslabel": "Processing",
|
||||
"timecreated" : 1517902087
|
||||
"timecreated" : 1517902087,
|
||||
"requestedbyuser" : {
|
||||
"fullname": "Martha Smith",
|
||||
"profileurl": "#"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -90,6 +110,7 @@
|
||||
<tr>
|
||||
<th scope="col">{{#str}}requesttype, tool_dataprivacy{{/str}}</th>
|
||||
<th scope="col">{{#str}}daterequested, tool_dataprivacy{{/str}}</th>
|
||||
<th scope="col">{{#str}}requestby, tool_dataprivacy{{/str}}</th>
|
||||
<th scope="col">{{#str}}requeststatus, tool_dataprivacy{{/str}}</th>
|
||||
<th scope="col" colspan="2">{{#str}}message, tool_dataprivacy{{/str}}</th>
|
||||
</tr>
|
||||
@@ -103,7 +124,8 @@
|
||||
}} data-status="{{status}}"{{!
|
||||
}}>
|
||||
<td>{{typename}}</td>
|
||||
<td>{{#userdate}} {{timecreated}}, {{#str}} strftimedate {{/str}} {{/userdate}}</td>
|
||||
<td>{{#userdate}} {{timecreated}}, {{#str}} strftimedatetime {{/str}} {{/userdate}}</td>
|
||||
<td><a href="{{requestedbyuser.profileurl}}" title="{{#str}}viewprofile{{/str}}">{{requestedbyuser.fullname}}</a></td>
|
||||
<td>
|
||||
<span class="label {{statuslabelclass}}">{{statuslabel}}</span>
|
||||
</td>
|
||||
|
||||
@@ -44,6 +44,10 @@
|
||||
"canreview": true,
|
||||
"reviewurl": "#",
|
||||
"timecreated": 1517561224,
|
||||
"requestedbyuser" : {
|
||||
"fullname": "Martha Smith",
|
||||
"profileurl": "#"
|
||||
},
|
||||
"statuslabel": "Pending",
|
||||
"statuslabelclass": "label-default",
|
||||
"messagehtml": "<p>Hello,</p><p>I would like to download all of my personal data.</p><p>Thanks!</p>"
|
||||
@@ -63,12 +67,16 @@
|
||||
<a href="mailto:{{foruser.email}}">{{foruser.email}}</a>
|
||||
<div class="clearfix m-t-1 m-b-1">
|
||||
<span class="pull-left m-r-1">
|
||||
<strong>{{#str}}daterequesteddetail, tool_dataprivacy{{/str}}</strong> {{#userdate}} {{timecreated}}, {{#str}} strftimedate {{/str}} {{/userdate}}
|
||||
<strong>{{#str}}daterequesteddetail, tool_dataprivacy{{/str}}</strong> {{#userdate}} {{timecreated}}, {{#str}} strftimedatetime {{/str}} {{/userdate}}
|
||||
</span>
|
||||
<span class="pull-left m-r-1">
|
||||
<strong>{{#str}}statusdetail, tool_dataprivacy{{/str}}</strong>
|
||||
<span class="label {{statuslabelclass}}">{{statuslabel}}</span>
|
||||
</span>
|
||||
<span class="pull-left m-r-1">
|
||||
<strong>{{#str}}requestbydetail, tool_dataprivacy{{/str}}</strong>
|
||||
<span><a href="{{requestedbyuser.profileurl}}" title="{{#str}}viewprofile{{/str}}">{{requestedbyuser.fullname}}</a></span>
|
||||
</span>
|
||||
</div>
|
||||
{{#canreview}}
|
||||
<!--a href="{{reviewurl}}" class="btn btn-default">{{#str}}reviewdata, tool_dataprivacy{{/str}}</a-->
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template tool_dataprivacy/request_filter
|
||||
|
||||
Template for the request filter element.
|
||||
|
||||
Context variables required for this template:
|
||||
* action string - The action URL for the form.
|
||||
* filteroptions - Array of filter options.
|
||||
* value string - The option value.
|
||||
* label string - The option label.
|
||||
* selected boolean - Whether the option is selected
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"action": "#",
|
||||
"filteroptions": [
|
||||
{
|
||||
"value": "1",
|
||||
"label": "Option 1"
|
||||
},
|
||||
{
|
||||
"value": "2",
|
||||
"label": "Option 2",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "3",
|
||||
"label": "Option 3",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "4",
|
||||
"label": "Option 4"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
<form method="post" action="{{action}}" class="m-b-1" role="search" id="request_filter_form">
|
||||
<label for="request-filters" class="sr-only">{{#str}}filters{{/str}}</label>
|
||||
<select name="request-filters[]" id="request-filters" multiple="multiple" class="form-autocomplete-original-select">
|
||||
{{#filteroptions}}
|
||||
<option value="{{value}}" {{#selected}}selected="selected"{{/selected}}>{{{label}}}</option>
|
||||
{{/filteroptions}}
|
||||
</select>
|
||||
<input type="hidden" id="filters-cleared" name="filters-cleared" value="0" />
|
||||
</form>
|
||||
{{#js}}
|
||||
require(['tool_dataprivacy/request_filter'], function(Filter) {
|
||||
Filter.init();
|
||||
});
|
||||
{{/js}}
|
||||
@@ -29,6 +29,7 @@ use tool_dataprivacy\api;
|
||||
use tool_dataprivacy\data_registry;
|
||||
use tool_dataprivacy\expired_context;
|
||||
use tool_dataprivacy\data_request;
|
||||
use tool_dataprivacy\local\helper;
|
||||
use tool_dataprivacy\task\initiate_data_request_task;
|
||||
use tool_dataprivacy\task\process_data_request_task;
|
||||
|
||||
@@ -411,42 +412,128 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for api::get_data_requests()
|
||||
* Data provider for \tool_dataprivacy_api_testcase::test_get_data_requests().
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_get_data_requests() {
|
||||
public function get_data_requests_provider() {
|
||||
$generator = new testing_data_generator();
|
||||
$user1 = $generator->create_user();
|
||||
$user2 = $generator->create_user();
|
||||
$comment = 'sample comment';
|
||||
$user3 = $generator->create_user();
|
||||
$user4 = $generator->create_user();
|
||||
$user5 = $generator->create_user();
|
||||
$users = [$user1, $user2, $user3, $user4, $user5];
|
||||
$completeonly = [api::DATAREQUEST_STATUS_COMPLETE];
|
||||
$completeandcancelled = [api::DATAREQUEST_STATUS_COMPLETE, api::DATAREQUEST_STATUS_CANCELLED];
|
||||
|
||||
// Make a data request as user 1.
|
||||
$this->setUser($user1);
|
||||
$d1 = api::create_data_request($user1->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
|
||||
// Make a data request as user 2.
|
||||
$this->setUser($user2);
|
||||
$d2 = api::create_data_request($user2->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
|
||||
return [
|
||||
// Own data requests.
|
||||
[$users, $user1, false, $completeonly],
|
||||
// Non-DPO fetching all requets.
|
||||
[$users, $user2, true, $completeonly],
|
||||
// Admin fetching all completed and cancelled requests.
|
||||
[$users, get_admin(), true, $completeandcancelled],
|
||||
// Admin fetching all completed requests.
|
||||
[$users, get_admin(), true, $completeonly],
|
||||
// Guest fetching all requests.
|
||||
[$users, guest_user(), true, $completeonly],
|
||||
];
|
||||
}
|
||||
|
||||
// Fetching data requests of specific users.
|
||||
$requests = api::get_data_requests($user1->id);
|
||||
$this->assertCount(1, $requests);
|
||||
$datarequest = reset($requests);
|
||||
$this->assertEquals($d1->to_record(), $datarequest->to_record());
|
||||
/**
|
||||
* Test for api::get_data_requests()
|
||||
*
|
||||
* @dataProvider get_data_requests_provider
|
||||
* @param stdClass[] $users Array of users to create data requests for.
|
||||
* @param stdClass $loggeduser The user logging in.
|
||||
* @param boolean $fetchall Whether to fetch all records.
|
||||
* @param int[] $statuses Status filters.
|
||||
*/
|
||||
public function test_get_data_requests($users, $loggeduser, $fetchall, $statuses) {
|
||||
$comment = 'Data %s request comment by user %d';
|
||||
$exportstring = helper::get_shortened_request_type_string(api::DATAREQUEST_TYPE_EXPORT);
|
||||
$deletionstring = helper::get_shortened_request_type_string(api::DATAREQUEST_TYPE_DELETE);
|
||||
// Make a data requests for the users.
|
||||
foreach ($users as $user) {
|
||||
$this->setUser($user);
|
||||
api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, sprintf($comment, $exportstring, $user->id));
|
||||
api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, sprintf($comment, $deletionstring, $user->id));
|
||||
}
|
||||
|
||||
$requests = api::get_data_requests($user2->id);
|
||||
$this->assertCount(1, $requests);
|
||||
$datarequest = reset($requests);
|
||||
$this->assertEquals($d2->to_record(), $datarequest->to_record());
|
||||
// Log in as the target user.
|
||||
$this->setUser($loggeduser);
|
||||
// Get records count based on the filters.
|
||||
$userid = $loggeduser->id;
|
||||
if ($fetchall) {
|
||||
$userid = 0;
|
||||
}
|
||||
$count = api::get_data_requests_count($userid);
|
||||
if (api::is_site_dpo($loggeduser->id)) {
|
||||
// DPOs should see all the requests.
|
||||
$this->assertEquals(count($users) * 2, $count);
|
||||
} else {
|
||||
if (empty($userid)) {
|
||||
// There should be no data requests for this user available.
|
||||
$this->assertEquals(0, $count);
|
||||
} else {
|
||||
// There should be only one (request with pending status).
|
||||
$this->assertEquals(2, $count);
|
||||
}
|
||||
}
|
||||
// Get data requests.
|
||||
$requests = api::get_data_requests($userid);
|
||||
// The number of requests should match the count.
|
||||
$this->assertCount($count, $requests);
|
||||
|
||||
// Fetching data requests of all users.
|
||||
// As guest.
|
||||
$this->setGuestUser();
|
||||
$requests = api::get_data_requests();
|
||||
$this->assertEmpty($requests);
|
||||
// Test filtering by status.
|
||||
if ($count && !empty($statuses)) {
|
||||
$filteredcount = api::get_data_requests_count($userid, $statuses);
|
||||
// There should be none as they are all pending.
|
||||
$this->assertEquals(0, $filteredcount);
|
||||
$filteredrequests = api::get_data_requests($userid, $statuses);
|
||||
$this->assertCount($filteredcount, $filteredrequests);
|
||||
|
||||
// As DPO (admin in this case, which is default if no site DPOs are set).
|
||||
$this->setAdminUser();
|
||||
$requests = api::get_data_requests();
|
||||
$this->assertCount(2, $requests);
|
||||
$statuscounts = [];
|
||||
foreach ($statuses as $stat) {
|
||||
$statuscounts[$stat] = 0;
|
||||
}
|
||||
$numstatus = count($statuses);
|
||||
// Get all requests with status filter and update statuses, randomly.
|
||||
foreach ($requests as $request) {
|
||||
if (rand(0, 1)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($numstatus > 1) {
|
||||
$index = rand(0, $numstatus - 1);
|
||||
$status = $statuses[$index];
|
||||
} else {
|
||||
$status = reset($statuses);
|
||||
}
|
||||
$statuscounts[$status]++;
|
||||
api::update_request_status($request->get('id'), $status);
|
||||
}
|
||||
$total = array_sum($statuscounts);
|
||||
$filteredcount = api::get_data_requests_count($userid, $statuses);
|
||||
$this->assertEquals($total, $filteredcount);
|
||||
$filteredrequests = api::get_data_requests($userid, $statuses);
|
||||
$this->assertCount($filteredcount, $filteredrequests);
|
||||
// Confirm the filtered requests match the status filter(s).
|
||||
foreach ($filteredrequests as $request) {
|
||||
$this->assertContains($request->get('status'), $statuses);
|
||||
}
|
||||
|
||||
if ($numstatus > 1) {
|
||||
// Fetch by individual status to check the numbers match.
|
||||
foreach ($statuses as $status) {
|
||||
$filteredcount = api::get_data_requests_count($userid, [$status]);
|
||||
$this->assertEquals($statuscounts[$status], $filteredcount);
|
||||
$filteredrequests = api::get_data_requests($userid, [$status]);
|
||||
$this->assertCount($filteredcount, $filteredrequests);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -142,7 +142,7 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
global $DB;
|
||||
|
||||
$purpose1 = api::create_purpose((object)['name' => 'p1', 'retentionperiod' => 'PT1H', 'lawfulbases' => 'gdpr_art_6_1_a']);
|
||||
$purpose2 = api::create_purpose((object)['name' => 'p1', 'retentionperiod' => 'P1000Y', 'lawfulbases' => 'gdpr_art_6_1_b']);
|
||||
$purpose2 = api::create_purpose((object)['name' => 'p1', 'retentionperiod' => 'P1Y', 'lawfulbases' => 'gdpr_art_6_1_b']);
|
||||
$cat = api::create_category((object)['name' => 'a']);
|
||||
|
||||
$record = (object)[
|
||||
@@ -165,8 +165,12 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
|
||||
// Old course.
|
||||
$course2 = $this->getDataGenerator()->create_course(['startdate' => '1', 'enddate' => '2']);
|
||||
// Course finished last week (so purpose1 retention period does delete stuff but purpose2 retention period does not).
|
||||
$dt = new \DateTime();
|
||||
$di = new \DateInterval('P7D');
|
||||
$dt->sub($di);
|
||||
|
||||
$course2 = $this->getDataGenerator()->create_course(['startdate' => '1', 'enddate' => $dt->getTimestamp()]);
|
||||
$forum1 = $this->getDataGenerator()->create_module('forum', array('course' => $course2->id));
|
||||
$forum2 = $this->getDataGenerator()->create_module('forum', array('course' => $course2->id));
|
||||
|
||||
|
||||
@@ -99,8 +99,8 @@ class helper {
|
||||
'name' => $name,
|
||||
'description' => $description,
|
||||
'timecreated' => transform::datetime($record->timecreated),
|
||||
'ip' => $record->ip,
|
||||
'origin' => static::transform_origin($record->origin),
|
||||
'ip' => $isauthor ? $record->ip : '',
|
||||
'other' => $other ? $other : []
|
||||
];
|
||||
|
||||
|
||||
@@ -74,7 +74,9 @@ class provider implements
|
||||
* @param approved_contextlist $contextlist The approved contexts to export information for.
|
||||
*/
|
||||
public static function export_user_data(approved_contextlist $contextlist) {
|
||||
static::call_subplugins_method_with_args('export_user_data', [$contextlist]);
|
||||
if (get_config('tool_log', 'exportlog')) {
|
||||
static::call_subplugins_method_with_args('export_user_data', [$contextlist]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,5 +42,8 @@ function xmldb_tool_log_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
$string['actlogshdr'] = 'Available log stores';
|
||||
$string['configlogplugins'] = 'Please enable all required plugins and arrange them in appropriate order.';
|
||||
$string['exportlog'] = 'Include logs when exporting.';
|
||||
$string['exportlogdetail'] = 'Include logs that relate to the user when exporting.';
|
||||
$string['logging'] = 'Logging';
|
||||
$string['managelogging'] = 'Manage log stores';
|
||||
$string['pluginname'] = 'Log store manager';
|
||||
|
||||
@@ -25,6 +25,16 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
if ($hassiteconfig) {
|
||||
|
||||
$privacysettings = $ADMIN->locate('privacysettings');
|
||||
|
||||
if ($ADMIN->fulltree) {
|
||||
$privacysettings->add(new admin_setting_configcheckbox('tool_log/exportlog',
|
||||
new lang_string('exportlog', 'tool_log'),
|
||||
new lang_string('exportlogdetail', 'tool_log'), 1)
|
||||
);
|
||||
}
|
||||
|
||||
$ADMIN->add('modules', new admin_category('logging', new lang_string('logging', 'tool_log')));
|
||||
|
||||
$temp = new admin_settingpage('managelogging', new lang_string('managelogging', 'tool_log'));
|
||||
|
||||
@@ -36,5 +36,8 @@ function xmldb_logstore_database_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -36,5 +36,8 @@ function xmldb_logstore_standard_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2018051401; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2018050800; // Requires this Moodle version.
|
||||
$plugin->component = 'tool_log'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -343,7 +343,8 @@ class api {
|
||||
$availablemods = core_plugin_manager::instance()->get_plugins_of_type('mod');
|
||||
$coursemodules = array();
|
||||
$appsupportedmodules = array('assign', 'book', 'chat', 'choice', 'data', 'feedback', 'folder', 'forum', 'glossary', 'imscp',
|
||||
'label', 'lesson', 'lti', 'page', 'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki');
|
||||
'label', 'lesson', 'lti', 'page', 'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop');
|
||||
|
||||
foreach ($availablemods as $mod) {
|
||||
if (in_array($mod->name, $appsupportedmodules)) {
|
||||
$coursemodules['$mmCourseDelegate_mmaMod' . ucfirst($mod->name)] = $mod->displayname;
|
||||
|
||||
@@ -76,5 +76,8 @@ function xmldb_tool_monitor_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,9 @@ class page_agreedocs implements renderable, templatable {
|
||||
/** @var array Info or error messages to show. */
|
||||
protected $messages = [];
|
||||
|
||||
/** @var bool This is an existing user (rather than non-loggedin/guest). */
|
||||
protected $isexistinguser;
|
||||
|
||||
/**
|
||||
* Prepare the page for rendering.
|
||||
*
|
||||
@@ -87,6 +90,7 @@ class page_agreedocs implements renderable, templatable {
|
||||
|
||||
$this->action = $action;
|
||||
|
||||
$this->isexistinguser = isloggedin() && !isguestuser();
|
||||
$behalfid = $behalfid ?: $USER->id;
|
||||
if ($realuser->id != $behalfid) {
|
||||
$this->behalfuser = core_user::get_user($behalfid, '*', MUST_EXIST);
|
||||
@@ -112,7 +116,7 @@ class page_agreedocs implements renderable, templatable {
|
||||
protected function accept_and_revoke_policies() {
|
||||
global $USER;
|
||||
|
||||
if (!empty($USER->id)) {
|
||||
if ($this->isexistinguser) {
|
||||
// Existing user.
|
||||
if (!empty($this->action) && confirm_sesskey()) {
|
||||
// The form has been sent. Update policies acceptances according to $this->agreedocs.
|
||||
@@ -182,15 +186,13 @@ class page_agreedocs implements renderable, templatable {
|
||||
* Before display the consent page, the user has to view all the still-non-accepted policy docs.
|
||||
* This function checks if the non-accepted policy docs have been shown and redirect to them.
|
||||
*
|
||||
* @param array $userid User identifier who wants to access to the consent page.
|
||||
* @param url $returnurl URL to return after shown the policy docs.
|
||||
* @param int $userid User identifier who wants to access to the consent page.
|
||||
* @param moodle_url $returnurl URL to return after shown the policy docs.
|
||||
*/
|
||||
protected function redirect_to_policies($userid, $returnurl = null) {
|
||||
global $USER;
|
||||
|
||||
$acceptances = api::get_user_acceptances($userid);
|
||||
$allpolicies = $this->policies;
|
||||
if (!empty($userid)) {
|
||||
if ($this->isexistinguser) {
|
||||
$acceptances = api::get_user_acceptances($userid);
|
||||
foreach ($allpolicies as $policy) {
|
||||
if (api::is_user_version_accepted($userid, $policy->id, $acceptances)) {
|
||||
// If this version is accepted by the user, remove from the pending policies list.
|
||||
@@ -234,16 +236,30 @@ class page_agreedocs implements renderable, templatable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to $SESSION->wantsurl if defined or to $CFG->wwwroot if not.
|
||||
* Redirect to signup page if defined or to $CFG->wwwroot if not.
|
||||
*/
|
||||
protected function redirect_to_previous_url() {
|
||||
global $SESSION;
|
||||
|
||||
if (!empty($SESSION->wantsurl)) {
|
||||
$returnurl = $SESSION->wantsurl;
|
||||
unset($SESSION->wantsurl);
|
||||
if ($this->isexistinguser) {
|
||||
// Existing user.
|
||||
if (!empty($SESSION->wantsurl)) {
|
||||
$returnurl = $SESSION->wantsurl;
|
||||
unset($SESSION->wantsurl);
|
||||
} else {
|
||||
$returnurl = new moodle_url('/admin/tool/policy/user.php');
|
||||
}
|
||||
} else {
|
||||
$returnurl = (new moodle_url('/admin/tool/policy/user.php'))->out();
|
||||
// Non-authenticated user.
|
||||
$issignup = \cache::make('core', 'presignup')->get('tool_policy_issignup');
|
||||
if ($issignup) {
|
||||
// User came here from signup page - redirect back there.
|
||||
$returnurl = new moodle_url('/login/signup.php');
|
||||
\cache::make('core', 'presignup')->set('tool_policy_issignup', false);
|
||||
} else {
|
||||
// Guests should not be on this page unless it's part of signup - redirect home.
|
||||
$returnurl = new moodle_url('/');
|
||||
}
|
||||
}
|
||||
|
||||
redirect($returnurl);
|
||||
@@ -255,35 +271,35 @@ class page_agreedocs implements renderable, templatable {
|
||||
* @param int $userid
|
||||
*/
|
||||
protected function prepare_global_page_access($userid) {
|
||||
global $PAGE, $SESSION, $SITE, $USER;
|
||||
global $PAGE, $SITE, $USER;
|
||||
|
||||
// Guest users or not logged users (but the users during the signup process) are not allowed to access to this page.
|
||||
$newsignupuser = !empty($SESSION->wantsurl) && strpos($SESSION->wantsurl, 'login/signup.php') !== false;
|
||||
if (isguestuser() || (empty($USER->id) && !$newsignupuser)) {
|
||||
$newsignupuser = \cache::make('core', 'presignup')->get('tool_policy_issignup');
|
||||
if (!$this->isexistinguser && !$newsignupuser) {
|
||||
$this->redirect_to_previous_url();
|
||||
}
|
||||
|
||||
// Check for correct user capabilities.
|
||||
if (!empty($USER->id)) {
|
||||
if ($this->isexistinguser) {
|
||||
// For existing users, it's needed to check if they have the capability for accepting policies.
|
||||
api::can_accept_policies($this->behalfid, true);
|
||||
} else {
|
||||
// For new users, the behalfid parameter is ignored.
|
||||
if ($this->behalfid != $USER->id) {
|
||||
if ($this->behalfid) {
|
||||
redirect(new moodle_url('/admin/tool/policy/index.php'));
|
||||
}
|
||||
}
|
||||
|
||||
// If the current user has the $USER->policyagreed = 1 or $userpolicyagreed = 1
|
||||
// and $SESSION->wantsurl is defined, redirect to the return page.
|
||||
$hasagreedsignupuser = empty($USER->id) && $this->signupuserpolicyagreed;
|
||||
// redirect to the return page.
|
||||
$hasagreedsignupuser = !$this->isexistinguser && $this->signupuserpolicyagreed;
|
||||
$hasagreedloggeduser = $USER->id == $userid && !empty($USER->policyagreed);
|
||||
if (!is_siteadmin() && ($hasagreedsignupuser || $hasagreedloggeduser)) {
|
||||
$this->redirect_to_previous_url();
|
||||
}
|
||||
|
||||
$myparams = [];
|
||||
if (!empty($USER->id) && !empty($this->behalfid) && $this->behalfid != $USER->id) {
|
||||
if ($this->isexistinguser && !empty($this->behalfid) && $this->behalfid != $USER->id) {
|
||||
$myparams['userid'] = $this->behalfid;
|
||||
}
|
||||
$myurl = new moodle_url('/admin/tool/policy/index.php', $myparams);
|
||||
@@ -308,7 +324,6 @@ class page_agreedocs implements renderable, templatable {
|
||||
global $USER;
|
||||
|
||||
// Get all the policy version acceptances for this user.
|
||||
$acceptances = api::get_user_acceptances($userid);
|
||||
$lang = current_language();
|
||||
foreach ($this->policies as $policy) {
|
||||
// Get a link to display the full policy document.
|
||||
@@ -320,9 +335,10 @@ class page_agreedocs implements renderable, templatable {
|
||||
$policymodal = html_writer::link($policy->url, $policy->name, $policyattributes);
|
||||
|
||||
// Check if this policy version has been agreed or not.
|
||||
if (!empty($userid)) {
|
||||
if ($this->isexistinguser) {
|
||||
// Existing user.
|
||||
$versionagreed = false;
|
||||
$acceptances = api::get_user_acceptances($userid);
|
||||
$policy->versionacceptance = api::get_user_version_acceptance($userid, $policy->id, $acceptances);
|
||||
if (!empty($policy->versionacceptance)) {
|
||||
// The policy version has ever been agreed. Check if status = 1 to know if still is accepted.
|
||||
@@ -352,13 +368,13 @@ class page_agreedocs implements renderable, templatable {
|
||||
* Export the page data for the mustache template.
|
||||
*
|
||||
* @param renderer_base $output renderer to be used to render the page elements.
|
||||
* @return stdClass
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
global $USER;
|
||||
|
||||
$myparams = [];
|
||||
if (!empty($USER->id) && !empty($this->behalfid) && $this->behalfid != $USER->id) {
|
||||
if ($this->isexistinguser && !empty($this->behalfid) && $this->behalfid != $USER->id) {
|
||||
$myparams['userid'] = $this->behalfid;
|
||||
}
|
||||
$data = (object) [
|
||||
|
||||
@@ -63,14 +63,7 @@ class page_viewalldoc implements renderable, templatable {
|
||||
*
|
||||
*/
|
||||
protected function prepare_policies() {
|
||||
global $USER;
|
||||
|
||||
if (isguestuser() || empty($USER->id)) {
|
||||
$audience = policy_version::AUDIENCE_GUESTS;
|
||||
} else {
|
||||
$audience = policy_version::AUDIENCE_LOGGEDIN;
|
||||
}
|
||||
$this->policies = api::list_current_versions($audience);
|
||||
$this->policies = api::list_current_versions();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,7 +45,7 @@ $PAGE->set_pagelayout('standard');
|
||||
$PAGE->set_url('/admin/tool/policy/index.php');
|
||||
$PAGE->set_popup_notification_allowed(false);
|
||||
|
||||
if (!empty($USER->id)) {
|
||||
if (isloggedin() && !isguestuser()) {
|
||||
// Existing user.
|
||||
$haspermissionagreedocs = api::can_accept_policies($behalfid);
|
||||
} else {
|
||||
|
||||
@@ -54,7 +54,7 @@ $string['backtotop'] = 'Back to top';
|
||||
$string['consentbulk'] = 'Consent';
|
||||
$string['consentdetails'] = 'Give consent on behalf of user';
|
||||
$string['consentpagetitle'] = 'Consent';
|
||||
$string['contactdpo'] = 'For questions regarding the policies please contact the Data Protection Officer.';
|
||||
$string['contactdpo'] = 'For any questions about the policies please contact the privacy officer.';
|
||||
$string['dataproc'] = 'Personal data processing';
|
||||
$string['deleting'] = 'Deleting a version';
|
||||
$string['deleteconfirm'] = '<p>Are you sure you want to delete policy <em>\'{$a->name}\'</em>?</p><p>This operation can not be undone.</p>';
|
||||
@@ -92,6 +92,7 @@ $string['moveup'] = 'Move up';
|
||||
$string['mustagreetocontinue'] = 'Before continuing you must agree to all these policies.';
|
||||
$string['newpolicy'] = 'New policy';
|
||||
$string['newversion'] = 'New version';
|
||||
$string['noactivepolicies'] = 'There are no policies with an active version.';
|
||||
$string['nofiltersapplied'] = 'No filters applied';
|
||||
$string['nopermissiontoagreedocs'] = 'No permission to agree to the policies';
|
||||
$string['nopermissiontoagreedocs_desc'] = 'Sorry, you do not have the required permissions to agree to the policies.<br />You will not be able to use this site until the following policies are agreed:';
|
||||
|
||||
@@ -83,7 +83,7 @@ function tool_policy_before_standard_html_head() {
|
||||
if (!empty($CFG->sitepolicyhandler)
|
||||
&& $CFG->sitepolicyhandler == 'tool_policy'
|
||||
&& empty($USER->policyagreed)
|
||||
&& isguestuser()) {
|
||||
&& (isguestuser() || !isloggedin())) {
|
||||
$output = $PAGE->get_renderer('tool_policy');
|
||||
$page = new \tool_policy\output\guestconsent();
|
||||
|
||||
@@ -104,9 +104,12 @@ function tool_policy_standard_footer_html() {
|
||||
$output = '';
|
||||
if (!empty($CFG->sitepolicyhandler)
|
||||
&& $CFG->sitepolicyhandler == 'tool_policy') {
|
||||
$url = (new moodle_url('/admin/tool/policy/viewall.php'))->out();
|
||||
$output .= html_writer::link($url, get_string('userpolicysettings', 'tool_policy'));
|
||||
$output = html_writer::div($output, 'policiesfooter');
|
||||
$policies = api::get_current_versions_ids();
|
||||
if (!empty($policies)) {
|
||||
$url = (new moodle_url('/admin/tool/policy/viewall.php'))->out();
|
||||
$output .= html_writer::link($url, get_string('userpolicysettings', 'tool_policy'));
|
||||
$output = html_writer::div($output, 'policiesfooter');
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
@@ -116,7 +119,7 @@ function tool_policy_standard_footer_html() {
|
||||
* Hooks redirection to policy acceptance pages before sign up.
|
||||
*/
|
||||
function tool_policy_pre_signup_requests() {
|
||||
global $CFG, $SESSION;
|
||||
global $CFG;
|
||||
|
||||
// Do nothing if we are not set as the site policies handler.
|
||||
if (empty($CFG->sitepolicyhandler) || $CFG->sitepolicyhandler !== 'tool_policy') {
|
||||
@@ -127,7 +130,7 @@ function tool_policy_pre_signup_requests() {
|
||||
$userpolicyagreed = cache::make('core', 'presignup')->get('tool_policy_userpolicyagreed');
|
||||
if (!empty($policies) && !$userpolicyagreed) {
|
||||
// Redirect to "Policy" pages for consenting before creating the user.
|
||||
$SESSION->wantsurl = (new \moodle_url('/login/signup.php'))->out();
|
||||
cache::make('core', 'presignup')->set('tool_policy_issignup', 1);
|
||||
redirect(new \moodle_url('/admin/tool/policy/index.php'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,10 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{{^policies }}
|
||||
{{# str }} noactivepolicies, tool_policy {{/ str }}
|
||||
{{/policies }}
|
||||
|
||||
{{#policies }}
|
||||
<hr>
|
||||
<div class="policy_version m-b-3">
|
||||
|
||||
@@ -81,12 +81,12 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
And I am on site homepage
|
||||
And I follow "Log in"
|
||||
When I press "Create new account"
|
||||
Then I should see "This site policy"
|
||||
Then I should see "This site policy" in the "region-main" "region"
|
||||
And I should see "short text2"
|
||||
And I should see "full text2"
|
||||
And I press "Next"
|
||||
And I should see "Please agree to the following policies"
|
||||
And I should see "This site policy"
|
||||
And I should see "This site policy" in the "region-main" "region"
|
||||
And I should see "short text2"
|
||||
And I should not see "full text2"
|
||||
And I set the field "I agree to the This site policy" to "1"
|
||||
@@ -129,22 +129,22 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
And I am on site homepage
|
||||
And I follow "Log in"
|
||||
When I press "Create new account"
|
||||
Then I should see "This site policy"
|
||||
Then I should see "This site policy" in the "region-main" "region"
|
||||
And I should see "short text2"
|
||||
And I should see "full text2"
|
||||
And I press "Next"
|
||||
And I should see "This privacy policy"
|
||||
And I should see "This privacy policy" in the "region-main" "region"
|
||||
And I should see "short text3"
|
||||
And I should see "full text3"
|
||||
And I press "Next"
|
||||
And I should see "Please agree to the following policies"
|
||||
And I should see "This site policy"
|
||||
And I should see "This site policy" in the "region-main" "region"
|
||||
And I should see "short text2"
|
||||
And I should not see "full text2"
|
||||
And I should see "This privacy policy"
|
||||
And I should see "This privacy policy" in the "region-main" "region"
|
||||
And I should see "short text3"
|
||||
And I should not see "full text3"
|
||||
And I should not see "This guests policy"
|
||||
And I should not see "This guests policy" in the "region-main" "region"
|
||||
And I should not see "short text4"
|
||||
And I should not see "full text4"
|
||||
And I set the field "I agree to the This site policy" to "1"
|
||||
@@ -495,20 +495,20 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
And I am on site homepage
|
||||
And I follow "Log in"
|
||||
When I press "Create new account"
|
||||
Then I should see "This site policy"
|
||||
Then I should see "This site policy" in the "region-main" "region"
|
||||
And I should see "short text2"
|
||||
And I should see "full text2"
|
||||
When I press "Next"
|
||||
Then I should see "This privacy policy"
|
||||
Then I should see "This privacy policy" in the "region-main" "region"
|
||||
And I should see "short text3"
|
||||
And I should see "full text3"
|
||||
When I press "Next"
|
||||
Then I should see "Please agree to the following policies"
|
||||
And I should see "This site policy"
|
||||
And I should see "This site policy" in the "region-main" "region"
|
||||
And I should see "short text2"
|
||||
And I should see "This privacy policy"
|
||||
And I should see "This privacy policy" in the "region-main" "region"
|
||||
And I should see "short text3"
|
||||
And I should not see "This guests policy"
|
||||
And I should not see "This guests policy" in the "region-main" "region"
|
||||
And I should not see "short text4"
|
||||
And I set the field "I agree to the This site policy" to "1"
|
||||
And I set the field "I agree to the This privacy policy" to "1"
|
||||
@@ -528,22 +528,22 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
And I follow "Log in"
|
||||
When I press "Create new account"
|
||||
# Confirm that the user can view and accept policies when attempting to create another account.
|
||||
Then I should see "This site policy"
|
||||
Then I should see "This site policy" in the "region-main" "region"
|
||||
And I should see "short text2"
|
||||
And I should see "full text2"
|
||||
When I press "Next"
|
||||
Then I should see "This privacy policy"
|
||||
Then I should see "This privacy policy" in the "region-main" "region"
|
||||
And I should see "short text3"
|
||||
And I should see "full text3"
|
||||
When I press "Next"
|
||||
Then I should see "Please agree to the following policies"
|
||||
And I should see "This site policy"
|
||||
And I should see "This site policy" in the "region-main" "region"
|
||||
And I should see "short text2"
|
||||
And I should not see "full text2"
|
||||
And I should see "This privacy policy"
|
||||
And I should see "This privacy policy" in the "region-main" "region"
|
||||
And I should see "short text3"
|
||||
And I should not see "full text3"
|
||||
And I should not see "This guests policy"
|
||||
And I should not see "This guests policy" in the "region-main" "region"
|
||||
And I should not see "short text4"
|
||||
And I should not see "full text4"
|
||||
And I set the field "I agree to the This site policy" to "1"
|
||||
@@ -611,3 +611,55 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
And I should see "Policies and agreements"
|
||||
And I should see "No permission to agree to the policies on behalf of this user"
|
||||
And I should see "Sorry, you do not have the required permission to agree to the following policies on behalf of User 1"
|
||||
|
||||
Scenario: Accept policy on sign up as a guest, one policy
|
||||
Given the following config values are set as admin:
|
||||
| registerauth | email |
|
||||
| passwordpolicy | 0 |
|
||||
| sitepolicyhandler | tool_policy |
|
||||
Given the following policies exist:
|
||||
| Policy | Name | Revision | Content | Summary | Status |
|
||||
| P1 | This site policy | | full text1 | short text1 | archived |
|
||||
| P1 | This site policy | | full text2 | short text2 | active |
|
||||
| P1 | This site policy | | full text3 | short text3 | draft |
|
||||
And I am on site homepage
|
||||
And I follow "Log in"
|
||||
# First log in as a guest
|
||||
And I press "Log in as a guest"
|
||||
# Now sign up
|
||||
And I follow "Log in"
|
||||
When I press "Create new account"
|
||||
Then I should see "This site policy"
|
||||
And I should see "short text2"
|
||||
And I should see "full text2"
|
||||
And I press "Next"
|
||||
And I should see "Please agree to the following policies"
|
||||
And I should see "This site policy"
|
||||
And I should see "short text2"
|
||||
And I should not see "full text2"
|
||||
And I set the field "I agree to the This site policy" to "1"
|
||||
And I press "Next"
|
||||
And I should not see "I understand and agree"
|
||||
And I set the following fields to these values:
|
||||
| Username | user1 |
|
||||
| Password | user1 |
|
||||
| Email address | user1@address.invalid |
|
||||
| Email (again) | user1@address.invalid |
|
||||
| First name | User1 |
|
||||
| Surname | L1 |
|
||||
And I press "Create my new account"
|
||||
And I should see "Confirm your account"
|
||||
And I should see "An email should have been sent to your address at user1@address.invalid"
|
||||
And I confirm email for "user1"
|
||||
And I should see "Thanks, User1 L1"
|
||||
And I should see "Your registration has been confirmed"
|
||||
And I open my profile in edit mode
|
||||
And the field "First name" matches value "User1"
|
||||
And I log out
|
||||
# Confirm that user can login and browse the site.
|
||||
And I log in as "user1"
|
||||
And I follow "Profile" in the user menu
|
||||
# User can see his own agreements in the profile.
|
||||
And I follow "Policies and agreements"
|
||||
And "Agreed" "icon" should exist in the "This site policy" "table_row"
|
||||
And I log out
|
||||
|
||||
@@ -288,13 +288,24 @@ class category_bin extends base_bin {
|
||||
global $DB;
|
||||
|
||||
// Grab the course category context.
|
||||
$context = \context_coursecat::instance($this->_categoryid);
|
||||
|
||||
// Delete the files.
|
||||
$fs = get_file_storage();
|
||||
$files = $fs->get_area_files($context->id, 'tool_recyclebin', TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA, $item->id);
|
||||
foreach ($files as $file) {
|
||||
$file->delete();
|
||||
$context = \context_coursecat::instance($this->_categoryid, IGNORE_MISSING);
|
||||
if (!empty($context)) {
|
||||
// Delete the files.
|
||||
$fs = get_file_storage();
|
||||
$fs->delete_area_files($context->id, 'tool_recyclebin', TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA, $item->id);
|
||||
} else {
|
||||
// Course category has been deleted. Find records using $item->id as this is unique for coursecat recylebin.
|
||||
$files = $DB->get_recordset('files', [
|
||||
'component' => 'tool_recyclebin',
|
||||
'filearea' => TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA,
|
||||
'itemid' => $item->id,
|
||||
]);
|
||||
$fs = get_file_storage();
|
||||
foreach ($files as $filer) {
|
||||
$file = $fs->get_file_instance($filer);
|
||||
$file->delete();
|
||||
}
|
||||
$files->close();
|
||||
}
|
||||
|
||||
// Delete the record.
|
||||
@@ -302,6 +313,11 @@ class category_bin extends base_bin {
|
||||
'id' => $item->id
|
||||
));
|
||||
|
||||
// The coursecat might have been deleted, check we have a context before triggering event.
|
||||
if (!$context) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fire event.
|
||||
$event = \tool_recyclebin\event\category_bin_item_deleted::create(array(
|
||||
'objectid' => $item->id,
|
||||
|
||||
@@ -274,13 +274,25 @@ class course_bin extends base_bin {
|
||||
global $DB;
|
||||
|
||||
// Grab the course context.
|
||||
$context = \context_course::instance($this->_courseid);
|
||||
$context = \context_course::instance($this->_courseid, IGNORE_MISSING);
|
||||
|
||||
// Delete the files.
|
||||
$fs = get_file_storage();
|
||||
$files = $fs->get_area_files($context->id, 'tool_recyclebin', TOOL_RECYCLEBIN_COURSE_BIN_FILEAREA, $item->id);
|
||||
foreach ($files as $file) {
|
||||
$file->delete();
|
||||
if (!empty($context)) {
|
||||
// Delete the files.
|
||||
$fs = get_file_storage();
|
||||
$fs->delete_area_files($context->id, 'tool_recyclebin', TOOL_RECYCLEBIN_COURSE_BIN_FILEAREA, $item->id);
|
||||
} else {
|
||||
// Course context has been deleted. Find records using $item->id as this is unique for course bin recyclebin.
|
||||
$files = $DB->get_recordset('files', [
|
||||
'component' => 'tool_recyclebin',
|
||||
'filearea' => TOOL_RECYCLEBIN_COURSE_BIN_FILEAREA,
|
||||
'itemid' => $item->id,
|
||||
]);
|
||||
$fs = get_file_storage();
|
||||
foreach ($files as $filer) {
|
||||
$file = $fs->get_file_instance($filer);
|
||||
$file->delete();
|
||||
}
|
||||
$files->close();
|
||||
}
|
||||
|
||||
// Delete the record.
|
||||
|
||||
@@ -51,5 +51,8 @@ function xmldb_tool_usertours_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ class edit_table extends XMLDBAction {
|
||||
$o.= ' <input type="hidden" name ="action" value="edit_table_save" />';
|
||||
$o.= ' <input type="hidden" name ="sesskey" value="' . sesskey() .'" />';
|
||||
$o.= ' <input type="hidden" name ="postaction" value="edit_table" />';
|
||||
$o.= ' <table id="formelements" class="boxaligncenter">';
|
||||
$o .= ' <table id="formelements">';
|
||||
// If the table is being used, we cannot rename it
|
||||
if ($structure->getTableUses($table->getName())) {
|
||||
$o.= ' <tr valign="top"><td>Name:</td><td><input type="hidden" name ="name" value="' . s($table->getName()) . '" />' . s($table->getName()) .'</td></tr>';
|
||||
@@ -243,7 +243,7 @@ class edit_table extends XMLDBAction {
|
||||
// The readable info
|
||||
$r = '</td><td class="readableinfo cell">' . $field->readableInfo() . '</td>';
|
||||
// Print table row
|
||||
$o .= '<tr class="r' . $row . '"><td class="table cell">' . $f . $b . $r . '</tr>';
|
||||
$o .= '<tr class="r' . $row . '"><td class="cell firstcol">' . $f . $b . $r . '</tr>';
|
||||
$row = ($row + 1) % 2;
|
||||
}
|
||||
$o .= '</table>';
|
||||
@@ -296,7 +296,7 @@ class edit_table extends XMLDBAction {
|
||||
// The readable info
|
||||
$r = '</td><td class="readableinfo cell">' . $key->readableInfo() . '</td>';
|
||||
// Print table row
|
||||
$o .= '<tr class="r' . $row . '"><td class="table cell">' . $k . $b . $r .'</tr>';
|
||||
$o .= '<tr class="r' . $row . '"><td class="cell firstcol">' . $k . $b . $r .'</tr>';
|
||||
$row = ($row + 1) % 2;
|
||||
}
|
||||
$o .= '</table>';
|
||||
@@ -337,7 +337,7 @@ class edit_table extends XMLDBAction {
|
||||
// The readable info
|
||||
$r = '</td><td class="readableinfo cell">' . $index->readableInfo() . '</td>';
|
||||
// Print table row
|
||||
$o .= '<tr class="r' . $row . '"><td class="table cell">' . $i . $b . $r .'</tr>';
|
||||
$o .= '<tr class="r' . $row . '"><td class="cell firstcol">' . $i . $b . $r .'</tr>';
|
||||
$row = ($row + 1) % 2;
|
||||
}
|
||||
$o .= '</table>';
|
||||
|
||||
@@ -115,7 +115,7 @@ class edit_xml_file extends XMLDBAction {
|
||||
$o.= ' <input type="hidden" name ="path" value="' . s($structure->getPath()) .'" />';
|
||||
$o.= ' <input type="hidden" name ="version" value="' . s($structure->getVersion()) .'" />';
|
||||
$o.= ' <input type="hidden" name ="sesskey" value="' . sesskey() .'" />';
|
||||
$o.= ' <table id="formelements" class="boxaligncenter">';
|
||||
$o .= ' <table id="formelements">';
|
||||
$o.= ' <tr valign="top"><td>Path:</td><td>' . s($structure->getPath()) . '</td></tr>';
|
||||
$o.= ' <tr valign="top"><td>Version:</td><td>' . s($structure->getVersion()) . '</td></tr>';
|
||||
$o.= ' <tr valign="top"><td><label for="comment" accesskey="c">Comment:</label></td><td><textarea name="comment" rows="3" cols="80" id="comment">' . $structure->getComment() . '</textarea></td></tr>';
|
||||
@@ -216,7 +216,7 @@ class edit_xml_file extends XMLDBAction {
|
||||
}
|
||||
$b .= '</td>';
|
||||
// Print table row
|
||||
$o .= '<tr class="r' . $row . '"><td class="table cell">' . $t . $b . '</tr>';
|
||||
$o .= '<tr class="r' . $row . '"><td class="cell firstcol">' . $t . $b . '</tr>';
|
||||
$row = ($row + 1) % 2;
|
||||
}
|
||||
$o .= '</table>';
|
||||
|
||||
@@ -162,7 +162,7 @@ class view_table_php extends XMLDBAction {
|
||||
$o.= ' <input type="hidden" name ="dir" value="' . str_replace($CFG->dirroot, '', $dirpath) . '" />';
|
||||
$o.= ' <input type="hidden" name ="table" value="' . s($tableparam) . '" />';
|
||||
$o.= ' <input type="hidden" name ="action" value="view_table_php" />';
|
||||
$o.= ' <table id="formelements" class="boxaligncenter" cellpadding="5">';
|
||||
$o .= ' <table id="formelements" cellpadding="5">';
|
||||
$o.= ' <tr><td><label for="menucommand" accesskey="c">' . $this->str['selectaction'] .' </label>' . html_writer::select($popcommands, 'command', $commandparam, false) . ' <label for="menufieldkeyindex" accesskey="f">' . $this->str['selectfieldkeyindex'] . ' </label>' .html_writer::select($popfields, 'fieldkeyindex', $origfieldkeyindexparam, false) . '</td></tr>';
|
||||
$o.= ' <tr><td colspan="2" align="center"><input type="submit" value="' .$this->str['view'] . '" /></td></tr>';
|
||||
$o.= ' </table>';
|
||||
|
||||
@@ -48,5 +48,8 @@ function xmldb_auth_cas_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+6
-6
@@ -709,12 +709,12 @@ class auth_plugin_db extends auth_plugin_base {
|
||||
raise_memory_limit(MEMORY_HUGE);
|
||||
|
||||
if (empty($this->config->table)) {
|
||||
echo $OUTPUT->notification('External table not specified.', 'notifyproblem');
|
||||
echo $OUTPUT->notification(get_string('auth_dbnoexttable', 'auth_db'), 'notifyproblem');
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($this->config->fielduser)) {
|
||||
echo $OUTPUT->notification('External user field not specified.', 'notifyproblem');
|
||||
echo $OUTPUT->notification(get_string('auth_dbnouserfield', 'auth_db'), 'notifyproblem');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -735,7 +735,7 @@ class auth_plugin_db extends auth_plugin_base {
|
||||
error_reporting($CFG->debug);
|
||||
ob_end_flush();
|
||||
|
||||
echo $OUTPUT->notification('Cannot connect the database.', 'notifyproblem');
|
||||
echo $OUTPUT->notification(get_string('auth_dbcannotconnect', 'auth_db'), 'notifyproblem');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -744,17 +744,17 @@ class auth_plugin_db extends auth_plugin_base {
|
||||
WHERE {$this->config->fielduser} <> 'random_unlikely_username'"); // Any unlikely name is ok here.
|
||||
|
||||
if (!$rs) {
|
||||
echo $OUTPUT->notification('Can not read external table.', 'notifyproblem');
|
||||
echo $OUTPUT->notification(get_string('auth_dbcannotreadtable', 'auth_db'), 'notifyproblem');
|
||||
|
||||
} else if ($rs->EOF) {
|
||||
echo $OUTPUT->notification('External table is empty.', 'notifyproblem');
|
||||
echo $OUTPUT->notification(get_string('auth_dbtableempty', 'auth_db'), 'notifyproblem');
|
||||
$rs->close();
|
||||
|
||||
} else {
|
||||
$fields_obj = $rs->FetchObj();
|
||||
$columns = array_keys((array)$fields_obj);
|
||||
|
||||
echo $OUTPUT->notification('External table contains following columns:<br />'.implode(', ', $columns), 'notifysuccess');
|
||||
echo $OUTPUT->notification(get_string('auth_dbcolumnlist', 'auth_db', implode(', ', $columns)), 'notifysuccess');
|
||||
$rs->close();
|
||||
}
|
||||
|
||||
|
||||
@@ -48,5 +48,8 @@ function xmldb_auth_db_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -68,5 +68,11 @@ $string['auth_dbuser'] = 'Username with read access to the database';
|
||||
$string['auth_dbuser_key'] = 'DB user';
|
||||
$string['auth_dbuserstoadd'] = 'User entries to add: {$a}';
|
||||
$string['auth_dbuserstoremove'] = 'User entries to remove: {$a}';
|
||||
$string['auth_dbnoexttable'] = 'External table not specified.';
|
||||
$string['auth_dbnouserfield'] = 'External user field not specified.';
|
||||
$string['auth_dbcannotconnect'] = 'Cannot connect to external database.';
|
||||
$string['auth_dbcannotreadtable'] = 'Cannot read external table.';
|
||||
$string['auth_dbtableempty'] = 'External table is empty.';
|
||||
$string['auth_dbcolumnlist'] = 'External table contains the following columns:<br />{$a}';
|
||||
$string['pluginname'] = 'External database';
|
||||
$string['privacy:metadata'] = 'The External database authentication plugin does not store any personal data.';
|
||||
|
||||
@@ -48,5 +48,8 @@ function xmldb_auth_email_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+11
-2
@@ -1187,9 +1187,18 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
$nuvalue = core_text::convert($newvalue, 'utf-8', $this->config->ldapencoding);
|
||||
empty($nuvalue) ? $nuvalue = array() : $nuvalue;
|
||||
$ouvalue = core_text::convert($oldvalue, 'utf-8', $this->config->ldapencoding);
|
||||
|
||||
foreach ($ldapkeys as $ldapkey) {
|
||||
$ldapkey = $ldapkey;
|
||||
// Skip update if $ldapkey does not exist in LDAP.
|
||||
if (!isset($user_entry[$ldapkey][0])) {
|
||||
$success = false;
|
||||
error_log($this->errorlogtag.get_string('updateremfailfield', 'auth_ldap',
|
||||
array('ldapkey' => $ldapkey,
|
||||
'key' => $key,
|
||||
'ouvalue' => $ouvalue,
|
||||
'nuvalue' => $nuvalue)));
|
||||
continue;
|
||||
}
|
||||
|
||||
$ldapvalue = $user_entry[$ldapkey][0];
|
||||
if (!$ambiguous) {
|
||||
// Skip update if the values already match
|
||||
|
||||
@@ -66,5 +66,8 @@ function xmldb_auth_ldap_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ $string['start_tls'] = 'Use regular LDAP service (port 389) with TLS encryption'
|
||||
$string['start_tls_key'] = 'Use TLS';
|
||||
$string['updateremfail'] = 'Error updating LDAP record. Error code: {$a->errno}; Error string: {$a->errstring}<br/>Key ({$a->key}) - old moodle value: \'{$a->ouvalue}\' new value: \'{$a->nuvalue}\'';
|
||||
$string['updateremfailamb'] = 'Failed to update LDAP with ambiguous field {$a->key}; old moodle value: \'{$a->ouvalue}\', new value: \'{$a->nuvalue}\'';
|
||||
$string['updateremfailfield'] = 'Failed to update LDAP with non-existent field (\'{$a->ldapkey}\'). Key ({$a->key}) - old moodle value: \'{$a->ouvalue}\' new value: \'{$a->nuvalue}\'';
|
||||
$string['updatepasserror'] = 'Error in user_update_password(). Error code: {$a->errno}; Error string: {$a->errstring}';
|
||||
$string['updatepasserrorexpire'] = 'Error in user_update_password() when reading password expiry time. Error code: {$a->errno}; Error string: {$a->errstring}';
|
||||
$string['updatepasserrorexpiregrace'] = 'Error in user_update_password() when modifying expirationtime and/or gracelogins. Error code: {$a->errno}; Error string: {$a->errstring}';
|
||||
|
||||
@@ -48,5 +48,8 @@ function xmldb_auth_manual_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -47,5 +47,8 @@ function xmldb_auth_mnet_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ $string['pluginname'] = 'MNet authentication';
|
||||
$string['privacy:metadata:external:mahara'] = 'This plugin can send data externally to a linked Mahara application.';
|
||||
$string['privacy:metadata:external:moodle'] = 'This plugin can send data externally to a linked Moodle application.';
|
||||
$string['privacy:metadata:mnet_external:address'] = 'The address of the user.';
|
||||
$string['privacy:metadata:mnet_external:aim'] = 'The AIM identifier of the user.';
|
||||
$string['privacy:metadata:mnet_external:aim'] = 'The AIM identifier of the user';
|
||||
$string['privacy:metadata:mnet_external:alternatename'] = 'An alternative name for the user.';
|
||||
$string['privacy:metadata:mnet_external:autosubscribe'] = 'A preference as to if the user should be auto-subscribed to forums the user posts in.';
|
||||
$string['privacy:metadata:mnet_external:calendartype'] = 'A user preference for the type of calendar to use.';
|
||||
@@ -53,8 +53,8 @@ $string['privacy:metadata:mnet_external:firstaccess'] = 'The time that this user
|
||||
$string['privacy:metadata:mnet_external:firstname'] = 'The first name of the user.';
|
||||
$string['privacy:metadata:mnet_external:firstnamephonetic'] = 'The phonetic details about the user\'s first name.';
|
||||
$string['privacy:metadata:mnet_external:icq'] = 'The ICQ number of the user.';
|
||||
$string['privacy:metadata:mnet_external:id'] = 'The identifier for the user.';
|
||||
$string['privacy:metadata:mnet_external:idnumber'] = 'An identification number given by the institution.';
|
||||
$string['privacy:metadata:mnet_external:id'] = 'The user ID';
|
||||
$string['privacy:metadata:mnet_external:idnumber'] = 'An identification number given by the institution';
|
||||
$string['privacy:metadata:mnet_external:imagealt'] = 'Alternative text for the user\'s image.';
|
||||
$string['privacy:metadata:mnet_external:institution'] = 'The institution that this user is a member of.';
|
||||
$string['privacy:metadata:mnet_external:lang'] = 'A user preference for the language shown.';
|
||||
@@ -64,20 +64,20 @@ $string['privacy:metadata:mnet_external:lastname'] = 'The surname of the user.';
|
||||
$string['privacy:metadata:mnet_external:lastnamephonetic'] = 'The phonetic details about the user\'s surname.';
|
||||
$string['privacy:metadata:mnet_external:maildigest'] = 'A setting for the mail digest for this user.';
|
||||
$string['privacy:metadata:mnet_external:maildisplay'] = 'A preference for the user about displaying their email address to other users.';
|
||||
$string['privacy:metadata:mnet_external:middlename'] = 'The middle name of the user.';
|
||||
$string['privacy:metadata:mnet_external:msn'] = 'The MSN identifier of the user.';
|
||||
$string['privacy:metadata:mnet_external:middlename'] = 'The middle name of the user';
|
||||
$string['privacy:metadata:mnet_external:msn'] = 'The MSN identifier of the user';
|
||||
$string['privacy:metadata:mnet_external:phone1'] = 'A phone number for the user.';
|
||||
$string['privacy:metadata:mnet_external:phone2'] = 'An additional phone number for the user.';
|
||||
$string['privacy:metadata:mnet_external:picture'] = 'The picture details associated with this user.';
|
||||
$string['privacy:metadata:mnet_external:policyagreed'] = 'A flag to determine if the user has agreed to the site policy.';
|
||||
$string['privacy:metadata:mnet_external:skype'] = 'The skype identifier of the user.';
|
||||
$string['privacy:metadata:mnet_external:skype'] = 'The Skype identifier of the user';
|
||||
$string['privacy:metadata:mnet_external:suspended'] = 'A flag to show if the user has been suspended on this system.';
|
||||
$string['privacy:metadata:mnet_external:timezone'] = 'The timezone that the user resides in.';
|
||||
$string['privacy:metadata:mnet_external:timezone'] = 'The timezone of the user';
|
||||
$string['privacy:metadata:mnet_external:trackforums'] = 'A preference for forums and tracking them.';
|
||||
$string['privacy:metadata:mnet_external:trustbitmask'] = 'The trust bit mask';
|
||||
$string['privacy:metadata:mnet_external:url'] = 'A URL related to this user.';
|
||||
$string['privacy:metadata:mnet_external:username'] = 'The username for this user.';
|
||||
$string['privacy:metadata:mnet_external:yahoo'] = 'The yahoo identifier of the user.';
|
||||
$string['privacy:metadata:mnet_external:yahoo'] = 'The Yahoo identifier of the user';
|
||||
$string['privacy:metadata:mnet_log'] = 'Details of remote actions carried out by a local user logged in a remote system.';
|
||||
$string['privacy:metadata:mnet_log:action'] = 'Action carried out by the user.';
|
||||
$string['privacy:metadata:mnet_log:cmid'] = 'ID of the course module.';
|
||||
@@ -94,7 +94,7 @@ $string['privacy:metadata:mnet_log:userid'] = 'Local ID of the user who carried
|
||||
$string['privacy:metadata:mnet_session'] = 'The details of each MNet user session in a remote system is stored temporarily.';
|
||||
$string['privacy:metadata:mnet_session:expires'] = 'Time when the session expires.';
|
||||
$string['privacy:metadata:mnet_session:mnethostid'] = 'Remote system MNet ID.';
|
||||
$string['privacy:metadata:mnet_session:token'] = 'Unique session identifier.';
|
||||
$string['privacy:metadata:mnet_session:token'] = 'Unique session identifier';
|
||||
$string['privacy:metadata:mnet_session:useragent'] = 'String denoting the user agent being which is accessing the page.';
|
||||
$string['privacy:metadata:mnet_session:userid'] = 'ID of the user jumping to remote system.';
|
||||
$string['privacy:metadata:mnet_session:username'] = 'Username of the user jumping to remote system.';
|
||||
|
||||
@@ -48,5 +48,8 @@ function xmldb_auth_none_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -44,5 +44,8 @@ function xmldb_auth_oauth2_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ $string['privacy:metadata:auth_oauth2:authsubsystem'] = 'This plugin is connecte
|
||||
$string['privacy:metadata:auth_oauth2:confirmtoken'] = 'The confirmation token.';
|
||||
$string['privacy:metadata:auth_oauth2:confirmtokenexpires'] = 'The timestamp when the confirmation token expires.';
|
||||
$string['privacy:metadata:auth_oauth2:email'] = 'The external email that maps to this account.';
|
||||
$string['privacy:metadata:auth_oauth2:issuerid'] = 'The identifier of the OAuth 2 issuer for this OAuth 2 login.';
|
||||
$string['privacy:metadata:auth_oauth2:issuerid'] = 'The ID of the OAuth 2 issuer for this OAuth 2 login';
|
||||
$string['privacy:metadata:auth_oauth2:tableexplanation'] = 'OAuth 2 accounts linked to a user\'s Moodle account.';
|
||||
$string['privacy:metadata:auth_oauth2:timecreated'] = 'The timestamp when the user account was linked to the OAuth 2 login.';
|
||||
$string['privacy:metadata:auth_oauth2:timemodified'] = 'The timestamp when this record was modified.';
|
||||
|
||||
@@ -48,5 +48,8 @@ function xmldb_auth_shibboleth_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ class restore_stepslib_date_testcase extends restore_date_testcase {
|
||||
// Testing the restore of an overridden grade.
|
||||
list($course, $assign) = $this->create_course_and_module('assign', []);
|
||||
$cm = $DB->get_record('course_modules', ['course' => $course->id, 'instance' => $assign->id]);
|
||||
$assignobj = new testable_assign(context_module::instance($cm->id), $cm, $course);
|
||||
$assignobj = new mod_assign_testable_assign(context_module::instance($cm->id), $cm, $course);
|
||||
$submission = $assignobj->get_user_submission($USER->id, true);
|
||||
$grade = $assignobj->get_user_grade($USER->id, true);
|
||||
$grade->grade = 75;
|
||||
@@ -300,7 +300,7 @@ class restore_stepslib_date_testcase extends restore_date_testcase {
|
||||
'completionusegrade' => 1 // Student must receive a grade to complete this activity.
|
||||
]);
|
||||
$cm = $DB->get_record('course_modules', ['course' => $course->id, 'instance' => $assign->id]);
|
||||
$assignobj = new testable_assign(context_module::instance($cm->id), $cm, $course);
|
||||
$assignobj = new mod_assign_testable_assign(context_module::instance($cm->id), $cm, $course);
|
||||
$submission = $assignobj->get_user_submission($USER->id, true);
|
||||
$grade = $assignobj->get_user_grade($USER->id, true);
|
||||
$grade->grade = 75;
|
||||
@@ -410,4 +410,4 @@ class restore_stepslib_date_testcase extends restore_date_testcase {
|
||||
$this->assertEquals($dates['originaldate'], $dates['restoredate']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,11 @@ class backup_ui_stage_initial extends backup_ui_stage {
|
||||
foreach ($tasks as &$task) {
|
||||
// For the initial stage we are only interested in the root settings.
|
||||
if ($task instanceof backup_root_task) {
|
||||
$form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
|
||||
if ($this->ui instanceof import_ui) {
|
||||
$form->add_heading('rootsettings', get_string('importrootsettings', 'backup'));
|
||||
} else {
|
||||
$form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
|
||||
}
|
||||
$settings = $task->get_settings();
|
||||
// First add all settings except the filename setting.
|
||||
foreach ($settings as &$setting) {
|
||||
@@ -469,7 +473,11 @@ class backup_ui_stage_confirmation extends backup_ui_stage {
|
||||
foreach ($tasks as $task) {
|
||||
if ($task instanceof backup_root_task) {
|
||||
// If its a backup root add a root settings heading to group nicely.
|
||||
$form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
|
||||
if ($this->ui instanceof import_ui) {
|
||||
$form->add_heading('rootsettings', get_string('importrootsettings', 'backup'));
|
||||
} else {
|
||||
$form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
|
||||
}
|
||||
} else if (!$courseheading) {
|
||||
// We haven't already add a course heading.
|
||||
$form->add_heading('coursesettings', get_string('includeditems', 'backup'));
|
||||
|
||||
@@ -383,7 +383,13 @@ abstract class base_moodleform extends moodleform {
|
||||
$this->require_definition_after_data();
|
||||
|
||||
$config = new stdClass;
|
||||
$config->title = get_string('confirmcancel', 'backup');
|
||||
if ($this->uistage->get_ui() instanceof import_ui) {
|
||||
$config->title = get_string('confirmcancelimport', 'backup');
|
||||
} else if ($this->uistage->get_ui() instanceof restore_ui) {
|
||||
$config->title = get_string('confirmcancelrestore', 'backup');
|
||||
} else {
|
||||
$config->title = get_string('confirmcancel', 'backup');
|
||||
}
|
||||
$config->question = get_string('confirmcancelquestion', 'backup');
|
||||
$config->yesLabel = get_string('confirmcancelyes', 'backup');
|
||||
$config->noLabel = get_string('confirmcancelno', 'backup');
|
||||
|
||||
@@ -901,7 +901,7 @@ class restore_ui_stage_review extends restore_ui_stage {
|
||||
foreach ($tasks as $task) {
|
||||
if ($task instanceof restore_root_task) {
|
||||
// If its a backup root add a root settings heading to group nicely.
|
||||
$form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
|
||||
$form->add_heading('rootsettings', get_string('restorerootsettings', 'backup'));
|
||||
} else if (!$courseheading) {
|
||||
// We haven't already add a course heading.
|
||||
$form->add_heading('coursesettings', get_string('coursesettings', 'backup'));
|
||||
@@ -1061,7 +1061,26 @@ class restore_ui_stage_process extends restore_ui_stage {
|
||||
if (!empty($info->role_mappings->mappings)) {
|
||||
$context = context_course::instance($this->ui->get_controller()->get_courseid());
|
||||
$assignableroles = get_assignable_roles($context, ROLENAME_ALIAS, false);
|
||||
$html .= $renderer->role_mappings($info->role_mappings->mappings, $assignableroles);
|
||||
|
||||
// Get current role mappings.
|
||||
$currentroles = role_fix_names(get_all_roles(), $context);
|
||||
// Get backup role mappings.
|
||||
$rolemappings = $info->role_mappings->mappings;
|
||||
|
||||
array_map(function($rolemapping) use ($currentroles) {
|
||||
foreach ($currentroles as $role) {
|
||||
// Find matching archetype to determine the backup's shortname for label display.
|
||||
if ($rolemapping->archetype == $role->archetype) {
|
||||
$rolemapping->name = $rolemapping->shortname;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($rolemapping->name == null) {
|
||||
$rolemapping->name = get_string('undefinedrolemapping', 'backup', $rolemapping->archetype);
|
||||
}
|
||||
}, $rolemappings);
|
||||
|
||||
$html .= $renderer->role_mappings($rolemappings, $assignableroles);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -54,5 +54,8 @@ function xmldb_block_badges_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -54,5 +54,8 @@ function xmldb_block_calendar_month_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -54,5 +54,8 @@ function xmldb_block_calendar_upcoming_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -55,5 +55,8 @@ function xmldb_block_community_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -57,5 +57,8 @@ function xmldb_block_completionstatus_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -57,5 +57,8 @@ function xmldb_block_course_summary_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,5 +42,8 @@ function xmldb_block_html_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -64,5 +64,8 @@ function xmldb_block_navigation_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.4.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.5.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user