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:
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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.';
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ===
|
||||
|
||||
Reference in New Issue
Block a user