Merge branch 'MDL-75187-master' of https://github.com/sarjona/moodle

This commit is contained in:
Sara Arjona 2022-09-06 06:39:58 +02:00
commit d235080a03
15 changed files with 646 additions and 66 deletions

10
mod/data/amd/build/deletepreset.min.js vendored Normal file
View File

@ -0,0 +1,10 @@
define("mod_data/deletepreset",["exports","core/notification","core/prefetch","core/str","core/ajax","core/url"],(function(_exports,_notification,_prefetch,_str,_ajax,_url){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Javascript module for deleting a database as a preset.
*
* @module mod_data/deletepreset
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_ajax=_interopRequireDefault(_ajax),_url=_interopRequireDefault(_url);const selectors_deletePresetButton='[data-action="deletepreset"]';_exports.init=()=>{(0,_prefetch.prefetchStrings)("mod_data",["deleteconfirm","deletewarning"]),(0,_prefetch.prefetchStrings)("core",["delete"]),registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const deleteOption=event.target.closest(selectors_deletePresetButton);deleteOption&&(event.preventDefault(),deletePresetConfirm(deleteOption))}))},deletePresetConfirm=deleteOption=>{const presetName=deleteOption.getAttribute("data-presetname"),dataId=deleteOption.getAttribute("data-dataid");_notification.default.saveCancelPromise((0,_str.get_string)("deleteconfirm","mod_data",presetName),(0,_str.get_string)("deletewarning","mod_data"),(0,_str.get_string)("delete","core")).then((()=>async function(dataId,presetName){var request={methodname:"mod_data_delete_saved_preset",args:{dataid:dataId,presetnames:{presetname:presetName}}};try{await _ajax.default.call([request])[0],window.location.href=_url.default.relativeUrl("mod/data/preset.php",{d:dataId},!1)}catch(error){_notification.default.exception(error)}}(dataId,presetName))).catch((()=>{}))}}));
//# sourceMappingURL=deletepreset.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"deletepreset.min.js","sources":["../src/deletepreset.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript module for deleting a database as a preset.\n *\n * @module mod_data/deletepreset\n * @copyright 2022 Amaia Anabitarte <amaia@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport Ajax from 'core/ajax';\nimport Url from 'core/url';\n\nconst selectors = {\n deletePresetButton: '[data-action=\"deletepreset\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n prefetchStrings('mod_data', [\n 'deleteconfirm',\n 'deletewarning',\n ]);\n prefetchStrings('core', [\n 'delete',\n ]);\n\n registerEventListeners();\n};\n\n/**\n * Register events for delete preset option in action menu.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const deleteOption = event.target.closest(selectors.deletePresetButton);\n if (deleteOption) {\n event.preventDefault();\n deletePresetConfirm(deleteOption);\n }\n });\n};\n\n/**\n * Show the confirmation modal to delete the preset.\n *\n * @param {HTMLElement} deleteOption the element to delete.\n */\nconst deletePresetConfirm = (deleteOption) => {\n const presetName = deleteOption.getAttribute('data-presetname');\n const dataId = deleteOption.getAttribute('data-dataid');\n\n Notification.saveCancelPromise(\n getString('deleteconfirm', 'mod_data', presetName),\n getString('deletewarning', 'mod_data'),\n getString('delete', 'core'),\n ).then(() => {\n return deletePreset(dataId, presetName);\n }).catch(() => {\n return;\n });\n};\n\n/**\n * Delete site user preset.\n *\n * @param {int} dataId The id of the current database activity.\n * @param {string} presetName The preset name to delete.\n * @return {promise} Resolved with the result and warnings of deleting a preset.\n */\nasync function deletePreset(dataId, presetName) {\n var request = {\n methodname: 'mod_data_delete_saved_preset',\n args: {\n dataid: dataId,\n presetnames: {presetname: presetName},\n }\n };\n try {\n await Ajax.call([request])[0];\n window.location.href = Url.relativeUrl(\n 'mod/data/preset.php',\n {\n d: dataId,\n },\n false\n );\n } catch (error) {\n Notification.exception(error);\n }\n}\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","deleteOption","target","closest","preventDefault","deletePresetConfirm","presetName","getAttribute","dataId","saveCancelPromise","then","request","methodname","args","dataid","presetnames","presetname","Ajax","call","window","location","href","Url","relativeUrl","d","error","exception","deletePreset","catch"],"mappings":";;;;;;;kNA6BMA,6BACkB,6CAMJ,mCACA,WAAY,CACxB,gBACA,gDAEY,OAAQ,CACpB,WAGJC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,aAAeD,MAAME,OAAOC,QAAQP,8BACtCK,eACAD,MAAMI,iBACNC,oBAAoBJ,mBAU1BI,oBAAuBJ,qBACnBK,WAAaL,aAAaM,aAAa,mBACvCC,OAASP,aAAaM,aAAa,qCAE5BE,mBACT,mBAAU,gBAAiB,WAAYH,aACvC,mBAAU,gBAAiB,aAC3B,mBAAU,SAAU,SACtBI,MAAK,mBAciBF,OAAQF,gBAC5BK,QAAU,CACVC,WAAY,+BACZC,KAAM,CACFC,OAAQN,OACRO,YAAa,CAACC,WAAYV,wBAIxBW,cAAKC,KAAK,CAACP,UAAU,GAC3BQ,OAAOC,SAASC,KAAOC,aAAIC,YACvB,sBACA,CACIC,EAAGhB,SAEP,GAEN,MAAOiB,6BACQC,UAAUD,QA/BhBE,CAAanB,OAAQF,cAC7BsB,OAAM"}

View File

@ -0,0 +1,109 @@
// 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/>.
/**
* Javascript module for deleting a database as a preset.
*
* @module mod_data/deletepreset
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Notification from 'core/notification';
import {prefetchStrings} from 'core/prefetch';
import {get_string as getString} from 'core/str';
import Ajax from 'core/ajax';
import Url from 'core/url';
const selectors = {
deletePresetButton: '[data-action="deletepreset"]',
};
/**
* Initialize module
*/
export const init = () => {
prefetchStrings('mod_data', [
'deleteconfirm',
'deletewarning',
]);
prefetchStrings('core', [
'delete',
]);
registerEventListeners();
};
/**
* Register events for delete preset option in action menu.
*/
const registerEventListeners = () => {
document.addEventListener('click', (event) => {
const deleteOption = event.target.closest(selectors.deletePresetButton);
if (deleteOption) {
event.preventDefault();
deletePresetConfirm(deleteOption);
}
});
};
/**
* Show the confirmation modal to delete the preset.
*
* @param {HTMLElement} deleteOption the element to delete.
*/
const deletePresetConfirm = (deleteOption) => {
const presetName = deleteOption.getAttribute('data-presetname');
const dataId = deleteOption.getAttribute('data-dataid');
Notification.saveCancelPromise(
getString('deleteconfirm', 'mod_data', presetName),
getString('deletewarning', 'mod_data'),
getString('delete', 'core'),
).then(() => {
return deletePreset(dataId, presetName);
}).catch(() => {
return;
});
};
/**
* Delete site user preset.
*
* @param {int} dataId The id of the current database activity.
* @param {string} presetName The preset name to delete.
* @return {promise} Resolved with the result and warnings of deleting a preset.
*/
async function deletePreset(dataId, presetName) {
var request = {
methodname: 'mod_data_delete_saved_preset',
args: {
dataid: dataId,
presetnames: {presetname: presetName},
}
};
try {
await Ajax.call([request])[0];
window.location.href = Url.relativeUrl(
'mod/data/preset.php',
{
d: dataId,
},
false
);
} catch (error) {
Notification.exception(error);
}
}

View File

@ -0,0 +1,122 @@
<?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/>.
namespace mod_data\external;
use core\notification;
use mod_data\manager;
use mod_data\preset;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/externallib.php');
/**
* This is the external method for deleting a saved preset.
*
* @package mod_data
* @since Moodle 4.1
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class delete_saved_preset extends \external_api {
/**
* Parameters.
*
* @return \external_function_parameters
*/
public static function execute_parameters(): \external_function_parameters {
return new \external_function_parameters([
'dataid' => new \external_value(PARAM_INT, 'Id of the data activity', VALUE_REQUIRED),
'presetnames' => new \external_multiple_structure(
new \external_value(PARAM_TEXT, 'The preset name to delete', VALUE_REQUIRED)
)
]);
}
/**
* Delete saved preset from the file system.
*
* @param int $dataid Id of the data activity to check context and permissions.
* @param array $presetnames List of saved preset names to delete.
* @return array True if the content has been deleted; false and the warning, otherwise.
*/
public static function execute(int $dataid, array $presetnames): array {
global $DB;
$result = false;
$warnings = [];
$params = self::validate_parameters(self::execute_parameters(), ['dataid' => $dataid, 'presetnames' => $presetnames]);
$instance = $DB->get_record('data', ['id' => $params['dataid']], '*', MUST_EXIST);
$manager = manager::create_from_instance($instance);
foreach ($params['presetnames'] as $presetname) {
try {
$preset = preset::create_from_instance($manager, $presetname);
if ($preset->can_manage()) {
if ($preset->delete()) {
notification::success(get_string('presetdeleted', 'mod_data'));
$result = true;
} else {
// An error ocurred while deleting the preset.
$warnings[] = [
'item' => $presetname,
'warningcode' => 'failedpresetdelete',
'message' => get_string('failedpresetdelete', 'mod_data')
];
notification::error(get_string('failedpresetdelete', 'mod_data'));
}
} else {
// The user has no permission to delete the preset.
$warnings[] = [
'item' => $presetname,
'warningcode' => 'cannotdeletepreset',
'message' => get_string('cannotdeletepreset', 'mod_data')
];
notification::error(get_string('cannotdeletepreset', 'mod_data'));
}
} catch (\moodle_exception $e) {
// The saved preset has not been deleted.
$warnings[] = [
'item' => $presetname,
'warningcode' => 'exception',
'message' => $e->getMessage()
];
notification::error($e->getMessage());
}
}
return [
'result' => $result,
'warnings' => $warnings
];
}
/**
* Return.
*
* @return \external_single_structure
*/
public static function execute_returns(): \external_single_structure {
return new \external_single_structure([
'result' => new \external_value(PARAM_BOOL, 'The processing result'),
'warnings' => new \external_warnings()
]);
}
}

View File

@ -95,7 +95,7 @@ class save_as_preset extends dynamic_form {
break;
}
}
if (isset($selectedpreset->name) && !data_user_can_delete_preset($context, $selectedpreset)) {
if (!$selectedpreset instanceof preset || !$selectedpreset->can_manage()) {
$errors['name'] = get_string('cannotoverwritepreset', 'data');
}
} else if ($formdata['action'] == 'saveaspreset' || $formdata['oldpresetname'] != $formdata['name']) {
@ -105,7 +105,7 @@ class save_as_preset extends dynamic_form {
$usercandelete = false;
foreach ($sitepresets as $preset) {
if ($formdata['name'] == $preset->name) {
if (data_user_can_delete_preset($context, $preset)) {
if ($preset->can_manage()) {
$errors['name'] = get_string('errorpresetexists', 'data');
$usercandelete = true;
} else {
@ -176,8 +176,8 @@ class save_as_preset extends dynamic_form {
break;
}
}
if (isset($selectedpreset->name) && data_user_can_delete_preset($context, $selectedpreset)) {
data_delete_site_preset($formdata->name);
if ($selectedpreset instanceof preset && $selectedpreset->can_manage()) {
$selectedpreset->delete();
}
}
$presetname = $formdata->name;

View File

@ -136,7 +136,7 @@ class presets implements templatable, renderable {
$actionmenu->attributes['class'] .= ' presets-actions';
// Only users with mod/data:manageuserpresets capability have options to edit the preset.
if (data_user_can_delete_preset($PAGE->context, $preset)) {
if ($preset->can_manage()) {
$params = [
'd' => $this->id,
'action' => 'edit',
@ -158,14 +158,19 @@ class presets implements templatable, renderable {
// Delete.
$params = [
'd' => $this->id,
'fullname' => "{$userid}/{$preset->shortname}",
'action' => 'confirmdelete',
'action' => 'delete',
];
$deleteactionurl = new moodle_url('/mod/data/preset.php', $params);
$attributes = [
'data-action' => 'deletepreset',
'data-dataid' => $this->id,
"data-presetname" => $preset->name,
];
$actionmenu->add(new action_menu_link_secondary(
$deleteactionurl,
null,
get_string('delete'),
$attributes,
));
}
}

View File

@ -535,4 +535,67 @@ class preset {
return $preset->asXML();
}
/**
* Checks to see if the user has permission to manage the preset.
*
* @return bool Returns true if the user can manage this preset, false otherwise.
*/
public function can_manage(): bool {
global $USER;
if ($this->isplugin) {
// Plugin presets can't be removed or edited.
return false;
}
$context = $this->manager->get_context();
if (has_capability('mod/data:manageuserpresets', $context)) {
return true;
} else {
if ($this->get_userid() == $USER->id) {
return true;
}
}
return false;
}
/**
* Deletes all files related to a saved preset.
*
* @return bool True if the preset is a saved preset and the file exists in the file system; false otherwise.
*/
public function delete(): bool {
if ($this->isplugin) {
// Plugin presets can't be removed.
return false;
}
$exists = false;
$filepath = $this->get_path();
$dir = self::get_file($filepath, '.');
if (!empty($dir)) {
$exists = true;
$fs = get_file_storage();
$files = $fs->get_directory_files(
$dir->get_contextid(),
$dir->get_component(),
$dir->get_filearea(),
$dir->get_itemid(),
$filepath
);
if (!empty($files)) {
foreach ($files as $file) {
$file->delete();
}
}
$dir->delete();
// Reseting storedfile property because the file has been removed.
$this->storedfile = null;
}
return $exists;
}
}

View File

@ -115,4 +115,11 @@ $functions = array(
'capabilities' => 'mod/data:writeentry',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_data_delete_saved_preset' => array(
'classname' => 'mod_data\external\delete_saved_preset',
'description' => 'Delete site user preset.',
'type' => 'write',
'ajax' => true,
'capabilities' => 'mod/data:manageuserpresets',
),
);

View File

@ -115,11 +115,12 @@ $string['delcheck'] = 'Bulk delete checkbox';
$string['delete'] = 'Delete';
$string['deleteallentries'] = 'Delete all entries';
$string['deletecomment'] = 'Are you sure you want to delete this comment?';
$string['deleteconfirm'] = 'Delete preset {$a}?';
$string['deleted'] = 'deleted';
$string['deleteentry'] = 'Delete entry';
$string['deletefield'] = 'Delete field';
$string['deletenotenrolled'] = 'Delete entries by users not enrolled';
$string['deletewarning'] = 'Are you sure you want to delete this preset?';
$string['deletewarning'] = 'Deleting a preset removes it from the list of available presets in all courses.';
$string['descending'] = 'Descending';
$string['directorynotapreset'] = '{$a->directory} Not a preset: missing files: {$a->missing_files}';
$string['disapprove'] = 'Undo approval';
@ -163,7 +164,7 @@ $string['exportformat'] = 'Export format';
$string['exportoptions'] = 'Export options';
$string['exportownentries'] = 'Export your own entries only? ({$a->mine}/{$a->all})';
$string['exportpreset'] = 'Export preset';
$string['failedpresetdelete'] = 'Error deleting a preset!';
$string['failedpresetdelete'] = 'An error was encountered while trying to delete the preset.';
$string['fieldadded'] = 'Field added';
$string['fieldallowautolink'] = 'Allow autolink';
$string['fielddeleted'] = 'Field deleted';
@ -321,6 +322,7 @@ $string['pleaseaddsome'] = 'Please create some below or <a href="{$a}">choose a
$string['pluginadministration'] = 'Database activity administration';
$string['pluginname'] = 'Database';
$string['portfolionotfile'] = 'Export to a portfolio rather than a file (csv and leap2a only)';
$string['presetdeleted'] = 'Preset deleted.';
$string['presetinfo'] = 'Saving as a preset will publish this template. Other users may be able to use it in their databases.';
$string['presetnotselected'] = 'No preset has been selected.';
$string['presets'] = 'Presets';

View File

@ -2085,8 +2085,13 @@ function data_get_available_site_presets($context, array $presets=array()) {
*
* @param string $name
* @return bool
* @deprecated since Moodle 4.1 MDL-75187 - please, use the preset::delete() function instead.
* @todo MDL-75189 This will be deleted in Moodle 4.5.
* @see preset::delete()
*/
function data_delete_site_preset($name) {
debugging('data_delete_site_preset() is deprecated. Please use preset::delete() instead.', DEBUG_DEVELOPER);
$fs = get_file_storage();
$files = $fs->get_directory_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, '/'.$name.'/');
@ -3852,10 +3857,22 @@ function data_get_advanced_search_sql($sort, $data, $recordids, $selectdata, $so
* @param stdClass $context Context object.
* @param stdClass $preset The preset object that we are checking for deletion.
* @return bool Returns true if the user can delete, otherwise false.
* @deprecated since Moodle 4.1 MDL-75187 - please, use the preset::can_manage() function instead.
* @todo MDL-75189 This will be deleted in Moodle 4.5.
* @see preset::can_manage()
*/
function data_user_can_delete_preset($context, $preset) {
global $USER;
debugging('data_user_can_delete_preset() is deprecated. Please use manager::can_manage() instead.', DEBUG_DEVELOPER);
if ($context->contextlevel == CONTEXT_MODULE && isset($preset->name)) {
$cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST);
$manager = manager::create_from_coursemodule($cm);
$todelete = preset::create_from_instance($manager, $preset->name);
return $todelete->can_manage();
}
if (has_capability('mod/data:manageuserpresets', $context)) {
return true;
} else {

View File

@ -53,7 +53,7 @@ if ($id) {
}
$action = optional_param('action', 'view', PARAM_ALPHA); // The page action.
$allowedactions = ['view', 'delete', 'confirmdelete', 'import', 'importzip', 'finishimport',
$allowedactions = ['view', 'import', 'importzip', 'finishimport',
'export'];
if (!in_array($action, $allowedactions)) {
throw new moodle_exception('invalidaccess');
@ -71,6 +71,7 @@ $PAGE->set_title(get_string('course') . ': ' . $course->fullname);
$PAGE->set_heading($course->fullname);
$PAGE->force_settings_menu(true);
$PAGE->activityheader->disable();
$PAGE->requires->js_call_amd('mod_data/deletepreset', 'init');
// fill in missing properties needed for updating of instance
$data->course = $cm->course;
@ -124,7 +125,7 @@ if ($formdata = $formimportzip->get_data()) {
exit(0);
}
if (in_array($action, ['confirmdelete', 'delete', 'finishimport'])) {
if ($action === 'finishimport') {
$fullname = optional_param('fullname', '' , PARAM_PATH); // The directory the preset is in.
// Find out preset owner userid and shortname.
$parts = explode('/', $fullname, 2);
@ -132,60 +133,29 @@ if (in_array($action, ['confirmdelete', 'delete', 'finishimport'])) {
$shortname = empty($parts[1]) ? '' : $parts[1];
echo html_writer::start_div('overflow-hidden');
if ($action === 'confirmdelete') {
$path = data_preset_path($course, $userid, $shortname);
$strwarning = get_string('deletewarning', 'data').'<br />'.$shortname;
$optionsyes = [
'fullname' => $fullname,
'action' => 'delete',
'd' => $data->id,
];
$optionsno = ['d' => $data->id];
echo $OUTPUT->confirm($strwarning, new moodle_url('/mod/data/preset.php', $optionsyes),
new moodle_url('/mod/data/preset.php', $optionsno));
echo $OUTPUT->footer();
exit(0);
} else if ($action === 'delete') {
if (!confirm_sesskey()) {
throw new moodle_exception('invalidsesskey');
}
$selectedpreset = new stdClass();
foreach ($presets as $preset) {
if ($preset->shortname == $shortname) {
$selectedpreset = $preset;
}
}
if (!isset($selectedpreset->shortname) || !data_user_can_delete_preset($context, $selectedpreset)) {
throw new \moodle_exception('invalidrequest');
}
data_delete_site_preset($shortname);
$strdeleted = get_string('deleted', 'data');
echo $OUTPUT->notification("$shortname $strdeleted", 'notifysuccess');
} else if ($action === 'finishimport') {
if (!confirm_sesskey()) {
throw new moodle_exception('invalidsesskey');
}
$overwritesettings = optional_param('overwritesettings', false, PARAM_BOOL);
if (!$fullname) {
$presetdir = $CFG->tempdir.'/forms/'.required_param('directory', PARAM_FILE);
if (!file_exists($presetdir) || !is_dir($presetdir)) {
throw new \moodle_exception('cannotimport');
}
$importer = new data_preset_upload_importer($course, $cm, $data, $presetdir);
} else {
$importer = new data_preset_existing_importer($course, $cm, $data, $fullname);
}
$importer->import($overwritesettings);
$strimportsuccess = get_string('importsuccess', 'data');
$straddentries = get_string('addentries', 'data');
$strtodatabase = get_string('todatabase', 'data');
if (!$DB->get_records('data_records', array('dataid'=>$data->id))) {
echo $OUTPUT->notification("$strimportsuccess <a href='edit.php?d=$data->id'>$straddentries</a> $strtodatabase", 'notifysuccess');
} else {
echo $OUTPUT->notification("$strimportsuccess", 'notifysuccess');
}
if (!confirm_sesskey()) {
throw new moodle_exception('invalidsesskey');
}
$overwritesettings = optional_param('overwritesettings', false, PARAM_BOOL);
if (!$fullname) {
$presetdir = $CFG->tempdir.'/forms/'.required_param('directory', PARAM_FILE);
if (!file_exists($presetdir) || !is_dir($presetdir)) {
throw new \moodle_exception('cannotimport');
}
$importer = new data_preset_upload_importer($course, $cm, $data, $presetdir);
} else {
$importer = new data_preset_existing_importer($course, $cm, $data, $fullname);
}
$importer->import($overwritesettings);
$strimportsuccess = get_string('importsuccess', 'data');
$straddentries = get_string('addentries', 'data');
$strtodatabase = get_string('todatabase', 'data');
if (!$DB->get_records('data_records', ['dataid' => $data->id])) {
echo $OUTPUT->notification("$strimportsuccess <a href='edit.php?d=$data->id'>$straddentries</a> $strtodatabase", 'notifysuccess');
} else {
echo $OUTPUT->notification("$strimportsuccess", 'notifysuccess');
}
echo $OUTPUT->continue_button(new moodle_url('/mod/data/preset.php', ['d' => $data->id]));
echo html_writer::end_div();
echo $OUTPUT->footer();

View File

@ -169,7 +169,6 @@ Feature: Users can view and manage data presets
And I click on "Save" "button" in the "Edit preset" "dialogue"
Then I should not see "Preset saved."
And I should see "Saved preset by teacher1"
And I should see "This preset has also a description" in the "Saved preset by teacher1" "table_row"
@javascript
Scenario: Teachers can edit presets and overwrite them if they are the authors
@ -237,3 +236,27 @@ Feature: Users can view and manage data presets
And I should see "This preset has also a description" in the "Saved preset 1" "table_row"
And I should not see "Saved preset by teacher1"
And I should not see "The preset1 has description"
@javascript
Scenario: Teachers can delete their own presets
Given the following "mod_data > fields" exist:
| database | type | name | description |
| data1 | text | Test field name | Test field description |
And the following "mod_data > presets" exist:
| database | name | description | user |
| data1 | Saved preset by teacher1 | My funny description goes here. | teacher1 |
And I am on the "Mountain landscapes" "data activity" page logged in as teacher1
When I follow "Presets"
And I should see "Image gallery"
And I should see "Saved preset 1"
And I should see "Saved preset by teacher1"
# Plugin presets can't be removed.
And I should not see "Actions" in the "Image gallery" "table_row"
# The teacher should not be able to delete presets saved by others.
And I should not see "Actions" in the "Saved preset 1" "table_row"
# The teacher should be able to delete their own preset.
And I open the action menu in "Saved preset by teacher" "table_row"
And I follow "Delete"
And I click on "Delete" "button" in the "Delete preset Saved preset by teacher1?" "dialogue"
And I should see "Preset deleted"
And I should not see "Saved preset by teacher1"

View File

@ -0,0 +1,135 @@
<?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/>.
namespace mod_data\external;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
use externallib_advanced_testcase;
use external_api;
use mod_data\manager;
/**
* External function test for delete_saved_preset.
*
* @package mod_data
* @category external
* @since Moodle 4.1
* @copyright 2022 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \mod_data\external\delete_saved_preset
*/
class delete_saved_preset_test extends externallib_advanced_testcase {
/**
* Test the behaviour of delete_saved_preset().
*
* @covers ::execute
*/
public function test_delete_saved_preset() {
$this->resetAfterTest();
// Create course, database activity and users.
$course = $this->getDataGenerator()->create_course();
$data = $this->getDataGenerator()->create_module('data', ['course' => $course->id]);
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$manager = manager::create_from_instance($data);
$initialpresets = $manager->get_available_presets();
$preset1name = 'Admin preset';
$preset2name = 'Teacher preset';
// Trying to delete a preset when there is no saved preset created.
$result = delete_saved_preset::execute($data->id, [$preset1name, $preset2name]);
$result = external_api::clean_returnvalue(delete_saved_preset::execute_returns(), $result);
$this->assertFalse($result['result']);
$this->assertCount(2, $result['warnings']);
// Check no preset has been deleted.
$currentpresets = $manager->get_available_presets();
$this->assertEquals(count($initialpresets), count($currentpresets));
// Create a saved preset.
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$record = (object)[
'name' => $preset1name,
'description' => 'Testing preset description',
];
$adminpreset = $plugingenerator->create_preset($data, $record);
// Update initial preset list.
$initialpresets = $manager->get_available_presets();
// There is a warning for non-existing preset.
$result = delete_saved_preset::execute($data->id, ['Another preset']);
$result = external_api::clean_returnvalue(delete_saved_preset::execute_returns(), $result);
$this->assertFalse($result['result']);
$this->assertCount(1, $result['warnings']);
// Check no preset has been deleted.
$currentpresets = $manager->get_available_presets();
$this->assertEquals(count($initialpresets), count($currentpresets));
// Create a saved preset by teacher.
$this->setUser($teacher);
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$record = (object)[
'name' => $preset2name,
'description' => 'Testing preset description',
];
$teacherpreset = $plugingenerator->create_preset($data, $record);
// Update initial preset list.
$this->setAdminUser();
$initialpresets = $manager->get_available_presets();
// Student can't delete presets.
$this->setUser($student);
$result = delete_saved_preset::execute($data->id, [$preset1name, $preset2name]);
$result = external_api::clean_returnvalue(delete_saved_preset::execute_returns(), $result);
$this->assertFalse($result['result']);
$this->assertCount(2, $result['warnings']);
// Check no preset has been deleted.
$this->setAdminUser();
$currentpresets = $manager->get_available_presets();
$this->assertEquals(count($initialpresets), count($currentpresets));
// Teacher can delete their preset.
$this->setUser($teacher);
$result = delete_saved_preset::execute($data->id, [$preset2name]);
$result = external_api::clean_returnvalue(delete_saved_preset::execute_returns(), $result);
$this->assertTrue($result['result']);
$this->assertCount(0, $result['warnings']);
// Check the preset has been deleted.
$this->setAdminUser();
$currentpresets = $manager->get_available_presets();
$this->assertEquals(count($initialpresets) - 1, count($currentpresets));
foreach ($currentpresets as $currentpreset) {
$this->assertNotEquals($currentpreset->name, $preset2name);
}
// Teacher can't delete other users' preset.
$this->setUser($teacher);
$result = delete_saved_preset::execute($data->id, [$preset1name]);
$result = external_api::clean_returnvalue(delete_saved_preset::execute_returns(), $result);
$this->assertFalse($result['result']);
$this->assertCount(1, $result['warnings']);
// Check no preset has been deleted.
$this->setAdminUser();
$currentpresets = $manager->get_available_presets();
$this->assertEquals(count($initialpresets) - 1, count($currentpresets));
}
}

View File

@ -610,4 +610,120 @@ class preset_test extends \advanced_testcase {
$file = preset::get_file($preset->get_path(), 'unexistingpreset.xml');
$this->assertNull($file);
}
/**
* Test for can_manage().
*
* @covers ::can_manage
*/
public function test_can_manage() {
$this->resetAfterTest();
// Create course, database activity and users.
$course = $this->getDataGenerator()->create_course();
$data = $this->getDataGenerator()->create_module('data', ['course' => $course->id]);
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$manager = manager::create_from_instance($data);
$preset1name = 'Admin preset';
$preset2name = 'Teacher preset';
// Create a saved preset by admin.
$this->setAdminUser();
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$record = (object) [
'name' => $preset1name,
'description' => 'Testing preset description',
];
$adminpreset = $plugingenerator->create_preset($data, $record);
// Create a saved preset by teacher.
$this->setUser($teacher);
$record = (object) [
'name' => $preset2name,
'description' => 'Testing preset description',
];
$teacherpreset = $plugingenerator->create_preset($data, $record);
// Plugins can't be deleted.
$pluginpresets = manager::get_available_plugin_presets();
$pluginpreset = reset($pluginpresets);
$this->assertFalse($pluginpreset->can_manage());
// Admin can delete all saved presets.
$this->setAdminUser();
$this->assertTrue($adminpreset->can_manage());
$this->assertTrue($teacherpreset->can_manage());
// Teacher can delete their own preset only.
$this->setUser($teacher);
$this->assertFalse($adminpreset->can_manage());
$this->assertTrue($teacherpreset->can_manage());
// Student can't delete any of the presets.
$this->setUser($student);
$this->assertFalse($adminpreset->can_manage());
$this->assertFalse($teacherpreset->can_manage());
}
/**
* Test for delete().
*
* @covers ::delete
*/
public function test_delete() {
$this->resetAfterTest();
// Create course, database activity and users.
$course = $this->getDataGenerator()->create_course();
$data = $this->getDataGenerator()->create_module('data', ['course' => $course->id]);
$manager = manager::create_from_instance($data);
$presetname = 'Admin preset';
// Create a saved preset by admin.
$this->setAdminUser();
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$record = (object) [
'name' => $presetname,
'description' => 'Testing preset description',
];
$adminpreset = $plugingenerator->create_preset($data, $record);
$initialpresets = $manager->get_available_presets();
// Plugins can't be deleted.
$pluginpresets = manager::get_available_plugin_presets();
$pluginpreset = reset($pluginpresets);
$result = $pluginpreset->delete();
$currentpluginpresets = manager::get_available_plugin_presets();
$this->assertEquals(count($pluginpresets), count($currentpluginpresets));
$result = $adminpreset->delete();
$this->assertTrue($result);
// After deleting the preset, there is no file linked.
$adminpreset = preset::create_from_instance($manager, $presetname);
$this->assertEmpty($adminpreset->storedfile);
// Check the preset has been deleted.
$currentpresets = $manager->get_available_presets();
$this->assertEquals(count($initialpresets) - 1, count($currentpresets));
// The behavior of trying to delete a preset twice.
$result = $adminpreset->delete();
$this->assertFalse($result);
// Check the preset has not been deleted.
$currentpresets = $manager->get_available_presets();
$this->assertEquals(count($initialpresets) - 1, count($currentpresets));
$emptypreset = preset::create_from_instance($manager, $presetname);
// The behavior of deleting an empty preset.
$result = $emptypreset->delete();
$this->assertFalse($result);
// Check the preset has not been deleted.
$currentpresets = $manager->get_available_presets();
$this->assertEquals(count($initialpresets) - 1, count($currentpresets));
}
}

View File

@ -24,7 +24,7 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2022081600; // The current module version (Date: YYYYMMDDXX).
$plugin->version = 2022082601; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2022041200; // Requires this Moodle version.
$plugin->component = 'mod_data'; // Full name of the plugin (used for diagnostics)
$plugin->cron = 0;