diff --git a/mod/data/amd/build/importpresets.min.js b/mod/data/amd/build/importpresets.min.js new file mode 100644 index 00000000000..0914786d6d0 --- /dev/null +++ b/mod/data/amd/build/importpresets.min.js @@ -0,0 +1,10 @@ +define("mod_data/importpresets",["exports","core_form/modalform","core/notification","core/str"],(function(_exports,_modalform,_notification,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +/** + * Javascript module for importing presets. + * + * @module mod_data/importpreset + * @copyright 2022 Laurent David + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_importPresetButton='[data-action="importpresets"]';_exports.init=()=>{const importPresetButton=document.querySelector(selectors_importPresetButton);importPresetButton.addEventListener("click",(event=>{event.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.get_string)("importpreset","mod_data")},formClass:"mod_data\\form\\import_presets",args:{cmid:importPresetButton.dataset.dataid},saveButtonText:(0,_str.get_string)("importandapply","mod_data")});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.assign(event.detail.url):_notification.default.addNotification({type:"error",message:event.detail.errors.join("
")})})),modalForm.show()}))}})); + +//# sourceMappingURL=importpresets.min.js.map \ No newline at end of file diff --git a/mod/data/amd/build/importpresets.min.js.map b/mod/data/amd/build/importpresets.min.js.map new file mode 100644 index 00000000000..3f14ff27d88 --- /dev/null +++ b/mod/data/amd/build/importpresets.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"importpresets.min.js","sources":["../src/importpresets.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 .\n\n/**\n * Javascript module for importing presets.\n *\n * @module mod_data/importpreset\n * @copyright 2022 Laurent David \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\n\nconst selectors = {\n importPresetButton: '[data-action=\"importpresets\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n const importPresetButton = document.querySelector(selectors.importPresetButton);\n\n importPresetButton.addEventListener('click', event => {\n event.preventDefault();\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('importpreset', 'mod_data'),\n },\n formClass: 'mod_data\\\\form\\\\import_presets',\n args: {cmid: importPresetButton.dataset.dataid},\n saveButtonText: getString('importandapply', 'mod_data'),\n });\n\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.assign(event.detail.url);\n } else {\n Notification.addNotification({\n type: 'error',\n message: event.detail.errors.join('
')\n });\n }\n });\n modalForm.show();\n });\n};\n"],"names":["selectors","importPresetButton","document","querySelector","addEventListener","event","preventDefault","modalForm","ModalForm","modalConfig","title","formClass","args","cmid","dataset","dataid","saveButtonText","events","FORM_SUBMITTED","detail","result","window","location","assign","url","addNotification","type","message","errors","join","show"],"mappings":";;;;;;;0LA2BMA,6BACkB,8CAMJ,WACVC,mBAAqBC,SAASC,cAAcH,8BAElDC,mBAAmBG,iBAAiB,SAASC,QACzCA,MAAMC,uBAEAC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,mBAAU,eAAgB,aAErCC,UAAW,iCACXC,KAAM,CAACC,KAAMZ,mBAAmBa,QAAQC,QACxCC,gBAAgB,mBAAU,iBAAkB,cAGhDT,UAAUH,iBAAiBG,UAAUU,OAAOC,gBAAgBb,QACpDA,MAAMc,OAAOC,OACbC,OAAOC,SAASC,OAAOlB,MAAMc,OAAOK,2BAEvBC,gBAAgB,CACzBC,KAAM,QACNC,QAAStB,MAAMc,OAAOS,OAAOC,KAAK,aAI9CtB,UAAUuB"} \ No newline at end of file diff --git a/mod/data/amd/src/importpresets.js b/mod/data/amd/src/importpresets.js new file mode 100644 index 00000000000..370391f0084 --- /dev/null +++ b/mod/data/amd/src/importpresets.js @@ -0,0 +1,62 @@ +// 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 . + +/** + * Javascript module for importing presets. + * + * @module mod_data/importpreset + * @copyright 2022 Laurent David + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import ModalForm from 'core_form/modalform'; +import Notification from 'core/notification'; +import {get_string as getString} from 'core/str'; + +const selectors = { + importPresetButton: '[data-action="importpresets"]', +}; + +/** + * Initialize module + */ +export const init = () => { + const importPresetButton = document.querySelector(selectors.importPresetButton); + + importPresetButton.addEventListener('click', event => { + event.preventDefault(); + + const modalForm = new ModalForm({ + modalConfig: { + title: getString('importpreset', 'mod_data'), + }, + formClass: 'mod_data\\form\\import_presets', + args: {cmid: importPresetButton.dataset.dataid}, + saveButtonText: getString('importandapply', 'mod_data'), + }); + + modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => { + if (event.detail.result) { + window.location.assign(event.detail.url); + } else { + Notification.addNotification({ + type: 'error', + message: event.detail.errors.join('
') + }); + } + }); + modalForm.show(); + }); +}; diff --git a/mod/data/classes/form/import_presets.php b/mod/data/classes/form/import_presets.php new file mode 100644 index 00000000000..e2a57e25076 --- /dev/null +++ b/mod/data/classes/form/import_presets.php @@ -0,0 +1,115 @@ +. + +namespace mod_data\form; + +use context; +use moodle_exception; +use moodle_url; +use core_form\dynamic_form; + +/** + * Import presets form. + * + * @package mod_data + * @copyright 2022 Laurent David + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class import_presets extends dynamic_form { + + /** + * Process the form submission + * + * @return array + * @throws moodle_exception + */ + public function process_dynamic_submission(): array { + global $CFG; + $filepath = $this->save_temp_file('importfile'); + $context = $this->get_context_for_dynamic_submission(); + $returnurl = new moodle_url('/mod/data/preset.php', [ + 'id' => $context->instanceid, + 'action' => 'importzip', + 'filepath' => str_replace($CFG->tempdir, '', $filepath) + ]); + return [ + 'result' => true, + 'url' => $returnurl->out(false), + ]; + } + + /** + * Get context + * + * @return context + */ + protected function get_context_for_dynamic_submission(): context { + $cmid = $this->optional_param('cmid', null, PARAM_INT); + $cm = get_coursemodule_from_id('data', $cmid); + $context = \context_module::instance($cm->id); + return $context; + } + + /** + * Set data + * + * @return void + */ + public function set_data_for_dynamic_submission(): void { + $data = (object) [ + 'cmid' => $this->optional_param('cmid', 0, PARAM_INT), + ]; + $this->set_data($data); + } + + /** + * Has access ? + * + * @return void + * @throws moodle_exception + */ + protected function check_access_for_dynamic_submission(): void { + if (!has_capability('mod/data:managetemplates', $this->get_context_for_dynamic_submission())) { + throw new moodle_exception('importpresetmissingcapability', 'data'); + } + } + + /** + * Get page URL + * + * @return moodle_url + */ + protected function get_page_url_for_dynamic_submission(): moodle_url { + $cmid = $this->optional_param('cmid', null, PARAM_INT); + return new moodle_url('/mod/data/preset.php', ['id' => $cmid]); + } + + /** + * Form definition + * + * @return void + */ + protected function definition() { + $mform = $this->_form; + $mform->addElement('html', \html_writer::div(get_string('importpreset_desc', 'mod_data'), 'py-3')); + $mform->addElement('hidden', 'cmid'); + $mform->setType('cmid', PARAM_INT); + + $mform->addElement('filepicker', 'importfile', get_string('choosepreset', 'mod_data'), null, + ['accepted_types' => '.zip']); + $mform->addRule('importfile', null, 'required'); + } +} diff --git a/mod/data/classes/local/importer/preset_existing_importer.php b/mod/data/classes/local/importer/preset_existing_importer.php index 92b21c90af3..2431f0f0899 100644 --- a/mod/data/classes/local/importer/preset_existing_importer.php +++ b/mod/data/classes/local/importer/preset_existing_importer.php @@ -51,7 +51,7 @@ class preset_existing_importer extends preset_importer { throw new \coding_exception('Invalid preset provided'); } - $this->userid = $userid; + $this->userid = intval($userid); $this->fullname = $fullname; $cm = $manager->get_coursemodule(); $course = $cm->get_course(); @@ -62,9 +62,9 @@ class preset_existing_importer extends preset_importer { /** * Returns user ID * - * @return int|string userid or empty string + * @return int userid */ - public function get_userid() { + public function get_userid(): int { return $this->userid; } diff --git a/mod/data/classes/local/importer/preset_importer.php b/mod/data/classes/local/importer/preset_importer.php index c5e48682847..12a9b7c1729 100644 --- a/mod/data/classes/local/importer/preset_importer.php +++ b/mod/data/classes/local/importer/preset_importer.php @@ -354,4 +354,48 @@ abstract class preset_importer { public function get_preset_selector(): array { return ['name' => 'directory', 'value' => $this->get_directory()]; } + + /** + * Helper function to finish up the import routine. + * + * Called from fields and presets pages. + * + * @param bool $overwritesettings Whether to overwrite activity settings or not. + * @param stdClass $instance database instance object + * @return void + */ + public function finish_import_process(bool $overwritesettings, stdClass $instance): void { + global $DB; + $this->import($overwritesettings); + $strimportsuccess = get_string('importsuccess', 'data'); + $straddentries = get_string('addentries', 'data'); + $strtodatabase = get_string('todatabase', 'data'); + if (!$DB->get_records('data_records', ['dataid' => $instance->id])) { + \core\notification::success("$strimportsuccess $straddentries $strtodatabase"); + } else { + \core\notification::success($strimportsuccess); + } + } + + /** + * Get the right importer instance from the provided parameters (POST or GET) + * + * @param manager $manager the current database manager + * @return preset_importer the relevant preset_importer instance + * @throws \moodle_exception when the file provided as parameter (POST or GET) does not exist + */ + public static function create_from_parameters(manager $manager): preset_importer { + global $CFG; + $fullname = optional_param('fullname', '', PARAM_PATH); // Directory the preset is in. + 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 preset_upload_importer($manager, $presetdir); + } else { + $importer = new preset_existing_importer($manager, $fullname); + } + return $importer; + } } diff --git a/mod/data/classes/output/action_bar.php b/mod/data/classes/output/action_bar.php index ae89ecaa58a..429b3f2b7c9 100644 --- a/mod/data/classes/output/action_bar.php +++ b/mod/data/classes/output/action_bar.php @@ -32,6 +32,9 @@ class action_bar { /** @var int $id The database module id. */ private $id; + /** @var int $cmid The database course module id. */ + private $cmid; + /** @var moodle_url $currenturl The URL of the current page. */ private $currenturl; @@ -43,6 +46,8 @@ class action_bar { */ public function __construct(int $id, moodle_url $pageurl) { $this->id = $id; + [$course, $cm] = get_course_and_cm_from_instance($this->id, 'data'); + $this->cmid = $cm->id; $this->currenturl = $pageurl; } @@ -61,9 +66,12 @@ class action_bar { ): string { global $PAGE, $DB; - $createfieldlink = new moodle_url('/mod/data/field.php', ['d' => $this->id]); + $createfieldlink = new moodle_url('/mod/data/field.php', ['id' => $this->cmid]); + $presetslink = new moodle_url('/mod/data/preset.php', ['id' => $this->cmid]); + $menu = [ $createfieldlink->out(false) => get_string('managefields', 'mod_data'), + $presetslink->out(false) => get_string('usestandard', 'mod_data'), ]; $selected = $createfieldlink->out(false); @@ -229,8 +237,10 @@ class action_bar { global $PAGE; $renderer = $PAGE->get_renderer('mod_data'); - $presetsactionbar = new presets_action_bar($this->id); - + if (!has_capability('mod/data:managetemplates', \context_module::instance($this->cmid))) { + return ''; + } + $presetsactionbar = new presets_action_bar($this->cmid); return $renderer->render_presets_action_bar($presetsactionbar); } diff --git a/mod/data/classes/output/presets.php b/mod/data/classes/output/presets.php index 29cb8edf2cc..87dedc5f236 100644 --- a/mod/data/classes/output/presets.php +++ b/mod/data/classes/output/presets.php @@ -18,6 +18,7 @@ namespace mod_data\output; use action_menu; use action_menu_link_secondary; +use mod_data\manager; use mod_data\preset; use moodle_url; use templatable; @@ -34,8 +35,8 @@ use stdClass; */ class presets implements templatable, renderable { - /** @var int $id The database module id. */ - private $id; + /** @var manager $manager The database module manager. */ + private $manager; /** @var array $presets The array containing the existing presets. */ private $presets; @@ -49,13 +50,13 @@ class presets implements templatable, renderable { /** * The class constructor. * - * @param int $id The database module id + * @param manager $manager The database manager * @param array $presets The array containing the existing presets - * @param moodle_url $formactionurl The the action url for the form + * @param moodle_url $formactionurl The action url for the form * @param bool $manage Whether the manage preset options should be displayed */ - public function __construct(int $id, array $presets, moodle_url $formactionurl, bool $manage = false) { - $this->id = $id; + public function __construct(manager $manager, array $presets, moodle_url $formactionurl, bool $manage = false) { + $this->manager = $manager; $this->presets = $presets; $this->formactionurl = $formactionurl; $this->manage = $manage; @@ -68,10 +69,9 @@ class presets implements templatable, renderable { * @return array */ public function export_for_template(renderer_base $output): array { - $presets = $this->get_presets($output); return [ - 'd' => $this->id, + 'id' => $this->manager->get_coursemodule()->id, 'formactionurl' => $this->formactionurl->out(), 'showmanage' => $this->manage, 'presets' => $presets, @@ -101,13 +101,14 @@ class presets implements templatable, renderable { $actions = $this->get_preset_action_menu($output, $preset, $userid); $fullname = "{$userid}/{$preset->shortname}"; + $id = $this->manager->get_instance()->id; $previewurl = new moodle_url( - '/mod/data/preset.php', - ['d' => $this->id, 'fullname' => $fullname, 'action' => 'preview'] + '/mod/data/preset.php', + ['d' => $id, 'fullname' => $fullname, 'action' => 'preview'] ); $presets[] = [ - 'id' => $this->id, + 'id' => $id, 'name' => $preset->name, 'url' => $previewurl->out(), 'shortname' => $preset->shortname, @@ -130,10 +131,10 @@ class presets implements templatable, renderable { * @return stdClass the resulting action menu */ private function get_preset_action_menu(renderer_base $output, $preset, ?int $userid): stdClass { - global $PAGE; $actions = new stdClass(); $actionmenu = null; + $id = $this->manager->get_instance()->id; // Only presets saved by users can be edited or removed (so the datapreset plugins shouldn't display these buttons). if ($this->manage && !$preset->isplugin) { $actionmenu = new action_menu(); @@ -146,13 +147,13 @@ class presets implements templatable, renderable { // Edit. if ($canmanage) { $params = [ - 'd' => $this->id, + 'd' => $id, 'action' => 'edit', ]; $editactionurl = new moodle_url('/mod/data/preset.php', $params); $attributes = [ 'data-action' => 'editpreset', - 'data-dataid' => $this->id, + 'data-dataid' => $id, "data-presetname" => $preset->name, "data-presetdescription" => $preset->description, ]; @@ -167,7 +168,7 @@ class presets implements templatable, renderable { // Export. $params = [ - 'd' => $this->id, + 'd' => $id, 'presetname' => $preset->name, 'action' => 'export', ]; @@ -181,13 +182,13 @@ class presets implements templatable, renderable { // Delete. if ($canmanage) { $params = [ - 'd' => $this->id, + 'd' => $id, 'action' => 'delete', ]; $deleteactionurl = new moodle_url('/mod/data/preset.php', $params); $attributes = [ 'data-action' => 'deletepreset', - 'data-dataid' => $this->id, + 'data-dataid' => $id, "data-presetname" => $preset->name, ]; $actionmenu->add(new action_menu_link_secondary( diff --git a/mod/data/classes/output/presets_action_bar.php b/mod/data/classes/output/presets_action_bar.php index b34a6975382..36d335a49b5 100644 --- a/mod/data/classes/output/presets_action_bar.php +++ b/mod/data/classes/output/presets_action_bar.php @@ -30,15 +30,15 @@ use renderable; class presets_action_bar implements templatable, renderable { /** @var int $id The database module id. */ - private $id; + private $cmid; /** * The class constructor. * - * @param int $id The database module id + * @param int $cmid The database module id */ - public function __construct(int $id) { - $this->id = $id; + public function __construct(int $cmid) { + $this->cmid = $cmid; } /** @@ -48,9 +48,11 @@ class presets_action_bar implements templatable, renderable { * @return array */ public function export_for_template(\renderer_base $output): array { - $importpresetlink = new moodle_url('/mod/data/preset.php', ['d' => $this->id, 'action' => 'import']); - + $importpresetlink = new moodle_url('/mod/data/preset.php', [ + 'id' => $this->cmid, 'action' => 'import' + ]); return [ + 'id' => $this->cmid, 'importpreseturl' => $importpresetlink->out(false), ]; } diff --git a/mod/data/classes/output/zero_state_action_bar.php b/mod/data/classes/output/zero_state_action_bar.php index d92ea1a4baa..5c4632a5237 100644 --- a/mod/data/classes/output/zero_state_action_bar.php +++ b/mod/data/classes/output/zero_state_action_bar.php @@ -53,8 +53,9 @@ class zero_state_action_bar implements templatable, renderable { $data = []; if ($this->manager->can_manage_templates()) { + $cm = $this->manager->get_coursemodule(); $instance = $this->manager->get_instance(); - $params = ['d' => $instance->id, 'backto' => $PAGE->url->out(false)]; + $params = ['id' => $cm->id, 'backto' => $PAGE->url->out(false)]; $usepresetlink = new moodle_url('/mod/data/preset.php', $params); $usepresetbutton = new \single_button($usepresetlink, @@ -68,7 +69,10 @@ class zero_state_action_bar implements templatable, renderable { $params['action'] = 'import'; $importpresetlink = new moodle_url('/mod/data/preset.php', $params); $importpresetbutton = new \single_button($importpresetlink, - get_string('importpreset', 'mod_data'), 'get', false); + get_string('importpreset', 'mod_data'), 'get', false, [ + 'data-action' => 'importpresets', + 'data-dataid' => $cm->id, + ]); $data['importpresetbutton'] = $importpresetbutton->export_for_template($output); } diff --git a/mod/data/field.php b/mod/data/field.php index e27cc863d94..d3c773c1305 100644 --- a/mod/data/field.php +++ b/mod/data/field.php @@ -23,9 +23,11 @@ * @package mod_data */ -use mod_data\manager; +use core\notification; use mod_data\local\importer\preset_existing_importer; +use mod_data\local\importer\preset_importer; use mod_data\local\importer\preset_upload_importer; +use mod_data\manager; require_once('../../config.php'); require_once('lib.php'); @@ -88,9 +90,6 @@ $context = $manager->get_context(); require_login($course, true, $cm); require_capability('mod/data:managetemplates', $context); -$formimportzip = new data_import_preset_zip_form(); -$formimportzip->set_data(array('d' => $data->id)); - $actionbar = new \mod_data\output\action_bar($data->id, $PAGE->url); $PAGE->add_body_class('mediumwidth'); @@ -111,34 +110,11 @@ $renderer = $manager->get_renderer(); if ($action == 'finishimport' && confirm_sesskey()) { data_print_header($course, $cm, $data, false); $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', 'error'); - } - $importer = new preset_upload_importer($manager, $presetdir); - } else { - $importer = new preset_existing_importer($manager, $fullname); - } - - if ($importer->needs_mapping()) { - $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 $straddentries $strtodatabase", - 'notifysuccess'); - } else { - echo $OUTPUT->notification("$strimportsuccess", 'notifysuccess'); - } - - echo $OUTPUT->continue_button(new moodle_url('/mod/data/field.php', ['d' => $data->id])); - echo $OUTPUT->footer(); - exit; - } + $importer = preset_importer::create_from_parameters($manager); + $importer->finish_import_process($overwritesettings, $data); + echo $OUTPUT->continue_button(new moodle_url('/mod/data/field.php', ['d' => $data->id])); + echo $OUTPUT->footer(); + exit; } switch ($mode) { @@ -278,13 +254,13 @@ switch ($mode) { break; case 'usepreset': - $importer = new preset_existing_importer($manager, $fullname); + $importer = preset_importer::create_from_parameters($manager); if (!$importer->needs_mapping()) { $backurl = new moodle_url('/mod/data/field.php', ['id' => $cm->id]); if ($importer->import(false)) { - \core\notification::success(get_string('importsuccess', 'mod_data')); + notification::success(get_string('importsuccess', 'mod_data')); } else { - \core\notification::error(get_string('presetapplied', 'mod_data')); + notification::error(get_string('presetapplied', 'mod_data')); } redirect($backurl); } diff --git a/mod/data/lang/en/data.php b/mod/data/lang/en/data.php index f27a4c6afd3..6e1bb8af354 100644 --- a/mod/data/lang/en/data.php +++ b/mod/data/lang/en/data.php @@ -213,10 +213,12 @@ $string['id'] = 'Entry ID'; $string['chooseexportfields'] = 'Choose the fields you wish to export'; $string['chooseexportformat'] = 'Choose the format you wish to export to'; $string['chooseorupload'] = 'Choose file'; +$string['choosepreset'] = 'Preset file'; $string['expired'] = 'Sorry, this activity closed on {$a} and is no longer available'; $string['importentries'] = 'Import entries'; $string['importpreset'] = 'Import a preset'; $string['importsuccess'] = 'The preset has been successfully applied.'; +$string['importpresetmissingcapability'] = 'You don\'t have permission to import a preset.'; $string['includeapproval'] = 'Include approval status'; $string['includetags'] = 'Include tags'; $string['includetime'] = 'Include time added/modified'; @@ -246,6 +248,9 @@ $string['unknown'] = 'Unknown field'; $string['invalidpreset'] = '{$a} is not a preset.'; $string['invalidrecord'] = 'Incorrect record'; $string['invalidurl'] = 'The URL you just entered is not valid'; +$string['importandapply'] = 'Import preset and apply'; +$string['importpreset_desc'] = 'The preset will be applied to this activity, creating fields and templates. + It won\'t appear in the list of presets.'; $string['jstemplate'] = 'Javascript template'; $string['latitude'] = 'Latitude'; $string['latlong'] = 'Coordinates'; diff --git a/mod/data/lib.php b/mod/data/lib.php index 48255711b44..6b0de3b1a0a 100644 --- a/mod/data/lib.php +++ b/mod/data/lib.php @@ -2625,6 +2625,7 @@ class data_preset_upload_importer extends data_preset_importer { } parent::__construct($course, $cm, $module, $filepath); } + public function cleanup() { return fulldelete($this->directory); } diff --git a/mod/data/preset.php b/mod/data/preset.php index a0470897fa9..2aba66f36cb 100644 --- a/mod/data/preset.php +++ b/mod/data/preset.php @@ -28,12 +28,12 @@ * @package mod_data */ +use mod_data\local\importer\preset_importer; +use mod_data\local\importer\preset_upload_importer; use mod_data\manager; use mod_data\preset; use mod_data\output\action_bar; use mod_data\output\preset_preview; -use mod_data\local\importer\preset_upload_importer; -use mod_data\local\importer\preset_existing_importer; require_once('../../config.php'); require_once($CFG->dirroot.'/mod/data/lib.php'); @@ -57,7 +57,7 @@ if ($id) { } $action = optional_param('action', 'view', PARAM_ALPHA); // The page action. -$allowedactions = ['view', 'import', 'importzip', 'finishimport', +$allowedactions = ['view', 'importzip', 'finishimport', 'export', 'preview']; if (!in_array($action, $allowedactions)) { throw new moodle_exception('invalidaccess'); @@ -113,11 +113,21 @@ if ($action === 'export') { exit(0); } -$formimportzip = new data_import_preset_zip_form(); -$formimportzip->set_data(array('d' => $data->id)); -if ($formimportzip->is_cancelled()) { - redirect(new moodle_url('/mod/data/preset.php', ['d' => $data->id])); +if ($action == 'importzip') { + $filepath = optional_param('filepath', '', PARAM_PATH); + $importer = new preset_upload_importer($manager, $CFG->tempdir . $filepath); + if ($importer->needs_mapping()) { + echo $OUTPUT->header(); + echo $OUTPUT->heading(get_string('importpreset', 'data'), 2, 'mb-4'); + echo $renderer->importing_preset($data, $importer); + echo $OUTPUT->footer(); + exit(0); + } + $importer->import(false); + core\notification::success(get_string('importsuccess', 'mod_data')); + redirect(new moodle_url('/mod/data/field.php', ['id' => $cm->id])); + exit(0); } // Preset preview injects CSS and JS to the page and should be done before the page header. @@ -142,55 +152,15 @@ if ($action === 'preview') { exit(0); } -if ($formdata = $formimportzip->get_data()) { - $file = new stdClass; - $file->name = $formimportzip->get_new_filename('importfile'); - $file->path = $formimportzip->save_temp_file('importfile'); - $importer = new preset_upload_importer($manager, $file->path); - if ($importer->needs_mapping()) { - echo $OUTPUT->header(); - echo $OUTPUT->heading(get_string('importpreset', 'data'), 2, 'mb-4'); - echo $renderer->importing_preset($data, $importer); - echo $OUTPUT->footer(); - exit(0); - } - - $importer->import(false); - redirect(new moodle_url('/mod/data/field.php', ['id' => $cm->id])); -} - echo $OUTPUT->header(); 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); - $userid = empty($parts[0]) ? 0 : (int)$parts[0]; - $shortname = empty($parts[1]) ? '' : $parts[1]; - echo html_writer::start_div('overflow-hidden'); - 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 preset_upload_importer($manager, $presetdir); - } else { - $importer = new preset_existing_importer($manager, $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 $straddentries $strtodatabase", 'notifysuccess'); - } else { - echo $OUTPUT->notification("$strimportsuccess", 'notifysuccess'); - } + $importer = preset_importer::create_from_parameters($manager); + $importer->finish_import_process($overwritesettings, $data); echo $OUTPUT->continue_button(new moodle_url('/mod/data/preset.php', ['d' => $data->id])); echo html_writer::end_div(); @@ -198,15 +168,10 @@ if ($action === 'finishimport') { exit(0); } -if ($action === 'import') { - echo $OUTPUT->heading(get_string('importpreset', 'data'), 2, 'mb-4'); - echo $formimportzip->display(); -} else { - $actionbar = new \mod_data\output\action_bar($data->id, $url); - echo $actionbar->get_presets_action_bar(); - echo $OUTPUT->heading(get_string('presets', 'data'), 2, 'mb-4'); - $presets = new \mod_data\output\presets($data->id, $presets, new \moodle_url('/mod/data/field.php'), true); - echo $renderer->render_presets($presets); -} +$actionbar = new \mod_data\output\action_bar($data->id, $url); +echo $actionbar->get_presets_action_bar(); +echo $OUTPUT->heading(get_string('presets', 'data'), 2, 'mb-4'); +$presets = new \mod_data\output\presets($manager, $presets, new \moodle_url('/mod/data/field.php'), true); +echo $renderer->render_presets($presets); echo $OUTPUT->footer(); diff --git a/mod/data/preset_form.php b/mod/data/preset_form.php index d0dd0e78de3..1140f1ddacf 100644 --- a/mod/data/preset_form.php +++ b/mod/data/preset_form.php @@ -46,7 +46,22 @@ class data_existing_preset_form extends moodleform { } } +/** + * Import preset class + * + * + * @package mod_data + * @deprecated since 4.1 This is deprecated since MDL-75188, please use the dynamic_form + * form (\mod_data\form\import_presets) + * @todo MDL-75189 This will be deleted in Moodle 4.5. + */ class data_import_preset_zip_form extends moodleform { + /** + * Form definition + * + * @return void + * @throws coding_exception + */ public function definition() { $this->_form->addElement('header', 'uploadpreset', get_string('fromfile', 'data')); $this->_form->addHelpButton('uploadpreset', 'fromfile', 'data'); diff --git a/mod/data/renderer.php b/mod/data/renderer.php index 2dbb802a55c..9ea5431982a 100644 --- a/mod/data/renderer.php +++ b/mod/data/renderer.php @@ -63,17 +63,17 @@ class mod_data_renderer extends plugin_renderer_base { $newfields = $params->importfields; $currentfields = $params->currentfields; - $html = html_writer::start_tag('div', ['class'=>'presetmapping']); - $html .= html_writer::start_tag('form', ['method'=>'post', 'action'=>'']); + $html = html_writer::start_tag('div', ['class' => 'presetmapping']); + $html .= html_writer::start_tag('form', ['method' => 'post', 'action' => '']); $html .= html_writer::start_tag('div'); - $html .= html_writer::empty_tag('input', ['type'=>'hidden', 'name'=>'action', 'value'=>'finishimport']); - $html .= html_writer::empty_tag('input', ['type'=>'hidden', 'name'=>'sesskey', 'value'=>sesskey()]); - $html .= html_writer::empty_tag('input', ['type'=>'hidden', 'name'=>'d', 'value'=>$datamodule->id]); + $html .= html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'action', 'value' => 'finishimport']); + $html .= html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()]); + $html .= html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'd', 'value' => $datamodule->id]); $inputselector = $importer->get_preset_selector(); $html .= html_writer::empty_tag( - 'input', - ['type'=>'hidden', 'name'=> $inputselector['name'], 'value' => $inputselector['value']] + 'input', + ['type' => 'hidden', 'name' => $inputselector['name'], 'value' => $inputselector['value']] ); if (!empty($newfields)) { diff --git a/mod/data/templates/presets.mustache b/mod/data/templates/presets.mustache index 0df8adc93b6..270f7dac7c5 100644 --- a/mod/data/templates/presets.mustache +++ b/mod/data/templates/presets.mustache @@ -18,13 +18,13 @@ Context variables required for this template: * formactionurl - The form action url. - * d - The database id. + * id - The database course module id (cmid). * presets - List of presets containing id, name, fullname, shortname and actions. Example context (json): { "formactionurl": "http://www.example.com", - "d": 1, + "id": 1, "presets": [ { "id": 1, @@ -51,7 +51,7 @@ {{#str}}presetshelp, mod_data{{/str}}
- + diff --git a/mod/data/templates/presets_action_bar.mustache b/mod/data/templates/presets_action_bar.mustache index 124aae6eaae..cf2cef09cb3 100644 --- a/mod/data/templates/presets_action_bar.mustache +++ b/mod/data/templates/presets_action_bar.mustache @@ -18,16 +18,23 @@ Context variables required for this template: * importpreseturl - The url to the import preset page. + * id - The database course module id (cmid). Example context (json): { - "importpreseturl": "http://www.example.com" + "importpreseturl": "http://www.example.com", + "id" : 1 } }}
+{{#js}} + require(['mod_data/importpresets'], function(importPresetsModal) { + importPresetsModal.init(); + }); +{{/js}}
diff --git a/mod/data/templates/zero_state.mustache b/mod/data/templates/zero_state.mustache index 8236aea57b6..fd65758acd1 100644 --- a/mod/data/templates/zero_state.mustache +++ b/mod/data/templates/zero_state.mustache @@ -25,6 +25,7 @@ Example context (json): { "noitemsimgurl": "https://moodlesite/theme/image.php/boost/mod_data/1535727318/nofields", + "title": "Title", "importpresetbutton": { "id" : "id1", "method" : "get", @@ -92,4 +93,9 @@ {{>core/single_button}} {{/usepresetbutton}} +{{#js}} + require(['mod_data/importpresets'], function(importPresetsModal) { + importPresetsModal.init(); + }); +{{/js}} diff --git a/mod/data/tests/behat/data_presets.feature b/mod/data/tests/behat/data_presets.feature index aea0d15eee1..7039da43ec8 100644 --- a/mod/data/tests/behat/data_presets.feature +++ b/mod/data/tests/behat/data_presets.feature @@ -301,3 +301,20 @@ Feature: Users can view and manage data presets And I open the action menu in "Saved preset 1" "table_row" And I should see "Export" And following "Export" "link" in the "Saved preset 1" "table_row" should download between "1" and "5000" bytes + + @javascript @_file_upload + Scenario Outline: Admins and Teachers can load a preset from a file + Given I am on the "Mountain landscapes" "data activity" page logged in as + When I follow "Presets" + Then I click on "Import" "link" + And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager + Then I click on "Import preset and apply" "button" in the ".modal-dialog" "css_element" + Then I should see "The preset has been successfully applied." + # I am on the field page. + And I should see "Manage fields" + Then I should see "The preset has been successfully applied." + + Examples: + | user | + | admin | + | teacher1 | diff --git a/mod/data/tests/behat/import_presets.feature b/mod/data/tests/behat/import_presets.feature index b57e7ea5145..0c4637cb5e6 100644 --- a/mod/data/tests/behat/import_presets.feature +++ b/mod/data/tests/behat/import_presets.feature @@ -26,8 +26,8 @@ Feature: Users can import presets Given I am on the "Mountain landscapes" "data activity" page logged in as teacher1 And I follow "Presets" And I click on "Import" "link" - And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Choose file" filemanager - When I click on "Save" "button" + And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager + When I click on "Import preset and apply" "button" Then I should not see "Field mappings" And I should see "Image" in the "image" "table_row" @@ -38,8 +38,8 @@ Feature: Users can import presets And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 And I follow "Presets" And I click on "Import" "link" - And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Choose file" filemanager - When I click on "Save" "button" + And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager + When I click on "Import preset and apply" "button" Then I should see "Field mappings" And I should see "image" And I should see "Create a new field" in the "image" "table_row" @@ -61,8 +61,8 @@ Feature: Users can import presets And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 And I follow "Presets" And I click on "Import" "link" - And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Choose file" filemanager - When I click on "Save" "button" + And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager + When I click on "Import preset and apply" "button" Then I should see "Field mappings" And I should see "image" And I should see "Create a new field" in the "image" "table_row" @@ -71,8 +71,8 @@ Feature: Users can import presets Given I am on the "Mountain landscapes" "data activity" page logged in as teacher1 And I follow "Presets" And I click on "Import" "button" - And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Choose file" filemanager - When I click on "Save" "button" + And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager + When I click on "Import preset and apply" "button" Then I should not see "Field mappings" And I should see "Image" in the "image" "table_row" @@ -83,8 +83,8 @@ Feature: Users can import presets And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 And I follow "Presets" And I click on "Import" "button" - And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Choose file" filemanager - When I click on "Save" "button" + And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager + When I click on "Import preset and apply" "button" Then I should see "Field mappings" And I should see "image" And I should see "Create a new field" in the "image" "table_row" @@ -106,8 +106,8 @@ Feature: Users can import presets And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 And I follow "Presets" And I click on "Import" "button" - And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Choose file" filemanager - When I click on "Save" "button" + And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager + When I click on "Import preset and apply" "button" Then I should see "Field mappings" And I should see "image" And I should see "Create a new field" in the "image" "table_row" @@ -115,7 +115,7 @@ Feature: Users can import presets Scenario: Teacher can import from zero state page on an empty database Given I am on the "Mountain landscapes" "data activity" page logged in as teacher1 And I click on "Import a preset" "button" - And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Choose file" filemanager - When I click on "Save" "button" + And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager + When I click on "Import preset and apply" "button" Then I should not see "Field mappings" And I should see "Image" in the "image" "table_row" diff --git a/mod/data/tests/behat/preview_preset.feature b/mod/data/tests/behat/preview_preset.feature index 0325bb65acf..af3c3b25286 100644 --- a/mod/data/tests/behat/preview_preset.feature +++ b/mod/data/tests/behat/preview_preset.feature @@ -26,8 +26,8 @@ Feature: Users can preview presets Scenario: Preview a user preset as list view template in database Given I follow "Presets" And I click on "Import" "button" - And I upload "mod/data/tests/fixtures/behat_preset.zip" file to "Choose file" filemanager - And I click on "Save" "button" + And I upload "mod/data/tests/fixtures/behat_preset.zip" file to "Preset file" filemanager + When I click on "Import preset and apply" "button" And I follow "Templates" And I click on "Save as preset" "button" And I set the field "Name" to "Saved preset by teacher1" @@ -72,8 +72,8 @@ Feature: Users can preview presets Scenario: Preview a user preset as single view template in database Given I follow "Presets" And I click on "Import" "button" - And I upload "mod/data/tests/fixtures/behat_preset.zip" file to "Choose file" filemanager - And I click on "Save" "button" + And I upload "mod/data/tests/fixtures/behat_preset.zip" file to "Preset file" filemanager + When I click on "Import preset and apply" "button" And I follow "Templates" And I click on "Save as preset" "button" And I set the field "Name" to "Saved preset by teacher1" @@ -147,8 +147,8 @@ Feature: Users can preview presets Scenario: Apply user preset from preview in database Given I follow "Presets" And I click on "Import" "button" - And I upload "mod/data/tests/fixtures/behat_preset.zip" file to "Choose file" filemanager - And I click on "Save" "button" + And I upload "mod/data/tests/fixtures/behat_preset.zip" file to "Preset file" filemanager + When I click on "Import preset and apply" "button" And I follow "Templates" And I click on "Save as preset" "button" And I set the field "Name" to "Saved preset by teacher1" diff --git a/mod/data/tests/behat/use_presets.feature b/mod/data/tests/behat/use_presets.feature new file mode 100644 index 00000000000..16d18520773 --- /dev/null +++ b/mod/data/tests/behat/use_presets.feature @@ -0,0 +1,71 @@ +@mod @mod_data @javascript @_file_upload +Feature: Users can use predefined presets + In order to use presets + As a user + I need to select an existing preset + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | data | Mountain landscapes | n | C1 | data1 | + And the following "mod_data > presets" exist: + | database | name | description | + | data1 | Saved preset 1 | The preset1 has description | + | data1 | Saved preset 2 | | + + Scenario: Teacher can use a preset from field page on a database with fields + Given the following "mod_data > fields" exist: + | database | type | name | description | + | data1 | text | Test field name | Test field description | + Given I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Fields" + And I set the field "Fields tertiary navigation" to "Use a preset" + And I click on "fullname" "radio" in the "Image gallery" "table_row" + And the "Use a preset" "button" should be enabled + Then I click on "Use a preset" "button" + Then I should see "Field mappings" + And I should see "image" + And I should see "Create a new field" in the "image" "table_row" + + Scenario: Teacher can use a preset from field page on a database with entries + And the following "mod_data > fields" exist: + | database | type | name | description | + | data1 | text | field1 | Test field description | + And the following "mod_data > templates" exist: + | database | name | + | data1 | singletemplate | + | data1 | listtemplate | + | data1 | addtemplate | + | data1 | asearchtemplate | + | data1 | rsstemplate | + And the following "mod_data > entries" exist: + | database | field1 | + | data1 | Student entry 1 | + Given I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Fields" + And I set the field "Fields tertiary navigation" to "Use a preset" + And I click on "fullname" "radio" in the "Image gallery" "table_row" + And the "Use a preset" "button" should be enabled + Then I click on "Use a preset" "button" + Then I should see "Field mappings" + And I should see "image" + And I should see "Create a new field" in the "image" "table_row" + + Scenario: Teacher can use a preset from zero state page on an empty database + Given I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I click on "Use a preset" "button" + And I click on "fullname" "radio" in the "Image gallery" "table_row" + And the "Use a preset" "button" should be enabled + Then I click on "Use a preset" "button" + Then I should not see "Field mappings" + And I should see "image" + And I should see "Image" in the "image" "table_row" diff --git a/mod/data/tests/behat/zero_state.feature b/mod/data/tests/behat/zero_state.feature index 9bfdac7f624..7c4ccb49fd2 100644 --- a/mod/data/tests/behat/zero_state.feature +++ b/mod/data/tests/behat/zero_state.feature @@ -1,4 +1,4 @@ -@mod @mod_data @javascript +@mod @mod_data Feature: Zero state page (no fields created) Background: @@ -18,8 +18,6 @@ Feature: Zero state page (no fields created) Scenario: Teachers see buttons to manage database when there is no field created on view page Given I am on the "Test database name" "data activity" page logged in as "teacher1" And "Import a preset" "button" should exist - When I click on "Import a preset" "button" - Then I should see "Import from zip file" And I am on the "Test database name" "data activity" page And "Create a field" "button" should exist And I click on "Create a field" "button" @@ -30,11 +28,12 @@ Feature: Zero state page (no fields created) And I click on "Use a preset" "button" And I should see "Presets" + @javascript Scenario: Teachers see buttons to manage database when there is no field created on templates page Given I am on the "Test database name" "data activity" page logged in as "teacher1" And "Import a preset" "button" should exist When I click on "Import a preset" "button" - Then I should see "Import from zip file" + Then I should see "Preset file" And I am on the "Test database name" "data activity" page And I click on "Templates" "link" And "Create a field" "button" should exist @@ -47,13 +46,12 @@ Feature: Zero state page (no fields created) And I click on "Use a preset" "button" And I should see "Presets" - Scenario: Teachers see buttons to manage database when there is no field created on fields page + @javascript @_file_upload + Scenario: Teachers can import preset from the zero state page Given I am on the "Test database name" "data activity" page logged in as "teacher1" - And I click on "Fields" "link" - And "Import a preset" "button" should not exist - And "Use a preset" "button" should not exist - And "Create a field" "button" should exist - Then I should see "No fields yet" - And I click on "Create a field" "button" - And I click on "Short text" "link" - And I should see "Create a field" + And "Import a preset" "button" should exist + When I click on "Import a preset" "button" + And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager + Then I click on "Import preset and apply" "button" in the ".modal-dialog" "css_element" + And I should see "Manage fields" + Then I should see "The preset has been successfully applied." diff --git a/mod/data/tests/fixtures/image_gallery_preset.zip b/mod/data/tests/fixtures/image_gallery_preset.zip index b11e53698b3..7d0cebf731b 100644 Binary files a/mod/data/tests/fixtures/image_gallery_preset.zip and b/mod/data/tests/fixtures/image_gallery_preset.zip differ