MDL-75142 mod_data: Add description to presets

This commit adds support to description field for presets:
- For plugin presets, description will be stored in the module_help
string in lang files.
- For saved presets, a new element, <description>, has been added
to the preset.xml file, to include this information.

A new field has been added to the "Save as preset" modal, to let users
define description (which is empty by default, to support pre-existing
presets that won't have it).

Apart from that, the "Save as preset" workflow has been slightly improved,
following the Product UX/UX suggestions to hide the checkbox to overwrite
the preset by default and show it only if the user tries to save a preset
with an existing name.
This commit is contained in:
Sara Arjona
2022-08-05 14:51:49 +02:00
parent 35b993b694
commit aecb87f53e
15 changed files with 1070 additions and 239 deletions
+23 -4
View File
@@ -21,6 +21,7 @@ use moodle_exception;
use moodle_url;
use core_form\dynamic_form;
use mod_data\manager;
use mod_data\preset;
/**
* Save database as preset form.
@@ -40,10 +41,16 @@ class save_as_preset extends dynamic_form {
$this->_form->setType('d', PARAM_INT);
$this->_form->addElement('hidden', 'action', 'save2');
$this->_form->setType('action', PARAM_ALPHANUM);
$this->_form->addElement('text', 'name', get_string('name'), ['size' => 60]);
$this->_form->setType('name', PARAM_FILE);
$this->_form->addRule('name', null, 'required');
$this->_form->addElement('checkbox', 'overwrite', '', get_string('overrwritedesc', 'data'));
// Overwrite checkbox will be hidden by default. It will only appear if there is an error when saving the preset.
$this->_form->addElement('checkbox', 'overwrite', '', get_string('overrwritedesc', 'data'), ['class' => 'hidden']);
$this->_form->addElement('textarea', 'description', get_string('description'), ['rows' => 5, 'cols' => 60]);
$this->_form->setType('name', PARAM_TEXT);
}
/**
@@ -91,11 +98,22 @@ class save_as_preset extends dynamic_form {
} else {
// If the preset exists now then we need to throw an error.
$sitepresets = $manager->get_available_saved_presets();
$usercandelete = false;
foreach ($sitepresets as $preset) {
if ($formdata['name'] == $preset->name) {
$errors['name'] = get_string('errorpresetexists', 'data');
if (data_user_can_delete_preset($context, $preset)) {
$errors['name'] = get_string('errorpresetexists', 'data');
$usercandelete = true;
} else {
$errors['name'] = get_string('errorpresetexistsbutnotoverwrite', 'data');
}
break;
}
}
// If there are some errors, the checkbox should be displayed, to let users overwrite the preset.
if (!empty($errors) && $usercandelete) {
$this->_form->getElement('overwrite')->removeAttribute('class');
}
}
return $errors;
@@ -139,8 +157,8 @@ class save_as_preset extends dynamic_form {
$context = \context_module::instance($cm->id, MUST_EXIST);
try {
$manager = manager::create_from_instance($data);
if (!empty($this->get_data()->overwrite)) {
$manager = manager::create_from_coursemodule($cm);
$presets = $manager->get_available_presets();
$selectedpreset = new \stdClass();
foreach ($presets as $preset) {
@@ -153,7 +171,8 @@ class save_as_preset extends dynamic_form {
data_delete_site_preset($this->get_data()->name);
}
}
data_presets_save($course, $cm, $data, $this->get_data()->name);
$preset = preset::create_from_instance($manager, $this->get_data()->name, $this->get_data()->description);
$preset->save();
$result = true;
} catch (\Exception $e) {
$errors[] = $e->getMessage();
+2 -14
View File
@@ -341,14 +341,7 @@ class manager {
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;
$preset = preset::create_from_storedfile($this, $file);
$presets[] = $preset;
}
@@ -366,12 +359,7 @@ class manager {
$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);
$preset = preset::create_from_plugin(null, $dir);
$presets[] = $preset;
}
}
+7 -4
View File
@@ -18,6 +18,7 @@ namespace mod_data\output;
use action_menu;
use action_menu_link_secondary;
use mod_data\preset;
use moodle_url;
use templatable;
use renderable;
@@ -86,12 +87,13 @@ class presets implements templatable, renderable {
$presets = [];
foreach ($this->presets as $preset) {
$presetname = $preset->name;
if (!empty($preset->userid)) {
$userid = $preset instanceof preset ? $preset->get_userid() : $preset->userid;
if (!empty($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;
$fields = 'id, ' . $namefields;
$presetuser = \core_user::get_user($preset->userid, $fields, MUST_EXIST);
$presetuser = \core_user::get_user($userid, $fields, MUST_EXIST);
$username = fullname($presetuser, true);
$presetname = "{$presetname} ({$username})";
}
@@ -101,7 +103,7 @@ class presets implements templatable, renderable {
// 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}",
['d' => $this->id, 'fullname' => "{$userid}/{$preset->shortname}",
'action' => 'confirmdelete']);
$actionmenu = new action_menu();
@@ -124,7 +126,8 @@ class presets implements templatable, renderable {
'name' => $preset->name,
'shortname' => $preset->shortname,
'fullname' => $presetname,
'userid' => $preset->userid,
'description' => $preset->description,
'userid' => $userid,
'actions' => $actions,
];
}
+418
View File
@@ -16,6 +16,12 @@
namespace mod_data;
use core_component;
use invalid_parameter_exception;
use SimpleXMLElement;
use stdClass;
use stored_file;
/**
* Class preset for database activity.
*
@@ -25,6 +31,243 @@ namespace mod_data;
*/
class preset {
/** @var manager manager instance. */
private $manager;
/** @var bool whether the preset is a plugin or has been saved by the user. */
public $isplugin;
/** @var string The preset name. */
public $name;
/** @var string The preset shortname. For datapreset plugins that is the folder; for saved presets, that's the preset name. */
public $shortname;
/** @var string The preset description. */
public $description;
/** @var stored_file For saved presets that's the file object for the root folder. It's null for plugins or for presets that
* haven't been saved yet. */
public $storedfile;
/**
* Class constructor.
*
* @param manager|null $manager the current instance manager
* @param bool $isplugin whether the preset is a plugin or has been saved by the user
* @param string $name the preset name
* @param string $shortname the preset shortname
* @param string|null $description the preset description
* @param stored_file|null $storedfile for saved presets, that's the file for the root folder
* @throws invalid_parameter_exception
*/
protected function __construct(
?manager $manager,
bool $isplugin,
string $name,
string $shortname,
?string $description = '',
?stored_file $storedfile = null
) {
if (!$isplugin && is_null($manager)) {
throw new invalid_parameter_exception('The $manager parameter can only be null for plugin presets.');
}
$this->manager = $manager;
$this->isplugin = $isplugin;
$this->name = $name;
$this->shortname = $shortname;
$this->description = $description;
$this->storedfile = $storedfile;
}
/**
* Create a preset instance from a stored file.
*
* @param manager $manager the current instance manager
* @param stored_file $file the preset root folder
* @return preset|null If the given file doesn't belong to the expected component/filearea/context, null will be returned
*/
public static function create_from_storedfile(manager $manager, stored_file $file): ?self {
if ($file->get_component() != DATA_PRESET_COMPONENT
|| $file->get_filearea() != DATA_PRESET_FILEAREA
|| $file->get_contextid() != DATA_PRESET_CONTEXT) {
return null;
}
$isplugin = false;
$name = trim($file->get_filepath(), '/');
$description = static::get_attribute_value($file->get_filepath(), 'description');
return new self($manager, $isplugin, $name, $name, $description, $file);
}
/**
* Create a preset instance from a plugin.
*
* @param manager|null $manager the current instance manager
* @param string $pluginname the datapreset plugin name
* @return preset|null The plugin preset or null if there is no datapreset plugin with the given name.
*/
public static function create_from_plugin(?manager $manager, string $pluginname): ?self {
$found = false;
$plugins = array_keys(core_component::get_plugin_list('datapreset'));
foreach ($plugins as $plugin) {
if ($plugin == $pluginname) {
$found = true;
break;
}
}
if (!$found) {
// If there is no datapreset plugin with this name, return null.
return null;
}
$name = static::get_name_from_plugin($pluginname);
$description = static::get_description_from_plugin($pluginname);
return new self($manager, true, $name, $pluginname, $description);
}
/**
* Create a preset instance from a data_record entry, a preset name and a description.
*
* @param manager $manager the current instance manager
* @param string $presetname the preset name
* @param string|null $description the preset description
* @return preset
*/
public static function create_from_instance(manager $manager, string $presetname, ?string $description = ''): self {
$isplugin = false;
$path = '/' . $presetname . '/';
$file = static::get_file($path, '.');
return new self($manager, $isplugin, $presetname, $presetname, $description, $file);
}
/**
* Save this preset.
*
* @return bool true if the preset has been saved; false otherwise.
*/
public function save(): bool {
global $USER;
if ($this->isplugin) {
// Plugin presets can't be saved.
return false;
}
$result = false;
if (is_null($this->storedfile)) {
// The preset hasn't been saved before.
$fs = get_file_storage();
// Create and save the preset.xml file, with the description, settings, fields...
$filerecord = static::get_filerecord('preset.xml', $this->get_path(), $USER->id);
$fs->create_file_from_string($filerecord, $this->generate_preset_xml());
// Create and save the template files.
$instance = $this->manager->get_instance();
foreach (manager::TEMPLATES_LIST as $templatename => $templatefile) {
$filerecord->filename = $templatefile;
$fs->create_file_from_string($filerecord, $instance->{$templatename});
}
// Update the storedfile with the one we've just saved.
$this->storedfile = static::get_file($this->get_path(), '.');
$result = true;
}
return $result;
}
/**
* Export this preset.
*
* @return string the full path to the exported preset file.
*/
public function export(): string {
if ($this->isplugin) {
// For now, only saved presets can be exported.
return '';
}
$presetname = clean_filename($this->name) . '-preset-' . gmdate("Ymd_Hi");
$exportsubdir = "mod_data/presetexport/$presetname";
$exportdir = make_temp_directory($exportsubdir);
// Generate and write the preset.xml file.
$presetxmldata = static::generate_preset_xml();
$presetxmlfile = fopen($exportdir . '/preset.xml', 'w');
fwrite($presetxmlfile, $presetxmldata);
fclose($presetxmlfile);
// Write the template files.
$instance = $this->manager->get_instance();
foreach (manager::TEMPLATES_LIST as $templatename => $templatefilename) {
$templatefile = fopen("$exportdir/$templatefilename", 'w');
fwrite($templatefile, $instance->{$templatename});
fclose($templatefile);
}
// Check if all files have been generated.
if (! static::is_directory_a_preset($exportdir)) {
throw new \moodle_exception('generateerror', 'data');
}
$presetfilenames = array_merge(array_values(manager::TEMPLATES_LIST), ['preset.xml']);
$filelist = [];
foreach ($presetfilenames as $filename) {
$filelist[$filename] = $exportdir . '/' . $filename;
}
$exportfile = $exportdir.'.zip';
file_exists($exportfile) && unlink($exportfile);
$fp = get_file_packer('application/zip');
$fp->archive_to_pathname($filelist, $exportfile);
foreach ($filelist as $file) {
unlink($file);
}
rmdir($exportdir);
return $exportfile;
}
/**
* Return the preset author.
*
* @return int|null
*/
public function get_userid(): ?int {
if (!empty($this->storedfile)) {
return $this->storedfile->get_userid();
}
return null;
}
/**
* Returns the preset path.
*
* @return string|null the preset path is null for plugins and /presetname/ for saved presets.
*/
public function get_path(): ?string {
if ($this->isplugin) {
return null;
}
if (!empty($this->storedfile)) {
return $this->storedfile->get_filepath();
}
return '/' . $this->name . '/';
}
/**
* Checks if a directory contains all the required files to define a preset.
*
@@ -55,4 +298,179 @@ class preset {
return $pluginname;
}
}
/**
* Returns the description to show for a datapreset plugin.
*
* @param string $pluginname The datapreset plugin name.
* @return string The plugin preset description to display.
*/
public static function get_description_from_plugin(string $pluginname): string {
if (get_string_manager()->string_exists('modulename_help', 'datapreset_'.$pluginname)) {
return get_string('modulename_help', 'datapreset_'.$pluginname);
} else {
return '';
}
}
/**
* Helper to get the value of one of the elements in the presets.xml file.
*
* @param string $filepath The preset filepath.
* @param string $name Attribute name to return.
* @return string|null The attribute value; null if the it doesn't exist or the file is not a valid XML.
*/
protected static function get_attribute_value(string $filepath, string $name): ?string {
$value = null;
$presetxml = static::get_content_from_file($filepath, 'preset.xml');
$parsedxml = simplexml_load_string($presetxml);
if ($parsedxml) {
switch ($name) {
case 'description':
if (property_exists($parsedxml, 'description')) {
$value = $parsedxml->description;
}
break;
}
}
return $value;
}
/**
* Helper method to get a file record given a filename, a filepath and a userid, for any of the preset files.
*
* @param string $filename The filename for the filerecord that will be returned.
* @param string $filepath The filepath for the filerecord that will be returned.
* @param int $userid The userid for the filerecord that will be returned.
* @return stdClass A filerecord object with the datapreset context, component and filearea and the given information.
*/
protected static function get_filerecord(string $filename, string $filepath, int $userid): stdClass {
$filerecord = new stdClass;
$filerecord->contextid = DATA_PRESET_CONTEXT;
$filerecord->component = DATA_PRESET_COMPONENT;
$filerecord->filearea = DATA_PRESET_FILEAREA;
$filerecord->itemid = 0;
$filerecord->filepath = $filepath;
$filerecord->userid = $userid;
$filerecord->filename = $filename;
return $filerecord;
}
/**
* Helper method to retrieve a file.
*
* @param string $filepath the directory to look in
* @param string $filename the name of the file we want
* @return stored_file|null the file or null if the file doesn't exist.
*/
public static function get_file(string $filepath, string $filename): ?stored_file {
$file = null;
$fs = get_file_storage();
$fileexists = $fs->file_exists(
DATA_PRESET_CONTEXT,
DATA_PRESET_COMPONENT,
DATA_PRESET_FILEAREA,
0,
$filepath,
$filename
);
if ($fileexists) {
$file = $fs->get_file(
DATA_PRESET_CONTEXT,
DATA_PRESET_COMPONENT,
DATA_PRESET_FILEAREA,
0,
$filepath,
$filename
);
}
return $file;
}
/**
* Helper method to retrieve the contents of a file.
*
* @param string $filepath the directory to look in
* @param string $filename the name of the file we want
* @return string|null the contents of the file or null if the file doesn't exist.
*/
protected static function get_content_from_file(string $filepath, string $filename): ?string {
$templatefile = static::get_file($filepath, $filename);
if ($templatefile) {
return $templatefile->get_content();
}
return null;
}
/**
* Helper method to generate the XML for this preset.
*
* @return string The XML for the preset
*/
protected function generate_preset_xml(): string {
global $DB;
if ($this->isplugin) {
// Only saved presets can generate the preset.xml file.
return '';
}
$presetxmldata = "<preset>\n\n";
// Add description.
$presetxmldata .= '<description>' . htmlspecialchars($this->description) . "</description>\n\n";
// Add settings.
// Raw settings are not preprocessed during saving of presets.
$rawsettings = [
'intro',
'comments',
'requiredentries',
'requiredentriestoview',
'maxentries',
'rssarticles',
'approval',
'manageapproved',
'defaultsortdir',
];
$presetxmldata .= "<settings>\n";
$instance = $this->manager->get_instance();
// First, settings that do not require any conversion.
foreach ($rawsettings as $setting) {
$presetxmldata .= "<$setting>" . htmlspecialchars($instance->$setting) . "</$setting>\n";
}
// Now specific settings.
if ($instance->defaultsort > 0 && $sortfield = data_get_field_from_id($instance->defaultsort, $instance)) {
$presetxmldata .= '<defaultsort>' . htmlspecialchars($sortfield->field->name) . "</defaultsort>\n";
} else {
$presetxmldata .= "<defaultsort>0</defaultsort>\n";
}
$presetxmldata .= "</settings>\n\n";
// Add fields. Grab all that are non-empty.
$fields = $DB->get_records('data_fields', ['dataid' => $instance->id]);
ksort($fields);
if (!empty($fields)) {
foreach ($fields as $field) {
$presetxmldata .= "<field>\n";
foreach ($field as $key => $value) {
if ($value != '' && $key != 'id' && $key != 'dataid') {
$presetxmldata .= "<$key>" . htmlspecialchars($value) . "</$key>\n";
}
}
$presetxmldata .= "</field>\n\n";
}
}
$presetxmldata .= '</preset>';
// Check this content is a valid XML.
$preset = new SimpleXMLElement($presetxmldata);
return $preset->asXML();
}
}
+3 -2
View File
@@ -146,7 +146,8 @@ $string['entrieslefttoaddtoview'] = 'You must add {$a->entrieslefttoview} more e
$string['entry'] = 'Entry';
$string['entrysaved'] = 'Your entry has been saved';
$string['errormustbeteacher'] = 'You need to be a teacher to use this page!';
$string['errorpresetexists'] = 'There is already a preset with the selected name';
$string['errorpresetexists'] = 'A preset with this name already exists.';
$string['errorpresetexistsbutnotoverwrite'] = 'A preset with this name already exists. Choose a different name.';
$string['errormustsupplyvalue'] = 'You must supply a value here.';
$string['example'] = 'Database module example';
$string['excel'] = 'Excel';
@@ -307,7 +308,7 @@ $string['optionaldescription'] = 'Short description (optional)';
$string['optionalfilename'] = 'Filename (optional)';
$string['other'] = 'Other';
$string['overwrite'] = 'Overwrite';
$string['overrwritedesc'] = 'Overwrite the preset if it already exists';
$string['overrwritedesc'] = 'Replace existing preset with this name and overwrite its contents';
$string['overwritesettings'] = 'Overwrite current settings';
$string['page-mod-data-x'] = 'Any database activity module page';
$string['pagesize'] = 'Entries per page';
+37 -181
View File
@@ -3450,52 +3450,16 @@ function data_extend_settings_navigation(settings_navigation $settings, navigati
* @param stdClass $data The database record
* @param string $path
* @return bool
* @deprecated since Moodle 4.1 MDL-75142 - please, use the preset::save() function instead.
* @todo MDL-75189 This will be deleted in Moodle 4.5.
* @see preset::save()
*/
function data_presets_save($course, $cm, $data, $path) {
global $USER;
$fs = get_file_storage();
$filerecord = new stdClass;
$filerecord->contextid = DATA_PRESET_CONTEXT;
$filerecord->component = DATA_PRESET_COMPONENT;
$filerecord->filearea = DATA_PRESET_FILEAREA;
$filerecord->itemid = 0;
$filerecord->filepath = '/'.$path.'/';
$filerecord->userid = $USER->id;
debugging('data_presets_save() is deprecated. Please use preset::save() instead.', DEBUG_DEVELOPER);
$filerecord->filename = 'preset.xml';
$fs->create_file_from_string($filerecord, data_presets_generate_xml($course, $cm, $data));
$filerecord->filename = 'singletemplate.html';
$fs->create_file_from_string($filerecord, $data->singletemplate);
$filerecord->filename = 'listtemplateheader.html';
$fs->create_file_from_string($filerecord, $data->listtemplateheader);
$filerecord->filename = 'listtemplate.html';
$fs->create_file_from_string($filerecord, $data->listtemplate);
$filerecord->filename = 'listtemplatefooter.html';
$fs->create_file_from_string($filerecord, $data->listtemplatefooter);
$filerecord->filename = 'addtemplate.html';
$fs->create_file_from_string($filerecord, $data->addtemplate);
$filerecord->filename = 'rsstemplate.html';
$fs->create_file_from_string($filerecord, $data->rsstemplate);
$filerecord->filename = 'rsstitletemplate.html';
$fs->create_file_from_string($filerecord, $data->rsstitletemplate);
$filerecord->filename = 'csstemplate.css';
$fs->create_file_from_string($filerecord, $data->csstemplate);
$filerecord->filename = 'jstemplate.js';
$fs->create_file_from_string($filerecord, $data->jstemplate);
$filerecord->filename = 'asearchtemplate.html';
$fs->create_file_from_string($filerecord, $data->asearchtemplate);
return true;
$manager = manager::create_from_instance($data);
$preset = preset::create_from_instance($manager, $path);
return $preset->save();
}
/**
@@ -3506,151 +3470,42 @@ function data_presets_save($course, $cm, $data, $path) {
* @param stdClass $cm The course module record
* @param stdClass $data The database record
* @return string The XML for the preset
* @deprecated since Moodle 4.1 MDL-75142 - please, use the protected preset::generate_preset_xml() function instead.
* @todo MDL-75189 This will be deleted in Moodle 4.5.
* @see preset::generate_preset_xml()
*/
function data_presets_generate_xml($course, $cm, $data) {
global $DB;
// Assemble "preset.xml":
$presetxmldata = "<preset>\n\n";
// Raw settings are not preprocessed during saving of presets
$raw_settings = array(
'intro',
'comments',
'requiredentries',
'requiredentriestoview',
'maxentries',
'rssarticles',
'approval',
'manageapproved',
'defaultsortdir'
debugging(
'data_presets_generate_xml() is deprecated. Please use the protected preset::generate_preset_xml() instead.',
DEBUG_DEVELOPER
);
$presetxmldata .= "<settings>\n";
// First, settings that do not require any conversion
foreach ($raw_settings as $setting) {
$presetxmldata .= "<$setting>" . htmlspecialchars($data->$setting) . "</$setting>\n";
}
// Now specific settings
if ($data->defaultsort > 0 && $sortfield = data_get_field_from_id($data->defaultsort, $data)) {
$presetxmldata .= '<defaultsort>' . htmlspecialchars($sortfield->field->name) . "</defaultsort>\n";
} else {
$presetxmldata .= "<defaultsort>0</defaultsort>\n";
}
$presetxmldata .= "</settings>\n\n";
// Now for the fields. Grab all that are non-empty
$fields = $DB->get_records('data_fields', array('dataid'=>$data->id));
ksort($fields);
if (!empty($fields)) {
foreach ($fields as $field) {
$presetxmldata .= "<field>\n";
foreach ($field as $key => $value) {
if ($value != '' && $key != 'id' && $key != 'dataid') {
$presetxmldata .= "<$key>" . htmlspecialchars($value) . "</$key>\n";
}
}
$presetxmldata .= "</field>\n\n";
}
}
$presetxmldata .= '</preset>';
return $presetxmldata;
$manager = manager::create_from_instance($data);
$preset = preset::create_from_instance($manager, $data->name);
$reflection = new \ReflectionClass(preset::class);
$method = $reflection->getMethod('generate_preset_xml');
$method->setAccessible(true);
return $method->invokeArgs($preset, []);
}
/**
* Export current fields and presets.
*
* @param stdClass $course The course the database module belongs to.
* @param stdClass $cm The course module record
* @param stdClass $data The database record
* @param bool $tostorage
* @return string the full path to the exported preset file.
* @deprecated since Moodle 4.1 MDL-75142 - please, use the preset::export() function instead.
* @todo MDL-75189 This will be deleted in Moodle 4.5.
* @see preset::export()
*/
function data_presets_export($course, $cm, $data, $tostorage=false) {
global $CFG, $DB;
debugging('data_presets_export() is deprecated. Please use preset::export() instead.', DEBUG_DEVELOPER);
$presetname = clean_filename($data->name) . '-preset-' . gmdate("Ymd_Hi");
$exportsubdir = "mod_data/presetexport/$presetname";
make_temp_directory($exportsubdir);
$exportdir = "$CFG->tempdir/$exportsubdir";
// Assemble "preset.xml":
$presetxmldata = data_presets_generate_xml($course, $cm, $data);
// After opening a file in write mode, close it asap
$presetxmlfile = fopen($exportdir . '/preset.xml', 'w');
fwrite($presetxmlfile, $presetxmldata);
fclose($presetxmlfile);
// Now write the template files
$singletemplate = fopen($exportdir . '/singletemplate.html', 'w');
fwrite($singletemplate, $data->singletemplate);
fclose($singletemplate);
$listtemplateheader = fopen($exportdir . '/listtemplateheader.html', 'w');
fwrite($listtemplateheader, $data->listtemplateheader);
fclose($listtemplateheader);
$listtemplate = fopen($exportdir . '/listtemplate.html', 'w');
fwrite($listtemplate, $data->listtemplate);
fclose($listtemplate);
$listtemplatefooter = fopen($exportdir . '/listtemplatefooter.html', 'w');
fwrite($listtemplatefooter, $data->listtemplatefooter);
fclose($listtemplatefooter);
$addtemplate = fopen($exportdir . '/addtemplate.html', 'w');
fwrite($addtemplate, $data->addtemplate);
fclose($addtemplate);
$rsstemplate = fopen($exportdir . '/rsstemplate.html', 'w');
fwrite($rsstemplate, $data->rsstemplate);
fclose($rsstemplate);
$rsstitletemplate = fopen($exportdir . '/rsstitletemplate.html', 'w');
fwrite($rsstitletemplate, $data->rsstitletemplate);
fclose($rsstitletemplate);
$csstemplate = fopen($exportdir . '/csstemplate.css', 'w');
fwrite($csstemplate, $data->csstemplate);
fclose($csstemplate);
$jstemplate = fopen($exportdir . '/jstemplate.js', 'w');
fwrite($jstemplate, $data->jstemplate);
fclose($jstemplate);
$asearchtemplate = fopen($exportdir . '/asearchtemplate.html', 'w');
fwrite($asearchtemplate, $data->asearchtemplate);
fclose($asearchtemplate);
// Check if all files have been generated
if (! preset::is_directory_a_preset($exportdir)) {
throw new \moodle_exception('generateerror', 'data');
}
$filenames = array(
'preset.xml',
'singletemplate.html',
'listtemplateheader.html',
'listtemplate.html',
'listtemplatefooter.html',
'addtemplate.html',
'rsstemplate.html',
'rsstitletemplate.html',
'csstemplate.css',
'jstemplate.js',
'asearchtemplate.html'
);
$filelist = array();
foreach ($filenames as $filename) {
$filelist[$filename] = $exportdir . '/' . $filename;
}
$exportfile = $exportdir.'.zip';
file_exists($exportfile) && unlink($exportfile);
$fp = get_file_packer('application/zip');
$fp->archive_to_pathname($filelist, $exportfile);
foreach ($filelist as $file) {
unlink($file);
}
rmdir($exportdir);
// Return the full path to the exported preset file:
return $exportfile;
$manager = manager::create_from_instance($data);
$preset = preset::create_from_instance($manager, $data->name);
return $preset->export();
}
/**
@@ -3999,7 +3854,8 @@ function data_user_can_delete_preset($context, $preset) {
return true;
} else {
$candelete = false;
if ($preset->userid == $USER->id) {
$userid = $preset instanceof preset ? $preset->get_userid() : $preset->userid;
if ($userid == $USER->id) {
$candelete = true;
}
return $candelete;
+3 -1
View File
@@ -29,6 +29,7 @@
*/
use mod_data\manager;
use mod_data\preset;
require_once('../../config.php');
require_once($CFG->dirroot.'/mod/data/lib.php');
@@ -84,7 +85,8 @@ if ($action === 'export') {
throw new \moodle_exception('headersent');
}
$exportfile = data_presets_export($course, $cm, $data);
$preset = preset::create_from_instance($manager, $data->name);
$exportfile = $preset->export();
$exportfilename = basename($exportfile);
header("Content-Type: application/download\n");
header("Content-Disposition: attachment; filename=\"$exportfilename\"");
@@ -24,5 +24,6 @@
*/
$string['modulename'] = 'Image gallery';
$string['modulename_help'] = 'Use this preset to collect images.';
$string['pluginname'] = 'Image gallery';
$string['privacy:metadata'] = 'The Image gallery plugin does not store any personal data.';
+2 -1
View File
@@ -17,7 +17,8 @@ class data_existing_preset_form extends moodleform {
$this->_form->setType('action', PARAM_ALPHANUM);
$delete = get_string('delete');
foreach ($this->_customdata['presets'] as $preset) {
$this->_form->addElement('radio', 'fullname', null, ' '.$preset->description, $preset->userid.'/'.$preset->shortname);
$userid = $preset instanceof \mod_data\preset ? $preset->get_userid() : $preset->userid;
$this->_form->addElement('radio', 'fullname', null, ' '.$preset->description, $userid.'/'.$preset->shortname);
}
$this->_form->addElement('submit', 'importexisting', get_string('choose'));
}
+4 -1
View File
@@ -31,6 +31,7 @@
"name": "Image gallery",
"shortname": "imagegallery",
"fullname": "Image gallery",
"description": "Use this preset to collect images",
"userid": 0,
"actions": []
},
@@ -56,7 +57,8 @@
<thead>
<tr>
<th class="pl-4 border-top-0" scope="col" style="width: 3%"></th>
<th class="pl-4 border-top-0" scope="col">{{#str}} name {{/str}}</th>
<th class="pl-4 border-top-0" scope="col" style="width:30%">{{#str}} name {{/str}}</th>
<th class="pl-4 border-top-0" scope="col">{{#str}} description {{/str}}</th>
<th class="pl-4 border-top-0" scope="col" style="width: 3%">{{#showmanage}}{{#str}} action {{/str}}{{/showmanage}}</th>
</tr>
</thead>
@@ -67,6 +69,7 @@
<input type="radio" name="fullname" value="{{userid}}/{{shortname}}" />
</td>
<td class="p-4">{{fullname}}</td>
<td class="p-4">{{description}}</td>
<td class="p-4 preset_action_menu">
{{#actions}}
<div class="float-right">
+62 -3
View File
@@ -18,9 +18,9 @@ Feature: Users can view and manage data presets
| 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 |
| database | name | description |
| data1 | Saved preset 1 | The preset1 has description |
| data1 | Saved preset 2 | |
@javascript
Scenario: Admins can delete saved presets
@@ -51,7 +51,9 @@ Feature: Users can view and manage data presets
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 "Use this preset to collect images." in the "Image gallery" "table_row"
And I should see "Saved preset 1"
And I should see "The preset1 has description" in the "Saved preset 1" "table_row"
And I should see "Saved preset 2"
And I should see "Saved preset by teacher1"
# Plugin presets can't be removed.
@@ -79,3 +81,60 @@ Feature: Users can view and manage data presets
Then I should see "Image gallery"
And I should not see "Saved preset 1"
And I should not see "Saved preset 2"
@javascript
Scenario: Teachers can save 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"
When I click on "Save as preset" "button"
Then I should see "Name" in the "Save all fields and templates as preset" "dialogue"
And I should see "Description" in the "Save all fields and templates as preset" "dialogue"
And "Replace existing preset with this name and overwrite its contents" "checkbox" should not be visible
# Teacher should be able to save preset.
And I set the field "Name" to "Saved preset by teacher1"
And I set the field "Description" to "My funny description goes here."
And I click on "Save" "button" in the "Save all fields and templates as preset" "dialogue"
And I should see "Saved successfully. Your preset will now be available across the site."
And I follow "Presets"
And I should see "Saved preset by teacher1"
And I should see "My funny description goes here." in the "Saved preset by teacher1" "table_row"
# Teacher can't overwrite an existing preset that they haven't created.
And I follow "Templates"
And I click on "Save as preset" "button"
And I set the field "Name" to "Saved preset 1"
And I click on "Save" "button" in the "Save all fields and templates as preset" "dialogue"
And I should see "A preset with this name already exists. Choose a different name."
And "Replace existing preset with this name and overwrite its contents" "checkbox" should not be visible
# Teacher can overwrite existing presets created by them, but they are not overwritten if the checkbox is not marked.
And I set the field "Name" to "Saved preset by teacher1"
And I set the field "Description" to "This is a new description that shouldn't be saved."
And I click on "Save" "button" in the "Save all fields and templates as preset" "dialogue"
And I should see "A preset with this name already exists."
And "Replace existing preset with this name and overwrite its contents" "checkbox" should be visible
# Confirm the checkbox is still displayed and nothing happens if it's not checked and no change is done in the name.
And I click on "Save" "button" in the "Save all fields and templates as preset" "dialogue"
And I should see "A preset with this name already exists."
And "Replace existing preset with this name and overwrite its contents" "checkbox" should be visible
And I click on "Cancel" "button" in the "Save all fields and templates as preset" "dialogue"
And I follow "Presets"
And I should see "Saved preset by teacher1"
And I should see "My funny description goes here." in the "Saved preset by teacher1" "table_row"
And I should not see "This is a new description that shouldn't be saved."
# But teacher can overwrite existing presets created by them.
But I follow "Templates"
And I click on "Save as preset" "button"
And I set the field "Name" to "Saved preset by teacher1"
And I set the field "Description" to "This is a new description that will be overwritten."
And I click on "Save" "button" in the "Save all fields and templates as preset" "dialogue"
And I should see "A preset with this name already exists."
And "Replace existing preset with this name and overwrite its contents" "checkbox" should be visible
And I click on "Replace existing preset with this name and overwrite its contents" "checkbox" in the "Save all fields and templates as preset" "dialogue"
And I click on "Save" "button" in the "Save all fields and templates as preset" "dialogue"
And I should see "Saved successfully. Your preset will now be available across the site."
And I follow "Presets"
And I should see "Saved preset by teacher1"
And I should see "This is a new description that will be overwritten." in the "Saved preset by teacher1" "table_row"
And I should not see "My funny description goes here." in the "Saved preset by teacher1" "table_row"
+15 -9
View File
@@ -24,6 +24,7 @@
*/
use mod_data\manager;
use mod_data\preset;
defined('MOODLE_INTERNAL') || die();
@@ -369,22 +370,27 @@ class mod_data_generator extends testing_module_generator {
*
* @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.
* @return preset The preset that has been created.
*/
public function create_preset(stdClass $instance, stdClass $record = null): bool {
global $DB;
public function create_preset(stdClass $instance, stdClass $record = null): preset {
if (is_null($record)) {
$record = new stdClass();
}
// Fill in optional values if not specified.
if (!isset($record->name)) {
$record->name = 'New preset ' . microtime();
$presetname = 'New preset ' . microtime();
if (isset($record->name)) {
$presetname = $record->name;
}
$presetdescription = null;
if (isset($record->description)) {
$presetdescription = $record->description;
}
$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);
$manager = manager::create_from_instance($instance);
$preset = preset::create_from_instance($manager, $presetname, $presetdescription);
$preset->save();
return $preset;
}
}
+53 -19
View File
@@ -16,6 +16,8 @@
namespace mod_data;
use stdClass;
/**
* PHPUnit data generator testcase.
*
@@ -234,9 +236,13 @@ class generator_test extends \advanced_testcase {
/**
* Test for create_preset().
*
* @dataProvider create_preset_provider
* @covers ::create_preset
* @param stdClass|null $record data for the preset that will be created (like name or description)
*/
public function test_create_preset() {
public function test_create_preset(?stdClass $record) {
global $USER;
$this->resetAfterTest();
$this->setAdminUser();
@@ -249,27 +255,55 @@ class generator_test extends \advanced_testcase {
$savedpresets = $manager->get_available_saved_presets();
$this->assertEmpty($savedpresets);
// Create one preset with the default configuration.
// Create one preset with the configuration in $record.
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$result = $plugingenerator->create_preset($activity);
$this->assertTrue($result);
$preset = $plugingenerator->create_preset($activity, $record);
// Check the preset has been saved.
$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);
}
// Check the preset name has the expected value.
if (is_null($record) || !property_exists($record, 'name')) {
$this->assertStringStartsWith('New preset', $preset->name);
} else {
$this->assertEquals($record->name, $preset->name);
}
// Check the preset description has the expected value.
if (is_null($record) || !property_exists($record, 'description')) {
$this->assertEmpty($preset->description);
} else {
$this->assertEquals($record->description, $preset->description);
}
// Check the file has been updated properly.
$this->assertNotNull($preset->storedfile);
$this->assertEquals($USER->id, $preset->get_userid());
}
/**
* Data provider for test_create_preset().
*
* @return array
*/
public function create_preset_provider(): array {
return [
'Create using the default configuration' => [
'record' => null,
],
'Create with a given name but no description' => [
'record' => (object) [
'name' => 'World recipes preset',
],
],
'Create with a given description but no name' => [
'record' => (object) [
'description' => 'This is a preset to collect the most popular world recipes.',
],
],
'Create with a given name and description' => [
'record' => (object) [
'name' => 'World recipes preset',
'description' => 'This is a preset to collect the most popular world recipes.',
],
],
];
}
}
+437
View File
@@ -16,6 +16,10 @@
namespace mod_data;
use file_archive;
use stdClass;
use zip_archive;
/**
* Preset tests class for mod_data.
*
@@ -27,6 +31,306 @@ namespace mod_data;
*/
class preset_test extends \advanced_testcase {
/**
* Test for static create_from_plugin method.
*
* @covers ::create_from_plugin
*/
public function test_create_from_plugin() {
$this->resetAfterTest();
$this->setAdminUser();
// Check create_from_plugin is working as expected when an existing plugin is given.
$pluginname = 'imagegallery';
$result = preset::create_from_plugin(null, $pluginname);
$this->assertTrue($result->isplugin);
$this->assertEquals(get_string('modulename', "datapreset_$pluginname"), $result->name);
$this->assertEquals($pluginname, $result->shortname);
$this->assertEquals(get_string('modulename_help', "datapreset_$pluginname"), $result->description);
$this->assertEmpty($result->get_userid());
$this->assertEmpty($result->storedfile);
$this->assertNull($result->get_path());
// Check create_from_plugin is working as expected when an unexisting plugin is given.
$pluginname = 'unexisting';
$result = preset::create_from_plugin(null, $pluginname);
$this->assertNull($result);
}
/**
* Test for static create_from_storedfile method.
*
* @covers ::create_from_storedfile
*/
public function test_create_from_storedfile() {
global $USER;
$this->resetAfterTest();
$this->setAdminUser();
// Create a course and a database activity.
$course = $this->getDataGenerator()->create_course();
$activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
$manager = manager::create_from_instance($activity);
// Create a saved preset.
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$record = (object) [
'name' => 'Testing preset name',
'description' => 'Testing preset description',
];
$plugingenerator->create_preset($activity, $record);
$savedpresets = $manager->get_available_saved_presets();
$savedpreset = reset($savedpresets);
// Check create_from_storedfile is working as expected with a valid preset file.
$result = preset::create_from_storedfile($manager, $savedpreset->storedfile);
$this->assertFalse($result->isplugin);
$this->assertEquals($record->name, $result->name);
$this->assertEquals($record->name, $result->shortname);
$this->assertEquals($record->description, $result->description);
$this->assertEquals($savedpreset->storedfile->get_userid(), $result->get_userid());
$this->assertNotEmpty($result->storedfile);
$this->assertEquals('/' . $record->name . '/', $result->get_path());
// Check create_from_storedfile is not creating a preset object when an invalid file is given.
$draftid = file_get_unused_draft_itemid();
$filerecord = [
'component' => 'user',
'filearea' => 'draft',
'contextid' => \context_user::instance($USER->id)->id,
'itemid' => $draftid,
'filename' => 'preset.xml',
'filepath' => '/'
];
$fs = get_file_storage();
$file = $fs->create_file_from_string($filerecord, 'This is the file content');
$result = preset::create_from_storedfile($manager, $file);
$this->assertNull($result);
}
/**
* Test for static create_from_instance method.
*
* @covers ::create_from_instance
*/
public function test_create_from_instance() {
$this->resetAfterTest();
$this->setAdminUser();
// Create a course and a database activity.
$course = $this->getDataGenerator()->create_course();
$activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
$manager = manager::create_from_instance($activity);
// Create a saved preset.
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$record = (object) [
'name' => 'Testing preset name',
'description' => 'Testing preset description',
];
$plugingenerator->create_preset($activity, $record);
$savedpresets = $manager->get_available_saved_presets();
$savedpreset = reset($savedpresets);
// Check create_from_instance is working as expected when a preset with this name exists.
$result = preset::create_from_instance($manager, $record->name, $record->description);
$this->assertFalse($result->isplugin);
$this->assertEquals($record->name, $result->name);
$this->assertEquals($record->name, $result->shortname);
$this->assertEquals($record->description, $result->description);
$this->assertEquals($savedpreset->storedfile->get_userid(), $result->get_userid());
$this->assertNotEmpty($result->storedfile);
$this->assertEquals('/' . $record->name . '/', $result->get_path());
// Check create_from_instance is working as expected when there is no preset with the given name.
$presetname = 'Unexisting preset';
$presetdescription = 'This is the description for the unexisting preset';
$result = preset::create_from_instance($manager, $presetname, $presetdescription);
$this->assertFalse($result->isplugin);
$this->assertEquals($presetname, $result->name);
$this->assertEquals($presetname, $result->shortname);
$this->assertEquals($presetdescription, $result->description);
$this->assertEmpty($result->get_userid());
$this->assertEmpty($result->storedfile);
$this->assertEquals('/' . $presetname . '/', $result->get_path());
}
/**
* Test for the save a preset method.
*
* @covers ::save
*/
public function test_save() {
$this->resetAfterTest();
$this->setAdminUser();
// Save should return false when trying to save a plugin preset.
$preset = preset::create_from_plugin(null, 'imagegallery');
$result = $preset->save();
$this->assertFalse($result);
// Create a course and a database activity.
$course = $this->getDataGenerator()->create_course();
$activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
$manager = manager::create_from_instance($activity);
// Add a field to the activity.
$fieldrecord = new stdClass();
$fieldrecord->name = 'field-1';
$fieldrecord->type = 'text';
$datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$datagenerator->create_field($fieldrecord, $activity);
// Create a saved preset.
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$record = (object) [
'name' => 'Testing preset name',
'description' => 'Testing preset description',
];
$plugingenerator->create_preset($activity, $record);
// Save should return false when trying to save an existing saved preset.
$preset = preset::create_from_instance($manager, $record->name, $record->description);
$result = $preset->save();
$this->assertFalse($result);
// The preset should be saved when it's new and there is no any other having the same name.
$savedpresets = $manager->get_available_saved_presets();
$this->assertCount(1, $savedpresets);
$presetname = 'New preset';
$presetdescription = 'This is the description for the new preset';
$preset = preset::create_from_instance($manager, $presetname, $presetdescription);
$result = $preset->save();
$this->assertTrue($result);
// Check the preset has been created.
$savedpresets = $manager->get_available_saved_presets();
$this->assertCount(2, $savedpresets);
$savedpresetsnames = array_map(function($preset) {
return $preset->name;
}, $savedpresets);
$this->assertContains($presetname, $savedpresetsnames);
}
/**
* Test for the export a preset method.
*
* @covers ::export
*/
public function test_export() {
$this->resetAfterTest();
$this->setAdminUser();
// Export should return empty string when trying to export a plugin preset.
$preset = preset::create_from_plugin(null, 'imagegallery');
$result = $preset->export();
$this->assertEmpty($result);
// Create a course and a database activity.
$course = $this->getDataGenerator()->create_course();
$activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
$manager = manager::create_from_instance($activity);
// Add a field to the activity.
$fieldrecord = new stdClass();
$fieldrecord->name = 'field-1';
$fieldrecord->type = 'text';
$datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$datagenerator->create_field($fieldrecord, $activity);
// For now, default templates are not created automatically. This will be changed in MDL-75234.
foreach (manager::TEMPLATES_LIST as $templatename => $notused) {
data_generate_default_template($activity, $templatename);
}
$preset = preset::create_from_instance($manager, $activity->name);
$result = $preset->export();
$presetfilenames = array_merge(array_values(manager::TEMPLATES_LIST), ['preset.xml']);
$ziparchive = new zip_archive();
$ziparchive->open($result, file_archive::OPEN);
$files = $ziparchive->list_files();
foreach ($files as $file) {
$this->assertContains($file->pathname, $presetfilenames);
// Check the file is not empty (except CSS, JS and listtemplateheader/footer files which are empty by default).
$ishtmlorxmlfile = str_ends_with($file->pathname, '.html') || str_ends_with($file->pathname, '.xml');
$islistheader = $file->pathname != manager::TEMPLATES_LIST['listtemplateheader'];
$islistfooter = $file->pathname != manager::TEMPLATES_LIST['listtemplatefooter'];
if ($ishtmlorxmlfile && !$islistheader && !$islistfooter) {
$this->assertGreaterThan(0, $file->size);
}
}
$ziparchive->close();
}
/**
* Test for get_userid().
*
* @covers ::get_userid
*/
public function test_get_userid() {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
$user = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
$this->setUser($user);
// Check userid is null for plugin preset.
$manager = manager::create_from_instance($activity);
$pluginpresets = $manager->get_available_plugin_presets();
$pluginpreset = reset($pluginpresets);
$this->assertNull($pluginpreset->get_userid());
// Check userid meets the user that has created the preset when it's a saved preset.
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$savedpreset = (object) [
'name' => 'Preset created by teacher',
];
$plugingenerator->create_preset($activity, $savedpreset);
$savedpresets = $manager->get_available_saved_presets();
$savedpreset = reset($savedpresets);
$this->assertEquals($user->id, $savedpreset->get_userid());
// Check userid is null when preset hasn't any file associated.
$preset = preset::create_from_instance($manager, 'Unexisting preset');
$this->assertNull($preset->get_userid());
}
/**
* Test for get_path().
*
* @covers ::get_path
*/
public function test_get_path() {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
// Check path is null for plugin preset.
$manager = manager::create_from_instance($activity);
$pluginpresets = $manager->get_available_plugin_presets();
$pluginpreset = reset($pluginpresets);
$this->assertNull($pluginpreset->get_path());
// Check path meets expected value when it's a saved preset.
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$savedpreset = (object) [
'name' => 'Saved preset',
];
$plugingenerator->create_preset($activity, $savedpreset);
$savedpresets = $manager->get_available_saved_presets();
$savedpreset = reset($savedpresets);
$this->assertEquals("/{$savedpreset->name}/", $savedpreset->get_path());
// Check path is /presetname/ when preset hasn't any file associated.
$presetname = 'Unexisting preset';
$preset = preset::create_from_instance($manager, $presetname);
$this->assertEquals("/{$presetname}/", $preset->get_path());
}
/**
* Test for is_directory_a_preset().
*
@@ -86,4 +390,137 @@ class preset_test extends \advanced_testcase {
$this->assertEquals($presetshortname, $name);
}
/**
* Test for get_description_from_plugin().
*
* @covers ::get_description_from_plugin
*/
public function test_get_description_from_plugin() {
$this->resetAfterTest();
$this->setAdminUser();
// The expected name for plugins with modulename in lang is this value.
$description = preset::get_description_from_plugin('imagegallery');
$this->assertEquals('Use this preset to collect images.', $description);
// However, if the plugin doesn't exist or the modulename is not defined, empty string will be returned.
$presetshortname = 'nonexistingpreset';
$description = preset::get_description_from_plugin($presetshortname);
$this->assertEmpty($description);
}
/**
* Test for generate_preset_xml().
*
* @covers ::generate_preset_xml
* @dataProvider generate_preset_xml_provider
* @param array $params activity config settings
* @param string|null $description preset description
*/
public function test_generate_preset_xml(array $params, ?string $description) {
$this->resetAfterTest();
$this->setAdminUser();
// Make accessible the method.
$reflection = new \ReflectionClass(preset::class);
$method = $reflection->getMethod('generate_preset_xml');
$method->setAccessible(true);
// The method should return empty string when trying to generate preset.xml for a plugin preset.
$preset = preset::create_from_plugin(null, 'imagegallery');
$result = $method->invokeArgs($preset, []);
$this->assertEmpty($result);
// Create a course and a database activity.
$course = $this->getDataGenerator()->create_course();
$activity = $this->getDataGenerator()->create_module(manager::MODULE, array_merge(['course' => $course], $params));
// Add a field to the activity.
$fieldrecord = new stdClass();
$fieldrecord->name = 'field-1';
$fieldrecord->type = 'text';
$datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$datagenerator->create_field($fieldrecord, $activity);
$manager = manager::create_from_instance($activity);
$preset = preset::create_from_instance($manager, $activity->name, $description);
// Call the generate_preset_xml method.
$result = $method->invokeArgs($preset, []);
// Check is a valid XML.
$parsedxml = simplexml_load_string($result);
// Check the description has the expected value.
$this->assertEquals($description, strval($parsedxml->description));
// Check settings have the expected values.
foreach ($params as $paramname => $paramvalue) {
$this->assertEquals($paramvalue, strval($parsedxml->settings->{$paramname}));
}
// Check field have the expected values.
$this->assertEquals($fieldrecord->name, strval($parsedxml->field->name));
$this->assertEquals($fieldrecord->type, strval($parsedxml->field->type));
}
/**
* Data provider for generate_preset_xml().
*
* @return array
*/
public function generate_preset_xml_provider(): array {
return [
'Generate preset.xml with the default params and empty description' => [
'params' => [],
'description' => null,
],
'Generate preset.xml with a description but the default params' => [
'params' => [],
'description' => 'This is a description',
],
'Generate preset.xml with empty description but changing some params' => [
'params' => [
'requiredentries' => 2,
'approval' => 1,
],
'description' => null,
],
'Generate preset.xml with a description and changing some params' => [
'params' => [
'maxentries' => 5,
'manageapproved' => 0,
],
'description' => 'This is a description',
],
];
}
/**
* Test for get_file().
*
* @covers ::get_file
*/
public function test_get_file() {
$this->resetAfterTest();
$this->setAdminUser();
// Create a course and a database activity.
$course = $this->getDataGenerator()->create_course();
$activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
$manager = manager::create_from_instance($activity);
$presetname = 'Saved preset';
// Check file doesn't exist if the preset hasn't been saved yet.
$preset = preset::create_from_instance($manager, $presetname);
$file = preset::get_file($preset->get_path(), 'preset.xml');
$this->assertNull($file);
// Check file is not empty when there is a saved preset with this name.
$preset->save();
$file = preset::get_file($preset->get_path(), 'preset.xml');
$this->assertNotNull($file);
$this->assertStringContainsString($presetname, $file->get_filepath());
$this->assertEquals('preset.xml', $file->get_filename());
// Check invalid preset file name doesn't exist.
$file = preset::get_file($preset->get_path(), 'unexistingpreset.xml');
$this->assertNull($file);
}
}
+3
View File
@@ -9,6 +9,9 @@ information provided here is intended especially for developers.
- data_get_available_presets
- data_get_available_site_presets
- data_preset_name
- data_presets_export
- data_presets_generate_xml
- data_presets_save
- is_directory_a_preset
=== 3.7 ===