diff --git a/mod/data/amd/build/selectpreset.min.js b/mod/data/amd/build/selectpreset.min.js index 87f886a27b4..9e64d02194d 100644 --- a/mod/data/amd/build/selectpreset.min.js +++ b/mod/data/amd/build/selectpreset.min.js @@ -1,10 +1,11 @@ -define("mod_data/selectpreset",["exports","core/notification","core/str"],(function(_exports,_notification,_str){var obj; +define("mod_data/selectpreset",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0; /** * Javascript module to control the form responsible for selecting a preset. * * @module mod_data/selectpreset * @copyright 2021 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};const selectors_selectPresetButton='input[name="selectpreset"]',selectors_selectedPresetRadioButton='input[name="fullname"]:checked';_exports.init=()=>{document.querySelector(selectors_selectPresetButton).addEventListener("click",(event=>{if(event.preventDefault(),document.querySelectorAll(selectors_selectedPresetRadioButton).length>0){event.target.closest("form").submit()}else(0,_str.get_string)("presetnotselected","mod_data").then((str=>_notification.default.addNotification({type:"error",message:str}))).catch(_notification.default.exception)}))}})); + */ +const selectors_presetRadioButton='input[name="fullname"]',selectors_selectPresetButton='input[name="selectpreset"]',selectors_selectedPresetRadioButton='input[name="fullname"]:checked';_exports.init=()=>{const radioButton=document.querySelectorAll(selectors_presetRadioButton);disableUsePresetButton(),radioButton.forEach((elem=>{elem.addEventListener("change",(function(event){event.preventDefault(),disableUsePresetButton()}))}))};const disableUsePresetButton=()=>{let selectPresetButton=document.querySelector(selectors_selectPresetButton);document.querySelectorAll(selectors_selectedPresetRadioButton).length>0?(selectPresetButton.removeAttribute("disabled"),selectPresetButton.classList.remove("btn-secondary"),selectPresetButton.classList.add("btn-primary")):(selectPresetButton.setAttribute("disabled",!0),selectPresetButton.classList.remove("btn-primary"),selectPresetButton.classList.add("btn-secondary"))}})); //# sourceMappingURL=selectpreset.min.js.map \ No newline at end of file diff --git a/mod/data/amd/build/selectpreset.min.js.map b/mod/data/amd/build/selectpreset.min.js.map index 2c640a2e26d..6ec272a58b0 100644 --- a/mod/data/amd/build/selectpreset.min.js.map +++ b/mod/data/amd/build/selectpreset.min.js.map @@ -1 +1 @@ -{"version":3,"file":"selectpreset.min.js","sources":["../src/selectpreset.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 to control the form responsible for selecting a preset.\n *\n * @module mod_data/selectpreset\n * @copyright 2021 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\n\nconst selectors = {\n selectPresetButton: 'input[name=\"selectpreset\"]',\n selectedPresetRadioButton: 'input[name=\"fullname\"]:checked',\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n const selectPresetButton = document.querySelector(selectors.selectPresetButton);\n\n selectPresetButton.addEventListener('click', event => {\n event.preventDefault();\n // Validate whether there is a selected preset before submitting the form.\n if (document.querySelectorAll(selectors.selectedPresetRadioButton).length > 0) {\n const presetsForm = event.target.closest('form');\n presetsForm.submit();\n } else {\n // No selected presets. Display an error message to user.\n getString('presetnotselected', 'mod_data').then((str) => {\n return Notification.addNotification({\n type: 'error',\n message: str\n });\n }).catch(Notification.exception);\n }\n });\n};\n"],"names":["selectors","document","querySelector","addEventListener","event","preventDefault","querySelectorAll","length","target","closest","submit","then","str","Notification","addNotification","type","message","catch","exception"],"mappings":";;;;;;;4JA0BMA,6BACkB,6BADlBA,oCAEyB,+CAMX,KACWC,SAASC,cAAcF,8BAE/BG,iBAAiB,SAASC,WACzCA,MAAMC,iBAEFJ,SAASK,iBAAiBN,qCAAqCO,OAAS,EAAG,CACvDH,MAAMI,OAAOC,QAAQ,QAC7BC,iCAGF,oBAAqB,YAAYC,MAAMC,KACtCC,sBAAaC,gBAAgB,CAChCC,KAAM,QACNC,QAASJ,QAEdK,MAAMJ,sBAAaK"} \ No newline at end of file +{"version":3,"file":"selectpreset.min.js","sources":["../src/selectpreset.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 to control the form responsible for selecting a preset.\n *\n * @module mod_data/selectpreset\n * @copyright 2021 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst selectors = {\n presetRadioButton: 'input[name=\"fullname\"]',\n selectPresetButton: 'input[name=\"selectpreset\"]',\n selectedPresetRadioButton: 'input[name=\"fullname\"]:checked',\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n const radioButton = document.querySelectorAll(selectors.presetRadioButton);\n\n // Initialize the \"Use preset\" button properly.\n disableUsePresetButton();\n\n radioButton.forEach((elem) => {\n elem.addEventListener('change', function(event) {\n event.preventDefault();\n // Enable the \"Use preset\" button when any of the radio buttons in the presets list is checked.\n disableUsePresetButton();\n });\n });\n\n};\n\n/**\n * Decide whether to disable or not the \"Use preset\" button.\n * When there is no preset selected, the button should be displayed disabled; otherwise, it will appear enabled as a primary button.\n *\n * @method\n * @private\n */\nconst disableUsePresetButton = () => {\n let selectPresetButton = document.querySelector(selectors.selectPresetButton);\n const selectedRadioButton = document.querySelectorAll(selectors.selectedPresetRadioButton);\n\n if (selectedRadioButton.length > 0) {\n // There is one preset selected, so the button should be enabled.\n selectPresetButton.removeAttribute('disabled');\n selectPresetButton.classList.remove('btn-secondary');\n selectPresetButton.classList.add('btn-primary');\n } else {\n // There is no any preset selected, so the button should be disabled.\n selectPresetButton.setAttribute('disabled', true);\n selectPresetButton.classList.remove('btn-primary');\n selectPresetButton.classList.add('btn-secondary');\n }\n};\n"],"names":["selectors","radioButton","document","querySelectorAll","disableUsePresetButton","forEach","elem","addEventListener","event","preventDefault","selectPresetButton","querySelector","length","removeAttribute","classList","remove","add","setAttribute"],"mappings":";;;;;;;;MAuBMA,4BACiB,yBADjBA,6BAEkB,6BAFlBA,oCAGyB,+CAMX,WACVC,YAAcC,SAASC,iBAAiBH,6BAG9CI,yBAEAH,YAAYI,SAASC,OACjBA,KAAKC,iBAAiB,UAAU,SAASC,OACrCA,MAAMC,iBAENL,sCAaNA,uBAAyB,SACvBM,mBAAqBR,SAASS,cAAcX,8BACpBE,SAASC,iBAAiBH,qCAE9BY,OAAS,GAE7BF,mBAAmBG,gBAAgB,YACnCH,mBAAmBI,UAAUC,OAAO,iBACpCL,mBAAmBI,UAAUE,IAAI,iBAGjCN,mBAAmBO,aAAa,YAAY,GAC5CP,mBAAmBI,UAAUC,OAAO,eACpCL,mBAAmBI,UAAUE,IAAI"} \ No newline at end of file diff --git a/mod/data/amd/src/selectpreset.js b/mod/data/amd/src/selectpreset.js index 3a5a1487974..1ac23b10959 100644 --- a/mod/data/amd/src/selectpreset.js +++ b/mod/data/amd/src/selectpreset.js @@ -21,10 +21,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import Notification from 'core/notification'; -import {get_string as getString} from 'core/str'; - const selectors = { + presetRadioButton: 'input[name="fullname"]', selectPresetButton: 'input[name="selectpreset"]', selectedPresetRadioButton: 'input[name="fullname"]:checked', }; @@ -33,22 +31,41 @@ const selectors = { * Initialize module. */ export const init = () => { - const selectPresetButton = document.querySelector(selectors.selectPresetButton); + const radioButton = document.querySelectorAll(selectors.presetRadioButton); - selectPresetButton.addEventListener('click', event => { - event.preventDefault(); - // Validate whether there is a selected preset before submitting the form. - if (document.querySelectorAll(selectors.selectedPresetRadioButton).length > 0) { - const presetsForm = event.target.closest('form'); - presetsForm.submit(); - } else { - // No selected presets. Display an error message to user. - getString('presetnotselected', 'mod_data').then((str) => { - return Notification.addNotification({ - type: 'error', - message: str - }); - }).catch(Notification.exception); - } + // Initialize the "Use preset" button properly. + disableUsePresetButton(); + + radioButton.forEach((elem) => { + elem.addEventListener('change', function(event) { + event.preventDefault(); + // Enable the "Use preset" button when any of the radio buttons in the presets list is checked. + disableUsePresetButton(); + }); }); + +}; + +/** + * Decide whether to disable or not the "Use preset" button. + * When there is no preset selected, the button should be displayed disabled; otherwise, it will appear enabled as a primary button. + * + * @method + * @private + */ +const disableUsePresetButton = () => { + let selectPresetButton = document.querySelector(selectors.selectPresetButton); + const selectedRadioButton = document.querySelectorAll(selectors.selectedPresetRadioButton); + + if (selectedRadioButton.length > 0) { + // There is one preset selected, so the button should be enabled. + selectPresetButton.removeAttribute('disabled'); + selectPresetButton.classList.remove('btn-secondary'); + selectPresetButton.classList.add('btn-primary'); + } else { + // There is no any preset selected, so the button should be disabled. + selectPresetButton.setAttribute('disabled', true); + selectPresetButton.classList.remove('btn-primary'); + selectPresetButton.classList.add('btn-secondary'); + } }; diff --git a/mod/data/classes/form/save_as_preset.php b/mod/data/classes/form/save_as_preset.php index 13bcd15cfe0..968718eeb66 100644 --- a/mod/data/classes/form/save_as_preset.php +++ b/mod/data/classes/form/save_as_preset.php @@ -20,6 +20,7 @@ use context; use moodle_exception; use moodle_url; use core_form\dynamic_form; +use mod_data\manager; /** * Save database as preset form. @@ -72,9 +73,11 @@ class save_as_preset extends dynamic_form { $errors = parent::validation($formdata, $files); $context = $this->get_context_for_dynamic_submission(); + $cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST); + $manager = manager::create_from_coursemodule($cm); if (!empty($formdata['overwrite'])) { - $presets = data_get_available_presets($context); + $presets = $manager->get_available_presets(); $selectedpreset = new \stdClass(); foreach ($presets as $preset) { if ($preset->name == $formdata['name']) { @@ -87,7 +90,7 @@ class save_as_preset extends dynamic_form { } } else { // If the preset exists now then we need to throw an error. - $sitepresets = data_get_available_site_presets($context); + $sitepresets = $manager->get_available_saved_presets(); foreach ($sitepresets as $preset) { if ($formdata['name'] == $preset->name) { $errors['name'] = get_string('errorpresetexists', 'data'); @@ -137,7 +140,8 @@ class save_as_preset extends dynamic_form { try { if (!empty($this->get_data()->overwrite)) { - $presets = data_get_available_presets($context); + $manager = manager::create_from_coursemodule($cm); + $presets = $manager->get_available_presets(); $selectedpreset = new \stdClass(); foreach ($presets as $preset) { if ($preset->name == $this->get_data()->name) { diff --git a/mod/data/classes/manager.php b/mod/data/classes/manager.php index ecabc6add7c..3111326ca5f 100644 --- a/mod/data/classes/manager.php +++ b/mod/data/classes/manager.php @@ -23,6 +23,7 @@ use data_field_base; use mod_data\event\course_module_viewed; use mod_data\event\template_viewed; use mod_data\event\template_updated; +use core_component; use stdClass; /** @@ -300,4 +301,81 @@ class manager { return true; } + + /** + * Returns an array of all the available presets. + * + * @return array A list with the datapreset plugins and the presets saved by users. + */ + public function get_available_presets(): array { + // First load the datapreset plugins that exist within the modules preset dir. + $pluginpresets = static::get_available_plugin_presets(); + + // Then find the presets that people have saved. + $savedpresets = static::get_available_saved_presets(); + + return array_merge($pluginpresets, $savedpresets); + } + + /** + * Returns an array of all the presets that users have saved to the site. + * + * @return array A list with the preset saved by the users. + */ + public function get_available_saved_presets(): array { + global $USER; + + $presets = []; + + $fs = get_file_storage(); + $files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA); + if (empty($files)) { + return $presets; + } + $canviewall = has_capability('mod/data:viewalluserpresets', $this->get_context()); + foreach ($files as $file) { + $isnotdirectory = ($file->is_directory() && $file->get_filepath() == '/') || !$file->is_directory(); + $userid = $file->get_userid(); + $cannotviewfile = !$canviewall && $userid != $USER->id; + if ($isnotdirectory || $cannotviewfile) { + continue; + } + + $preset = new stdClass(); + $preset->isplugin = false; + $preset->path = $file->get_filepath(); + $preset->name = trim($preset->path, '/'); + $preset->shortname = $preset->name; + $preset->userid = $userid; + $preset->id = $file->get_id(); + $preset->storedfile = $file; + $presets[] = $preset; + } + + return $presets; + } + + /** + * Returns an array of all the available plugin presets. + * + * @return array A list with the datapreset plugins. + */ + public static function get_available_plugin_presets(): array { + $presets = []; + + $dirs = core_component::get_plugin_list('datapreset'); + foreach ($dirs as $dir => $fulldir) { + if (preset::is_directory_a_preset($fulldir)) { + $preset = new stdClass(); + $preset->isplugin = true; + $preset->path = $fulldir; + $preset->userid = 0; + $preset->shortname = $dir; + $preset->name = preset::get_name_from_plugin($dir); + $presets[] = $preset; + } + } + + return $presets; + } } diff --git a/mod/data/classes/output/presets.php b/mod/data/classes/output/presets.php index 2e0cf4de5cd..7c1c6618b74 100644 --- a/mod/data/classes/output/presets.php +++ b/mod/data/classes/output/presets.php @@ -16,6 +16,8 @@ namespace mod_data\output; +use action_menu; +use action_menu_link_secondary; use moodle_url; use templatable; use renderable; @@ -64,54 +66,69 @@ class presets implements templatable, renderable { */ public function export_for_template(\renderer_base $output): array { + $presets = $this->get_presets(); return [ 'd' => $this->id, 'formactionul' => $this->formactionurl->out(), - 'presetstable' => $this->get_presets_table(), + 'showmanage' => $this->manage, + 'presets' => $presets, ]; } /** - * Generates and returns the HTML for the presets table. + * Returns the presets list with the information required to display them. * - * @return string + * @return array Presets list. */ - private function get_presets_table(): string { - global $OUTPUT, $PAGE, $DB; - - $presetstable = new \html_table(); - $presetstable->align = ['center', 'left', 'left']; - $presetstable->size = ['1%', '90%', '1%']; + private function get_presets(): array { + global $OUTPUT, $PAGE; + $presets = []; foreach ($this->presets as $preset) { $presetname = $preset->name; if (!empty($preset->userid)) { + // If the preset has the userid field, the full name of creator it will be added to the end of the name. $userfieldsapi = \core_user\fields::for_name(); $namefields = $userfieldsapi->get_sql('', false, '', '', false)->selects; - $presetuser = $DB->get_record('user', array('id' => $preset->userid), 'id, ' . $namefields, MUST_EXIST); + $fields = 'id, ' . $namefields; + $presetuser = \core_user::get_user($preset->userid, $fields, MUST_EXIST); $username = fullname($presetuser, true); $presetname = "{$presetname} ({$username})"; } - $deleteaction = ''; + $actions = []; if ($this->manage) { - if (data_user_can_delete_preset($PAGE->context, $preset) && $preset->name != 'Image gallery') { + // Only presets saved by users can be removed (so the datapreset plugins shouldn't display the delete button). + if (!$preset->isplugin && data_user_can_delete_preset($PAGE->context, $preset)) { $deleteactionurl = new moodle_url('/mod/data/preset.php', ['d' => $this->id, 'fullname' => "{$preset->userid}/{$preset->shortname}", 'action' => 'confirmdelete']); - $deleteaction = $OUTPUT->action_icon($deleteactionurl, - new \pix_icon('t/delete', get_string('delete'))); + + $actionmenu = new action_menu(); + $icon = $OUTPUT->pix_icon('i/menu', get_string('actions')); + $actionmenu->set_menu_trigger($icon, 'btn btn-icon d-flex align-items-center justify-content-center'); + $actionmenu->set_action_label(get_string('actions')); + $actionmenu->attributes['class'] .= ' presets-actions'; + + $actionmenu->add(new action_menu_link_secondary( + $deleteactionurl, + null, + get_string('delete'), + )); + $actions = $actionmenu->export_for_template($OUTPUT); } } - $presetstable->data[] = [ - \html_writer::tag('input', '', array('type' => 'radio', 'name' => 'fullname', - 'value' => "{$preset->userid}/{$preset->shortname}")), - $presetname, - $deleteaction, + $presets[] = [ + 'id' => $this->id, + 'name' => $preset->name, + 'shortname' => $preset->shortname, + 'fullname' => $presetname, + 'userid' => $preset->userid, + 'actions' => $actions, ]; } - return \html_writer::table($presetstable); + return $presets; } } diff --git a/mod/data/classes/preset.php b/mod/data/classes/preset.php new file mode 100644 index 00000000000..6647766d58b --- /dev/null +++ b/mod/data/classes/preset.php @@ -0,0 +1,58 @@ +. + +namespace mod_data; + +/** + * Class preset for database activity. + * + * @package mod_data + * @copyright 2022 Sara Arjona + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class preset { + + /** + * Checks if a directory contains all the required files to define a preset. + * + * @param string $directory The patch to check if it contains the preset files or not. + * @return bool True if the directory contains all the preset files; false otherwise. + */ + public static function is_directory_a_preset(string $directory): bool { + $status = true; + $directory = rtrim($directory, '/\\') . '/'; + $presetfilenames = array_merge(array_values(manager::TEMPLATES_LIST), ['preset.xml']); + foreach ($presetfilenames as $filename) { + $status &= file_exists($directory.$filename); + } + + return $status; + } + + /** + * Returns the best name to show for a datapreset plugin. + * + * @param string $pluginname The datapreset plugin name. + * @return string The plugin preset name to display. + */ + public static function get_name_from_plugin(string $pluginname): string { + if (get_string_manager()->string_exists('modulename', 'datapreset_'.$pluginname)) { + return get_string('modulename', 'datapreset_'.$pluginname); + } else { + return $pluginname; + } + } +} diff --git a/mod/data/field.php b/mod/data/field.php index 21368cc8e3d..de20d658f86 100644 --- a/mod/data/field.php +++ b/mod/data/field.php @@ -23,6 +23,8 @@ * @package mod_data */ +use mod_data\manager; + require_once('../../config.php'); require_once('lib.php'); require_once($CFG->dirroot.'/mod/data/preset_form.php'); @@ -94,7 +96,8 @@ if ($id) { require_login($course, true, $cm); -$context = context_module::instance($cm->id); +$manager = manager::create_from_coursemodule($cm); +$context = $manager->get_context(); require_capability('mod/data:managetemplates', $context); $formimportzip = new data_import_preset_zip_form(); @@ -315,10 +318,10 @@ switch ($mode) { } } else { echo $OUTPUT->heading(get_string('presets', 'data'), 2, 'mb-4'); - $presets = data_get_available_presets($context); - $presetstable = new \mod_data\output\presets($data->id, $presets, + $presets = $manager->get_available_presets(); + $presetsdata = new \mod_data\output\presets($data->id, $presets, new \moodle_url('/mod/data/field.php')); - echo $renderer->render_presets($presetstable, false); + echo $renderer->render_presets($presetsdata); } echo $OUTPUT->footer(); exit; @@ -463,4 +466,3 @@ if (($mode == 'new') && (!empty($newtype))) { // Adding a new field. /// Finish the page echo $OUTPUT->footer(); - diff --git a/mod/data/lang/en/data.php b/mod/data/lang/en/data.php index d2ce2a79dda..185a714f7eb 100644 --- a/mod/data/lang/en/data.php +++ b/mod/data/lang/en/data.php @@ -320,6 +320,7 @@ $string['portfolionotfile'] = 'Export to a portfolio rather than a file (csv and $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'; +$string['presetshelp'] = 'Choose a preset to use as a starting point.'; $string['privacy:metadata:commentpurpose'] = 'Comments on database records'; $string['privacy:metadata:data_content'] = 'Represents one answer to one field in database activity module'; $string['privacy:metadata:data_content:fieldid'] = 'Field definition ID'; diff --git a/mod/data/lib.php b/mod/data/lib.php index 2bcf4d8f795..f11f58cf6f4 100644 --- a/mod/data/lib.php +++ b/mod/data/lib.php @@ -22,6 +22,7 @@ */ use mod_data\manager; +use mod_data\preset; defined('MOODLE_INTERNAL') || die(); @@ -2030,52 +2031,30 @@ function data_convert_to_roles($data, $teacherroles=array(), $studentroles=array * @param string $shortname * @param string $path * @return string + * @deprecated since Moodle 4.1 MDL-75148 - please, use the preset::get_name_from_plugin() function instead. + * @todo MDL-75189 This will be deleted in Moodle 4.5. + * @see preset::get_name_from_plugin() */ function data_preset_name($shortname, $path) { + debugging('data_preset_name() is deprecated. Please use preset::get_name_from_plugin() instead.', DEBUG_DEVELOPER); - // We are looking inside the preset itself as a first choice, but also in normal data directory - $string = get_string('modulename', 'datapreset_'.$shortname); - - if (substr($string, 0, 1) == '[') { - return $shortname; - } else { - return $string; - } + return preset::get_name_from_plugin($shortname); } /** * Returns an array of all the available presets. * * @return array + * @deprecated since Moodle 4.1 MDL-75148 - please, use the manager::get_available_presets() function instead. + * @todo MDL-75189 This will be deleted in Moodle 4.5. + * @see manager::get_available_presets() */ function data_get_available_presets($context) { - global $CFG, $USER; + debugging('data_get_available_presets() is deprecated. Please use manager::get_available_presets() instead.', DEBUG_DEVELOPER); - $presets = array(); - - // First load the ratings sub plugins that exist within the modules preset dir - if ($dirs = core_component::get_plugin_list('datapreset')) { - foreach ($dirs as $dir=>$fulldir) { - if (is_directory_a_preset($fulldir)) { - $preset = new stdClass(); - $preset->path = $fulldir; - $preset->userid = 0; - $preset->shortname = $dir; - $preset->name = data_preset_name($dir, $fulldir); - if (file_exists($fulldir.'/screenshot.jpg')) { - $preset->screenshot = $CFG->wwwroot.'/mod/data/preset/'.$dir.'/screenshot.jpg'; - } else if (file_exists($fulldir.'/screenshot.png')) { - $preset->screenshot = $CFG->wwwroot.'/mod/data/preset/'.$dir.'/screenshot.png'; - } else if (file_exists($fulldir.'/screenshot.gif')) { - $preset->screenshot = $CFG->wwwroot.'/mod/data/preset/'.$dir.'/screenshot.gif'; - } - $presets[] = $preset; - } - } - } - // Now add to that the site presets that people have saved - $presets = data_get_available_site_presets($context, $presets); - return $presets; + $cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST); + $manager = manager::create_from_coursemodule($cm); + return $manager->get_available_presets(); } /** @@ -2084,30 +2063,20 @@ function data_get_available_presets($context) { * @param stdClass $context The context that we are looking from. * @param array $presets * @return array An array of presets + * @deprecated since Moodle 4.1 MDL-75148 - please, use the manager::get_available_saved_presets() function instead. + * @todo MDL-75189 This will be deleted in Moodle 4.5. + * @see manager::get_available_saved_presets() */ function data_get_available_site_presets($context, array $presets=array()) { - global $USER; + debugging( + 'data_get_available_site_presets() is deprecated. Please use manager::get_available_saved_presets() instead.', + DEBUG_DEVELOPER + ); - $fs = get_file_storage(); - $files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA); - $canviewall = has_capability('mod/data:viewalluserpresets', $context); - if (empty($files)) { - return $presets; - } - foreach ($files as $file) { - if (($file->is_directory() && $file->get_filepath()=='/') || !$file->is_directory() || (!$canviewall && $file->get_userid() != $USER->id)) { - continue; - } - $preset = new stdClass; - $preset->path = $file->get_filepath(); - $preset->name = trim($preset->path, '/'); - $preset->shortname = $preset->name; - $preset->userid = $file->get_userid(); - $preset->id = $file->get_id(); - $preset->storedfile = $file; - $presets[] = $preset; - } - return $presets; + $cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST); + $manager = manager::create_from_coursemodule($cm); + $savedpresets = $manager->get_available_saved_presets(); + return array_merge($presets, $savedpresets); } /** @@ -2261,22 +2230,18 @@ function data_in_readonly_period($data) { } /** - * @return bool + * Check if the files in a directory are the expected for a preset. + * + * @return bool Wheter the defined $directory has or not all the expected preset files. + * + * @deprecated since Moodle 4.1 MDL-75148 - please, use the preset::is_directory_a_preset() function instead. + * @todo MDL-75189 This will be deleted in Moodle 4.5. + * @see manager::is_directory_a_preset() */ function is_directory_a_preset($directory) { - $directory = rtrim($directory, '/\\') . '/'; - $status = file_exists($directory.'singletemplate.html') && - file_exists($directory.'listtemplate.html') && - file_exists($directory.'listtemplateheader.html') && - file_exists($directory.'listtemplatefooter.html') && - file_exists($directory.'addtemplate.html') && - file_exists($directory.'rsstemplate.html') && - file_exists($directory.'rsstitletemplate.html') && - file_exists($directory.'csstemplate.css') && - file_exists($directory.'jstemplate.js') && - file_exists($directory.'preset.xml'); + debugging('is_directory_a_preset() is deprecated. Please use preset::is_directory_a_preset() instead.', DEBUG_DEVELOPER); - return $status; + return preset::is_directory_a_preset($directory); } /** @@ -2350,7 +2315,7 @@ abstract class data_preset_importer { require_once($CFG->libdir.'/xmlize.php'); $fs = $fileobj = null; - if (!is_directory_a_preset($this->directory)) { + if (!preset::is_directory_a_preset($this->directory)) { //maybe the user requested a preset stored in the Moodle file storage $fs = get_file_storage(); @@ -3650,7 +3615,7 @@ function data_presets_export($course, $cm, $data, $tostorage=false) { fclose($asearchtemplate); // Check if all files have been generated - if (! is_directory_a_preset($exportdir)) { + if (! preset::is_directory_a_preset($exportdir)) { throw new \moodle_exception('generateerror', 'data'); } diff --git a/mod/data/preset.php b/mod/data/preset.php index dcdb3a2f93d..e10370c71a2 100644 --- a/mod/data/preset.php +++ b/mod/data/preset.php @@ -28,21 +28,27 @@ * @package mod_data */ +use mod_data\manager; + require_once('../../config.php'); require_once($CFG->dirroot.'/mod/data/lib.php'); require_once($CFG->dirroot.'/mod/data/preset_form.php'); -$id = optional_param('id', 0, PARAM_INT); // The course module id. +// The course module id. +$id = optional_param('id', 0, PARAM_INT); +$manager = null; if ($id) { - $cm = get_coursemodule_from_id('data', $id, null, null, MUST_EXIST); - $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); - $data = $DB->get_record('data', array('id' => $cm->instance), '*', MUST_EXIST); + list($course, $cm) = get_course_and_cm_from_cmid($id, manager::MODULE); + $manager = manager::create_from_coursemodule($cm); + $data = $manager->get_instance(); } else { - $d = required_param('d', PARAM_INT); // database activity id - $data = $DB->get_record('data', array('id' => $d), '*', MUST_EXIST); - $course = $DB->get_record('course', array('id' => $data->course), '*', MUST_EXIST); - $cm = get_coursemodule_from_instance('data', $data->id, $course->id, null, MUST_EXIST); + // We must have the database activity id. + $d = required_param('d', PARAM_INT); + $data = $DB->get_record('data', ['id' => $d], '*', MUST_EXIST); + $manager = manager::create_from_instance($data); + $cm = $manager->get_coursemodule(); + $course = get_course($cm->course); } $action = optional_param('action', 'view', PARAM_ALPHA); // The page action. @@ -52,7 +58,8 @@ if (!in_array($action, $allowedactions)) { throw new moodle_exception('invalidaccess'); } -$context = context_module::instance($cm->id, MUST_EXIST); +$context = $manager->get_context(); + require_login($course, false, $cm); require_capability('mod/data:managetemplates', $context); @@ -70,7 +77,7 @@ $data->cmidnumber = $cm->idnumber; $data->instance = $cm->instance; $renderer = $PAGE->get_renderer('mod_data'); -$presets = data_get_available_presets($context); +$presets = $manager->get_available_presets(); if ($action === 'export') { if (headers_sent()) { diff --git a/mod/data/preset/imagegallery/asearchtemplate.html b/mod/data/preset/imagegallery/asearchtemplate.html new file mode 100644 index 00000000000..8e807baf3ea --- /dev/null +++ b/mod/data/preset/imagegallery/asearchtemplate.html @@ -0,0 +1,12 @@ +
+ + + + + + + + + +
Title: [[title]]
Caption: [[caption]]
+
diff --git a/mod/data/styles.css b/mod/data/styles.css index 00494bd8e1b..53026c90bea 100644 --- a/mod/data/styles.css +++ b/mod/data/styles.css @@ -148,3 +148,7 @@ #page-mod-data-edit .datatagcontrol { padding-left: 10px; } + +.preset_action_menu .dropdown-toggle::after { + display: none; +} diff --git a/mod/data/templates/presets.mustache b/mod/data/templates/presets.mustache index 3d1958a2af7..06754453dfe 100644 --- a/mod/data/templates/presets.mustache +++ b/mod/data/templates/presets.mustache @@ -19,22 +19,69 @@ Context variables required for this template: * formactionul - The form action url. * d - The database id. - * presetstable - The HTML output of the table with presets + * presets - List of presets containing id, name, fullname, shortname and actions. Example context (json): { "formactionul": "http://www.example.com", "d": 1, - "presetstable": "
" + "presets": [ + { + "id": 1, + "name": "Image gallery", + "shortname": "imagegallery", + "fullname": "Image gallery", + "userid": 0, + "actions": [] + }, + { + "id": 2, + "name": "Preset saved manually", + "shortname": "Preset saved manually", + "fullname": "Preset saved manually (admin)", + "userid": 2, + "actions": [] + } + ] } }} +{{#str}}presetshelp, mod_data{{/str}} +
- {{{presetstable}}} - + + + + + + + + + + + {{#presets}} + + + + + + {{/presets}} + +
{{#str}} name {{/str}}{{#showmanage}}{{#str}} action {{/str}}{{/showmanage}}
+ + {{fullname}} + {{#actions}} +
+ {{>core/action_menu}} +
+ {{/actions}} +
+ +
+ {{#js}} require(['mod_data/selectpreset'], function(selectPreset) { selectPreset.init(); diff --git a/mod/data/templates/presets_action_bar.mustache b/mod/data/templates/presets_action_bar.mustache index 6511ecde826..f7a93a979f8 100644 --- a/mod/data/templates/presets_action_bar.mustache +++ b/mod/data/templates/presets_action_bar.mustache @@ -25,7 +25,7 @@ } }}
-
+
diff --git a/mod/data/tests/behat/data_presets.feature b/mod/data/tests/behat/data_presets.feature new file mode 100644 index 00000000000..d8f5f09965d --- /dev/null +++ b/mod/data/tests/behat/data_presets.feature @@ -0,0 +1,81 @@ +@mod @mod_data +Feature: Users can view and manage data presets + In order to use presets + As a user + I need to view, manage and use presets + + 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 | + | data1 | Saved preset 1 | + | data1 | Saved preset 2 | + + @javascript + Scenario: Admins can delete saved presets + Given I am on the "Mountain landscapes" "data activity" page logged in as admin + When I follow "Presets" + Then I should see "Choose a preset to use as a starting point." + And I should see "Image gallery" + And I should see "Saved preset 1" + And I should see "Saved preset 2" + # Plugin presets can't be removed. + And I should not see "Actions" in the "Image gallery" "table_row" + # The admin should be able to delete saved presets. + But I open the action menu in "Saved preset 1" "table_row" + And I should see "Delete" + And I open the action menu in "Saved preset 2" "table_row" + And I should see "Delete" + + @javascript + Scenario: Teachers can see and use presets + Given the following "mod_data > fields" exist: + | database | type | name | description | + | data1 | text | Test field name | Test field description | + And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Templates" + And I click on "Save as preset" "button" + And I set the field "Name" to "Saved preset by teacher1" + And I click on "Save" "button" in the "Save all fields and templates as preset" "dialogue" + When I follow "Presets" + Then I should see "Choose a preset to use as a starting point." + And I should see "Image gallery" + And I should see "Saved preset 1" + And I should see "Saved preset 2" + 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" + # Teachers should be able to delete their saved presets. + And I open the action menu in "Saved preset by teacher1" "table_row" + And I should see "Delete" + # Teachers can't delete the presets they haven't created. + And I should not see "Actions" in the "Saved preset 1" "table_row" + # The "Use preset" button should be enabled only when a preset is selected. + And the "Use preset" "button" should be disabled + And I click on "fullname" "radio" in the "Image gallery" "table_row" + And the "Use preset" "button" should be enabled + + @javascript + Scenario: Only users with the viewalluserpresets capability can see presets created by other users + Given the following "permission override" exists: + | role | editingteacher | + | capability | mod/data:viewalluserpresets | + | permission | Prohibit | + | contextlevel | System | + | reference | | + When I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Presets" + Then I should see "Image gallery" + And I should not see "Saved preset 1" + And I should not see "Saved preset 2" diff --git a/mod/data/tests/generator/behat_mod_data_generator.php b/mod/data/tests/generator/behat_mod_data_generator.php index 1ae6097238d..8f39d2d02c5 100644 --- a/mod/data/tests/generator/behat_mod_data_generator.php +++ b/mod/data/tests/generator/behat_mod_data_generator.php @@ -49,6 +49,12 @@ class behat_mod_data_generator extends behat_generator_base { 'required' => ['database', 'name'], 'switchids' => ['database' => 'databaseid'], ], + 'presets' => [ + 'singular' => 'preset', + 'datagenerator' => 'preset', + 'required' => ['database', 'name'], + 'switchids' => ['database' => 'databaseid'], + ], ]; } @@ -124,6 +130,19 @@ class behat_mod_data_generator extends behat_generator_base { } } + /** + * Saves a preset. + * + * @param array $data Preset data. + */ + protected function process_preset(array $data): void { + global $DB; + + $instance = $DB->get_record('data', ['id' => $data['databaseid']], '*', MUST_EXIST); + + $this->get_data_generator()->create_preset($instance, (object) $data); + } + /** * Get the module data generator. * diff --git a/mod/data/tests/generator/lib.php b/mod/data/tests/generator/lib.php index 719552dfa24..8ed2ea440ca 100644 --- a/mod/data/tests/generator/lib.php +++ b/mod/data/tests/generator/lib.php @@ -23,6 +23,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use mod_data\manager; + defined('MOODLE_INTERNAL') || die(); @@ -361,4 +363,28 @@ class mod_data_generator extends testing_module_generator { return $recordid; } + + /** + * Creates a preset from a mod_data instance. + * + * @param stdClass $instance The mod_data instance. + * @param stdClass|null $record The preset information, like 'name'. + * @return bool Whether the preset has been created or not. + */ + public function create_preset(stdClass $instance, stdClass $record = null): bool { + global $DB; + + if (is_null($record)) { + $record = new stdClass(); + } + + // Fill in optional values if not specified. + if (!isset($record->name)) { + $record->name = 'New preset ' . microtime(); + } + + $course = $DB->get_record('course', ['id' => $instance->course], '*', MUST_EXIST); + $cm = get_coursemodule_from_instance(manager::MODULE, $instance->id, $course->id, null, MUST_EXIST); + return data_presets_save($course, $cm, $instance, $record->name); + } } diff --git a/mod/data/tests/generator_test.php b/mod/data/tests/generator_test.php index 2b09fbc1c52..b7e78d9a40c 100644 --- a/mod/data/tests/generator_test.php +++ b/mod/data/tests/generator_test.php @@ -23,6 +23,7 @@ namespace mod_data; * @category phpunit * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \mod_data_generator */ class generator_test extends \advanced_testcase { public function test_generator() { @@ -229,4 +230,46 @@ class generator_test extends \advanced_testcase { $this->assertEquals(array('Cats', 'mice'), array_values(\core_tag_tag::get_item_tags_array('mod_data', 'data_records', $datarecordid))); } + + /** + * Test for create_preset(). + * + * @covers ::create_preset + */ + public function test_create_preset() { + $this->resetAfterTest(); + $this->setAdminUser(); + + $course = $this->getDataGenerator()->create_course(); + $activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]); + $cm = get_coursemodule_from_id(manager::MODULE, $activity->cmid, 0, false, MUST_EXIST); + + // Check initially there are no saved presets. + $manager = manager::create_from_coursemodule($cm); + $savedpresets = $manager->get_available_saved_presets(); + $this->assertEmpty($savedpresets); + + // Create one preset with the default configuration. + $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data'); + $result = $plugingenerator->create_preset($activity); + $this->assertTrue($result); + $savedpresets = $manager->get_available_saved_presets(); + $this->assertCount(1, $savedpresets); + $preset = reset($savedpresets); + $this->assertStringStartsWith('New preset', $preset->name); + + // Create another preset with a specific name. + $record = (object) [ + 'name' => 'World recipes preset', + ]; + $result = $plugingenerator->create_preset($activity, $record); + $this->assertTrue($result); + $savedpresets = $manager->get_available_saved_presets(); + $this->assertCount(2, $savedpresets); + foreach ($savedpresets as $preset) { + if (!str_starts_with($preset->name, 'New preset')) { + $this->assertEquals('World recipes preset', $preset->name); + } + } + } } diff --git a/mod/data/tests/manager_test.php b/mod/data/tests/manager_test.php index 9f5dd7e43f4..55c3e34fc2d 100644 --- a/mod/data/tests/manager_test.php +++ b/mod/data/tests/manager_test.php @@ -18,6 +18,7 @@ namespace mod_data; use context_module; use moodle_url; +use core_component; /** * Manager tests class for mod_data. @@ -161,4 +162,215 @@ class manager_test extends \advanced_testcase { $this->assertEventContextNotUsed($event); $this->assertNotEmpty($event->get_name()); } + + /** + * Test for get_available_presets(). + * + * @covers ::get_available_presets + */ + public function test_get_available_presets() { + global $DB; + + $this->resetAfterTest(); + + $course = $this->getDataGenerator()->create_course(); + $user = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); + $this->setUser($user); + + $activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]); + $cm = get_coursemodule_from_id(manager::MODULE, $activity->cmid, 0, false, MUST_EXIST); + + // Check available presets meet the datapreset plugins when there are no any preset saved by users. + $datapresetplugins = core_component::get_plugin_list('datapreset'); + $manager = manager::create_from_coursemodule($cm); + $presets = $manager->get_available_presets(); + $this->assertCount(count($datapresetplugins), $presets); + // Confirm that, at least, the "Image gallery" is one of them. + $namepresets = array_map(function($preset) { + return $preset->name; + }, $presets); + $this->assertContains('Image gallery', $namepresets); + + // Login as admin and create some presets saved manually by users. + $this->setAdminUser(); + $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data'); + $savedpresets = []; + for ($i = 1; $i <= 3; $i++) { + $preset = (object) [ + 'name' => 'Preset name ' . $i, + ]; + $plugingenerator->create_preset($activity, $preset); + $savedpresets[] = $preset; + } + $savedpresetsnames = array_map(function($preset) { + return $preset->name; + }, $savedpresets); + $this->setUser($user); + + // Check available presets meet the datapreset plugins + presets saved manually by users. + $presets = $manager->get_available_presets(); + $this->assertCount(count($datapresetplugins) + count($savedpresets), $presets); + // Confirm that, apart from the "Image gallery" preset, the ones created manually have been also returned. + $namepresets = array_map(function($preset) { + return $preset->name; + }, $presets); + $this->assertContains('Image gallery', $namepresets); + foreach ($savedpresets as $savedpreset) { + $this->assertContains($savedpreset->name, $namepresets); + } + // Check all the presets have the proper value for the isplugin attribute. + foreach ($presets as $preset) { + if (in_array($preset->name, $savedpresetsnames)) { + $this->assertFalse($preset->isplugin); + } else { + $this->assertTrue($preset->isplugin); + } + } + + // Unassign the capability to the teacher role and check that only plugin presets are returned (because the saved presets + // have been created by admin). + $teacherrole = $DB->get_record('role', ['shortname' => 'teacher']); + unassign_capability('mod/data:viewalluserpresets', $teacherrole->id); + $presets = $manager->get_available_presets(); + $this->assertCount(count($datapresetplugins), $presets); + // Confirm that, at least, the "Image gallery" is one of them. + $namepresets = array_map(function($preset) { + return $preset->name; + }, $presets); + $this->assertContains('Image gallery', $namepresets); + foreach ($savedpresets as $savedpreset) { + $this->assertNotContains($savedpreset->name, $namepresets); + } + + // Create a preset with the current user and check that, although the viewalluserpresets is not assigned to the teacher + // role, the preset is returned because the teacher is the owner. + $savedpreset = (object) [ + 'name' => 'Preset created by teacher', + ]; + $plugingenerator->create_preset($activity, $savedpreset); + $presets = $manager->get_available_presets(); + // The presets total is all the plugin presets plus the preset created by the teacher. + $this->assertCount(count($datapresetplugins) + 1, $presets); + // Confirm that, at least, the "Image gallery" is one of them. + $namepresets = array_map(function($preset) { + return $preset->name; + }, $presets); + $this->assertContains('Image gallery', $namepresets); + // Confirm that savedpresets are still not returned. + foreach ($savedpresets as $savedpreset) { + $this->assertNotContains($savedpreset->name, $namepresets); + } + // Confirm the new preset created by the teacher is returned too. + $this->assertContains('Preset created by teacher', $namepresets); + } + + /** + * Test for get_available_plugin_presets(). + * + * @covers ::get_available_plugin_presets + */ + public function test_get_available_plugin_presets() { + $this->resetAfterTest(); + $this->setAdminUser(); + + $course = $this->getDataGenerator()->create_course(); + $activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]); + + // Check available plugin presets meet the datapreset plugins. + $datapresetplugins = core_component::get_plugin_list('datapreset'); + $manager = manager::create_from_instance($activity); + $presets = $manager->get_available_plugin_presets(); + $this->assertCount(count($datapresetplugins), $presets); + // Confirm that, at least, the "Image gallery" is one of them. + $namepresets = array_map(function($preset) { + return $preset->name; + }, $presets); + $this->assertContains('Image gallery', $namepresets); + + // Create a preset saved manually by users. + $savedpreset = (object) [ + 'name' => 'Preset name 1', + ]; + $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data'); + $plugingenerator->create_preset($activity, $savedpreset); + + // Check available plugin presets don't contain the preset saved manually. + $presets = $manager->get_available_plugin_presets(); + $this->assertCount(count($datapresetplugins), $presets); + // Confirm that, at least, the "Image gallery" is one of them. + $namepresets = array_map(function($preset) { + return $preset->name; + }, $presets); + $this->assertContains('Image gallery', $namepresets); + // Confirm that the preset saved manually hasn't been returned. + $this->assertNotContains($savedpreset->name, $namepresets); + // Check all the presets have the proper value for the isplugin attribute. + foreach ($presets as $preset) { + $this->assertTrue($preset->isplugin); + } + } + + /** + * Test for get_available_saved_presets(). + * + * @covers ::get_available_saved_presets + */ + public function test_get_available_saved_presets() { + global $DB; + + $this->resetAfterTest(); + + $course = $this->getDataGenerator()->create_course(); + $user = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); + $this->setUser($user); + + $activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]); + $cm = get_coursemodule_from_id(manager::MODULE, $activity->cmid, 0, false, MUST_EXIST); + + // Check available saved presets is empty (because, for now, no user preset has been created). + $manager = manager::create_from_coursemodule($cm); + $presets = $manager->get_available_saved_presets(); + $this->assertCount(0, $presets); + + // Create some presets saved manually by the admin user. + $this->setAdminUser(); + $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data'); + $savedpresets = []; + for ($i = 1; $i <= 3; $i++) { + $preset = (object) [ + 'name' => 'Preset name ' . $i, + ]; + $plugingenerator->create_preset($activity, $preset); + $savedpresets[] = $preset; + } + // Create one more preset saved manually by the teacher user. + $this->setUser($user); + $teacherpreset = (object) [ + 'name' => 'Preset created by teacher', + ]; + $plugingenerator->create_preset($activity, $teacherpreset); + $savedpresets[] = $teacherpreset; + + $savedpresetsnames = array_map(function($preset) { + return $preset->name; + }, $savedpresets); + + // Check available saved presets only contain presets saved manually by users. + $presets = $manager->get_available_saved_presets(); + $this->assertCount(count($savedpresets), $presets); + // Confirm that it contains only the presets created manually. + foreach ($presets as $preset) { + $this->assertContains($preset->name, $savedpresetsnames); + $this->assertFalse($preset->isplugin); + } + + // Unassign the mod/data:viewalluserpresets capability to the teacher role and check that saved presets are not returned. + $teacherrole = $DB->get_record('role', ['shortname' => 'teacher']); + unassign_capability('mod/data:viewalluserpresets', $teacherrole->id); + + $presets = $manager->get_available_saved_presets(); + $this->assertCount(1, $presets); + $preset = reset($presets); + $this->assertEquals($teacherpreset->name, $preset->name); + } } diff --git a/mod/data/tests/preset_test.php b/mod/data/tests/preset_test.php new file mode 100644 index 00000000000..3c07c7e5ebc --- /dev/null +++ b/mod/data/tests/preset_test.php @@ -0,0 +1,89 @@ +. + +namespace mod_data; + +/** + * Preset tests class for mod_data. + * + * @package mod_data + * @category test + * @copyright 2022 Sara Arjona + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \mod_data\preset + */ +class preset_test extends \advanced_testcase { + + /** + * Test for is_directory_a_preset(). + * + * @dataProvider is_directory_a_preset_provider + * @covers ::is_directory_a_preset + * @param string $directory + * @param bool $expected + */ + public function test_is_directory_a_preset(string $directory, bool $expected): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $result = preset::is_directory_a_preset($directory); + $this->assertEquals($expected, $result); + } + + /** + * Data provider for test_is_directory_a_preset(). + * + * @return array + */ + public function is_directory_a_preset_provider(): array { + global $CFG; + + return [ + 'Valid preset directory' => [ + 'directory' => $CFG->dirroot . '/mod/data/preset/imagegallery', + 'expected' => true, + ], + 'Invalid preset directory' => [ + 'directory' => $CFG->dirroot . '/mod/data/field/checkbox', + 'expected' => false, + ], + 'Unexisting preset directory' => [ + 'directory' => $CFG->dirroot . 'unexistingdirectory', + 'expected' => false, + ], + ]; + } + + /** + * Test for get_name_from_plugin(). + * + * @covers ::get_name_from_plugin + */ + public function test_get_name_from_plugin() { + $this->resetAfterTest(); + $this->setAdminUser(); + + // The expected name for plugins with modulename in lang is this value. + $name = preset::get_name_from_plugin('imagegallery'); + $this->assertEquals('Image gallery', $name); + + // However, if the plugin doesn't exist or the modulename is not defined, the preset shortname will be returned. + $presetshortname = 'nonexistingpreset'; + $name = preset::get_name_from_plugin($presetshortname); + $this->assertEquals($presetshortname, $name); + } + +} diff --git a/mod/data/upgrade.txt b/mod/data/upgrade.txt index 95ad4c8f212..88ca5336c9b 100644 --- a/mod/data/upgrade.txt +++ b/mod/data/upgrade.txt @@ -5,6 +5,11 @@ information provided here is intended especially for developers. * The method data_view is now deprecated. Use $maganer->set_module_viewed instead. * The data_print_template function is now deprecated and replaced by mod_data\template class. * The data_print_ratings function now has an extra $print to get the ratings output instead of printing it directly. +* The following functions have been deprecated because they have been moved to the manager class: + - data_get_available_presets + - data_get_available_site_presets + - data_preset_name + - is_directory_a_preset === 3.7 === * External functions get_entries, get_entry and search_entries now return an additional field "tags" containing the entry tags.