From b755cfa544f6d591a6599de73ca01211d4416dae Mon Sep 17 00:00:00 2001 From: Amaia Anabitarte Date: Thu, 3 Nov 2022 15:15:41 +0100 Subject: [PATCH] MDL-75492 mod_data: Refactor code to get available importing information --- .../local/importer/preset_importer.php | 191 ++++++++----- mod/data/renderer.php | 16 +- mod/data/tests/behat/import_presets.feature | 94 ++++++- mod/data/tests/behat/use_presets.feature | 186 +++++++++++++ mod/data/tests/preset_importer_test.php | 263 +++++++++++++++--- 5 files changed, 631 insertions(+), 119 deletions(-) create mode 100644 mod/data/tests/behat/use_presets.feature diff --git a/mod/data/classes/local/importer/preset_importer.php b/mod/data/classes/local/importer/preset_importer.php index 12a9b7c1729..5382f64b8d9 100644 --- a/mod/data/classes/local/importer/preset_importer.php +++ b/mod/data/classes/local/importer/preset_importer.php @@ -36,6 +36,18 @@ abstract class preset_importer { /** @var string directory where to find the preset. */ protected $directory; + /** @var array fields to remove. */ + public $fieldstoremove; + + /** @var array fields to update. */ + public $fieldstoupdate; + + /** @var array fields to create. */ + public $fieldstocreate; + + /** @var array settings to be imported. */ + public $settings; + /** * Constructor * @@ -45,6 +57,9 @@ abstract class preset_importer { public function __construct(manager $manager, string $directory) { $this->manager = $manager; $this->directory = $directory; + + // Read the preset and saved result. + $this->settings = $this->get_preset_settings(); } /** @@ -176,22 +191,33 @@ abstract class preset_importer { } // Now work out fields to user friendly array. - $fieldsarray = $parsedxml['preset']['#']['field']; - foreach ($fieldsarray as $field) { - if (!is_array($field)) { - continue; - } - $fieldstoimport = new StdClass(); - foreach ($field['#'] as $param => $value) { - if (!is_array($value)) { + if ( + array_key_exists('preset', $parsedxml) && + array_key_exists('#', $parsedxml['preset']) && + array_key_exists('field', $parsedxml['preset']['#'])) { + $fieldsarray = $parsedxml['preset']['#']['field']; + foreach ($fieldsarray as $field) { + if (!is_array($field)) { continue; } - $fieldstoimport->$param = $value[0]['#']; + $fieldstoimport = new StdClass(); + foreach ($field['#'] as $param => $value) { + if (!is_array($value)) { + continue; + } + $fieldstoimport->$param = $value[0]['#']; + } + $fieldstoimport->dataid = $module->id; + $fieldstoimport->type = clean_param($fieldstoimport->type, PARAM_ALPHA); + $result->importfields[] = $fieldstoimport; } - $fieldstoimport->dataid = $module->id; - $fieldstoimport->type = clean_param($fieldstoimport->type, PARAM_ALPHA); - $result->importfields[] = $fieldstoimport; } + + // Calculate default mapping. + if (is_null($this->fieldstoremove) && is_null($this->fieldstocreate) && is_null($this->fieldstoupdate)) { + $this->set_affected_fields($result->importfields, $result->currentfields); + } + // Now add the HTML templates to the settings array so we can update d. foreach (manager::TEMPLATES_LIST as $templatename => $templatefile) { $result->settings->$templatename = $this->get_file_contents( @@ -215,63 +241,44 @@ abstract class preset_importer { public function import(bool $overwritesettings): bool { global $DB, $OUTPUT; - $params = $this->get_preset_settings(); - $settings = $params->settings; - $newfields = $params->importfields; - $currentfields = $params->currentfields; - $preservedfields = []; + $settings = $this->settings->settings; + $currentfields = $this->settings->currentfields; + $missingfieldtypes = []; $module = $this->manager->get_instance(); - // Maps fields and makes new ones. - if (!empty($newfields)) { - // We require an injective mapping, and need to know what to protect. - foreach ($newfields as $newid => $newfield) { - $cid = optional_param("field_$newid", -1, PARAM_INT); - if ($cid == -1) { - continue; - } - if (array_key_exists($cid, $preservedfields)) { - throw new \moodle_exception('notinjectivemap', 'data'); - } else { - $preservedfields[$cid] = true; - } - } - - foreach ($newfields as $newid => $newfield) { - $cid = optional_param("field_$newid", -1, PARAM_INT); - - /* A mapping. Just need to change field params. Data kept. */ - if ($cid != -1 && isset($currentfields[$cid])) { - $fieldobject = data_get_field_from_id($currentfields[$cid]->id, $module); - foreach ($newfield as $param => $value) { - if ($param != "id") { - $fieldobject->field->$param = $value; - } + foreach ($this->fieldstoupdate as $currentid => $updatable) { + if ($currentid != -1 && isset($currentfields[$currentid])) { + $fieldobject = data_get_field_from_id($currentfields[$currentid]->id, $module); + foreach ($updatable as $param => $value) { + if ($param != "id") { + $fieldobject->field->$param = $value; } - unset($fieldobject->field->similarfield); - $fieldobject->update_field(); - unset($fieldobject); - } else { - /* Make a new field */ - $filepath = "field/$newfield->type/field.class.php"; - if (!file_exists($filepath)) { - $missingfieldtypes[] = $newfield->name; - continue; - } - include_once($filepath); - - if (!isset($newfield->description)) { - $newfield->description = ''; - } - $classname = 'data_field_'.$newfield->type; - $fieldclass = new $classname($newfield, $module); - $fieldclass->insert_field(); - unset($fieldclass); } + unset($fieldobject->field->similarfield); + $fieldobject->update_field(); + unset($fieldobject); } - if (!empty($missingfieldtypes)) { - echo $OUTPUT->notification(get_string('missingfieldtypeimport', 'data') . html_writer::alist($missingfieldtypes)); + } + + foreach ($this->fieldstocreate as $newfield) { + /* Make a new field */ + $filepath = "field/$newfield->type/field.class.php"; + if (!file_exists($filepath)) { + $missingfieldtypes[] = $newfield->name; + continue; } + include_once($filepath); + + if (!isset($newfield->description)) { + $newfield->description = ''; + } + $classname = 'data_field_' . $newfield->type; + $fieldclass = new $classname($newfield, $module); + $fieldclass->insert_field(); + unset($fieldclass); + } + if (!empty($missingfieldtypes)) { + echo $OUTPUT->notification(get_string('missingfieldtypeimport', 'data') . html_writer::alist($missingfieldtypes)); } // Get rid of all old unused data. @@ -328,6 +335,59 @@ abstract class preset_importer { return $this->cleanup(); } + /** + * Returns information about the fields needs to be removed, updated or created. + * + * @param array $newfields Array of new fields to be applied. + * @param array $currentfields Array of current fields on database activity. + * @return void + */ + public function set_affected_fields(array $newfields = [], array $currentfields = []): void { + $fieldstoremove = []; + $fieldstocreate = []; + $preservedfields = []; + + // Maps fields and makes new ones. + if (!empty($newfields)) { + // We require an injective mapping, and need to know what to protect. + foreach ($newfields as $newid => $newfield) { + $preservedfieldid = optional_param("field_$newid", -1, PARAM_INT); + + if (array_key_exists($preservedfieldid, $preservedfields)) { + throw new \moodle_exception('notinjectivemap', 'data'); + } + + if ($preservedfieldid == -1) { + // Let's check if there is any field with same type and name that we could map to. + foreach ($currentfields as $currentid => $currentfield) { + if (($currentfield->type == $newfield->type) && + ($currentfield->name == $newfield->name) && !array_key_exists($currentid, $preservedfields)) { + // We found a possible default map. + $preservedfieldid = $currentid; + $preservedfields[$currentid] = $newfield; + } + } + } + if ($preservedfieldid == -1) { + // We need to create a new field. + $fieldstocreate[] = $newfield; + } else { + $preservedfields[$preservedfieldid] = $newfield; + } + } + } + + foreach ($currentfields as $currentid => $currentfield) { + if (!array_key_exists($currentid, $preservedfields)) { + $fieldstoremove[] = $currentfield; + } + } + + $this->fieldstocreate = $fieldstocreate; + $this->fieldstoremove = $fieldstoremove; + $this->fieldstoupdate = $preservedfields; + } + /** * Any clean up routines should go here * @@ -343,7 +403,10 @@ abstract class preset_importer { * @return bool True if the current database needs to map the fields imported. */ public function needs_mapping(): bool { - return $this->manager->has_fields(); + if (!$this->manager->has_fields()) { + return false; + } + return (!empty($this->fieldstocreate) || !empty($this->fieldstoremove)); } /** diff --git a/mod/data/renderer.php b/mod/data/renderer.php index 9ea5431982a..d9ee3cee5fa 100644 --- a/mod/data/renderer.php +++ b/mod/data/renderer.php @@ -59,7 +59,7 @@ class mod_data_renderer extends plugin_renderer_base { $strwarning = get_string('mappingwarning', 'data'); $strfieldmappings = get_string('fieldmappings', 'data'); - $params = $importer->get_preset_settings(); + $params = $importer->settings; $newfields = $params->importfields; $currentfields = $params->currentfields; @@ -94,10 +94,18 @@ class mod_data_renderer extends plugin_renderer_base { continue; } if ($currentfield->name == $newfield->name) { - $row[1] .= html_writer::tag('option', get_string('mapexistingfield', 'data', $currentfield->name), array('value'=>$cid, 'selected'=>'selected')); - $selected=true; + $row[1] .= html_writer::tag( + 'option', + get_string('mapexistingfield', 'data', $currentfield->name), + ['value' => $cid, 'selected' => 'selected'] + ); + $selected = true; } else { - $row[1] .= html_writer::tag('option', get_string('mapexistingfield', 'data', $currentfield->name), array('value'=>$cid)); + $row[1] .= html_writer::tag( + 'option', + get_string('mapexistingfield', 'data', $currentfield->name), + ['value' => $cid] + ); } } diff --git a/mod/data/tests/behat/import_presets.feature b/mod/data/tests/behat/import_presets.feature index fdefa98ec47..4c5ae9d198d 100644 --- a/mod/data/tests/behat/import_presets.feature +++ b/mod/data/tests/behat/import_presets.feature @@ -17,16 +17,11 @@ Feature: Users can import presets And the following "activities" exist: | activity | name | intro | course | idnumber | | data | Mountain landscapes | n | C1 | data1 | - And the following "mod_data > presets" exist: - | database | name | description | - | data1 | Saved preset 1 | The preset1 has description | - | data1 | Saved preset 2 | | Scenario: Teacher can import from preset page on an empty database Given I am on the "Mountain landscapes" "data activity" page logged in as teacher1 And I follow "Presets" - And I click on "Actions" "button" - And I choose "Import preset" in the open action menu + And I choose the "Import preset" item in the "Action" action menu And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager When I click on "Import preset and apply" "button" Then I should not see "Field mappings" @@ -38,8 +33,7 @@ Feature: Users can import presets | 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 "Presets" - And I click on "Actions" "button" - And I choose "Import preset" in the open action menu + And I choose the "Import preset" item in the "Action" action menu And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager When I click on "Import preset and apply" "button" Then I should see "Field mappings" @@ -62,8 +56,7 @@ Feature: Users can import presets | data1 | Student entry 1 | And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 And I follow "Presets" - And I click on "Actions" "button" - And I choose "Import preset" in the open action menu + And I choose the "Import preset" item in the "Action" action menu And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager When I click on "Import preset and apply" "button" Then I should see "Field mappings" @@ -86,13 +79,17 @@ Feature: Users can import presets | data1 | Student entry 1 | And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 And I follow "Presets" - And I click on "Actions" "button" - And I choose "Import preset" in the open action menu + And I choose the "Import preset" item in the "Action" action menu And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager When I click on "Import preset and apply" "button" Then I should see "Field mappings" - And I should see "image" - And I should see "Create a new field" in the "image" "table_row" + And I should see "title" + And I should see "Create a new field" in the "title" "table_row" + # We map existing field to keep the entry data + And I set the field "id_title" to "Map to field1" + And I click on "Continue" "button" + And I follow "Database" + And I should see "Student entry" Scenario: Teacher can import from zero state page on an empty database Given I am on the "Mountain landscapes" "data activity" page logged in as teacher1 @@ -101,3 +98,72 @@ Feature: Users can import presets When I click on "Import preset and apply" "button" Then I should not see "Field mappings" And I should see "Image" in the "image" "table_row" + + Scenario: Importing a preset could create new fields + Given the following "mod_data > fields" exist: + | database | type | name | + | data1 | text | title | + And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Fields" + And I should see "title" + And I should not see "Description" + And I should not see "image" + And I follow "Presets" + And I choose the "Import preset" item in the "Action" action menu + And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager + When I click on "Import preset and apply" "button" + And I click on "Continue" "button" + And I should see "The preset has been successfully applied" + And I click on "Continue" "button" + And I follow "Fields" + Then I should see "title" + And I should see "description" in the "description" "table_row" + And I should see "image" in the "image" "table_row" + + Scenario: Importing a preset could create map fields + Given the following "mod_data > fields" exist: + | database | type | name | + | data1 | text | oldtitle | + And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Fields" + And I should see "oldtitle" + And I should not see "Description" + And I should not see "image" + And I follow "Presets" + And I choose the "Import preset" item in the "Action" action menu + And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager + When I click on "Import preset and apply" "button" + # Let's map a field that is not mapped by default + And I should see "Create a new field" in the "oldtitle" "table_row" + And I set the field "id_title" to "Map to oldtitle" + And I click on "Continue" "button" + And I should see "The preset has been successfully applied" + And I click on "Continue" "button" + And I follow "Fields" + Then I should not see "oldtitle" + And I should see "title" + And I should see "description" in the "description" "table_row" + And I should see "image" in the "image" "table_row" + + Scenario: Importing same preset twice doesn't show mapping dialogue + # Importing a preset on an empty database doesn't show the mapping dialogue, so we add a field for the database + # not to be empty. + Given the following "mod_data > fields" exist: + | database | type | name | + | data1 | text | title | + And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Presets" + And I choose the "Import preset" item in the "Action" action menu + And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager + When I click on "Import preset and apply" "button" + And I should see "Field mappings" + And I click on "Continue" "button" + And I should see "The preset has been successfully applied" + And I click on "Continue" "button" + And I follow "Fields" + And I follow "Presets" + And I choose the "Import preset" item in the "Action" action menu + And I upload "mod/data/tests/fixtures/image_gallery_preset.zip" file to "Preset file" filemanager + And I click on "Import preset and apply" "button" + Then I should not see "Field mappings" + And I should see "The preset has been successfully applied" diff --git a/mod/data/tests/behat/use_presets.feature b/mod/data/tests/behat/use_presets.feature new file mode 100644 index 00000000000..2451443c18a --- /dev/null +++ b/mod/data/tests/behat/use_presets.feature @@ -0,0 +1,186 @@ +@mod @mod_data @javascript +Feature: Users can use predefined presets + In order to use presets + As a user + I need to select an existing preset + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | data | Mountain landscapes | introduction... | C1 | data1 | + And the following "mod_data > fields" exist: + | database | type | name | description | + | data1 | text | Test field name | Test field description | + Scenario: Using a preset on a non empty database could create new fields + Given the following "mod_data > fields" exist: + | database | type | name | + | data1 | text | title | + And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Fields" + And I should see "title" + And I should not see "Description" + And I should not see "image" + And I follow "Presets" + And I click on "fullname" "radio" in the "Image gallery" "table_row" + And I click on "Use this preset" "button" + And I click on "Continue" "button" + And I should see "The preset has been successfully applied" + And I click on "Continue" "button" + And I follow "Fields" + Then I should see "title" + And I should see "description" in the "description" "table_row" + And I should see "image" in the "image" "table_row" + + Scenario: Using a preset on a non-empty database could show the option to map fields + Given the following "mod_data > fields" exist: + | database | type | name | + | data1 | text | oldtitle | + And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Fields" + And I should see "oldtitle" + And I should not see "Description" + And I should not see "image" + And I follow "Presets" + And I click on "fullname" "radio" in the "Image gallery" "table_row" + And I click on "Use this preset" "button" + # Let's map a field that is not mapped by default + And I should see "Create a new field" in the "oldtitle" "table_row" + And I set the field "id_title" to "Map to oldtitle" + And I click on "Continue" "button" + And I should see "The preset has been successfully applied" + And I click on "Continue" "button" + And I follow "Fields" + Then I should not see "oldtitle" + And I should see "title" + And I should see "description" in the "description" "table_row" + And I should see "image" in the "image" "table_row" + + Scenario: Teacher can use a preset from presets page on a database with existing entries + # Creating an entry to test use a preset feature with databases with entries. + Given the following "mod_data > entries" exist: + | database | Test field name | + | data1 | Student entry 1 | + And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Presets" + And I click on "fullname" "radio" in the "Image gallery" "table_row" + And the "Use this preset" "button" should be enabled + Then I click on "Use this preset" "button" + Then I should see "Field mappings" + And I should see "title" + And I should see "Create a new field" in the "title" "table_row" + # We map existing field to keep the entry data + And I set the field "id_title" to "Map to Test field name" + And I click on "Continue" "button" + And I should see "The preset has been successfully applied" + And I follow "Fields" + And I should see "title" + And I follow "Database" + And I should see "Student entry 1" + + Scenario: Using same preset twice doesn't show mapping dialogue and applies the preset directly + Given I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Presets" + And I click on "fullname" "radio" in the "Image gallery" "table_row" + When I click on "Use this preset" "button" + And I should see "Field mappings" + And I set the field "id_title" to "Map to Test field name" + And I click on "Continue" "button" + And I should see "The preset has been successfully applied" + And I click on "Continue" "button" + And I follow "Presets" + And I click on "fullname" "radio" in the "Image gallery" "table_row" + And I click on "Use this preset" "button" + Then I should not see "Field mappings" + And I should see "The preset has been successfully applied" + + Scenario: Using a preset from preset preview page on a non empty database could create new fields + Given the following "mod_data > fields" exist: + | database | type | name | + | data1 | text | title | + And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Fields" + And I should see "title" + And I should not see "Description" + And I should not see "image" + And I follow "Presets" + And I click on "Image gallery" "link" + And I click on "Use this preset" "button" + And I click on "Continue" "button" + And I should see "The preset has been successfully applied" + And I click on "Continue" "button" + And I follow "Fields" + Then I should see "title" + And I should see "description" in the "description" "table_row" + And I should see "image" in the "image" "table_row" + + Scenario: Using a preset from preset preview page on a non-empty database could show the option to map fields + Given the following "mod_data > fields" exist: + | database | type | name | + | data1 | text | oldtitle | + And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Fields" + And I should see "oldtitle" + And I should not see "Description" + And I should not see "image" + And I follow "Presets" + And I click on "Image gallery" "link" + And I click on "Use this preset" "button" + # Let's map a field that is not mapped by default + And I should see "Create a new field" in the "oldtitle" "table_row" + And I set the field "id_title" to "Map to oldtitle" + And I click on "Continue" "button" + And I should see "The preset has been successfully applied" + And I click on "Continue" "button" + And I follow "Fields" + Then I should not see "oldtitle" + And I should see "title" + And I should see "description" in the "description" "table_row" + And I should see "image" in the "image" "table_row" + + Scenario: Teacher can use a preset from preset preview page on a database with existing entries + # Creating an entry to test use a preset feature with databases with entries. + Given the following "mod_data > entries" exist: + | database | Test field name | + | data1 | Student entry 1 | + And I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Presets" + And I click on "Image gallery" "link" + And the "Use this preset" "button" should be enabled + Then I click on "Use this preset" "button" + Then I should see "Field mappings" + And I should see "title" + And I should see "Create a new field" in the "title" "table_row" + # We map existing field to keep the entry data + And I set the field "id_title" to "Map to Test field name" + And I click on "Continue" "button" + And I should see "The preset has been successfully applied" + And I follow "Fields" + And I should see "title" + And I follow "Database" + And I should see "Student entry 1" + + Scenario: Using same preset twice from preset preview page doesn't show mapping dialogue and applies the preset + directly + Given I am on the "Mountain landscapes" "data activity" page logged in as teacher1 + And I follow "Presets" + And I click on "Image gallery" "link" + When I click on "Use this preset" "button" + And I should see "Field mappings" + And I set the field "id_title" to "Map to Test field name" + And I click on "Continue" "button" + And I should see "The preset has been successfully applied" + And I click on "Continue" "button" + And I follow "Presets" + And I click on "Image gallery" "link" + And I click on "Use this preset" "button" + Then I should not see "Field mappings" + And I should see "The preset has been successfully applied" diff --git a/mod/data/tests/preset_importer_test.php b/mod/data/tests/preset_importer_test.php index db78968cd3b..1b2cef1adae 100644 --- a/mod/data/tests/preset_importer_test.php +++ b/mod/data/tests/preset_importer_test.php @@ -17,7 +17,6 @@ namespace mod_data; use mod_data\local\importer\preset_existing_importer; -use mod_data\local\importer\preset_upload_importer; /** * Preset importer tests class for mod_data. @@ -30,65 +29,255 @@ use mod_data\local\importer\preset_upload_importer; */ class preset_importer_test extends \advanced_testcase { + /** + * Data provider for build providers for test_needs_mapping and test_set_affected_fields. + * + * @return array[] + */ + public function preset_importer_provider(): array { + // Image gallery preset is: ['title' => 'text', 'description' => 'textarea', 'image' => 'picture']; + + $titlefield = new \stdClass(); + $titlefield->name = 'title'; + $titlefield->type = 'text'; + + $descfield = new \stdClass(); + $descfield->name = 'description'; + $descfield->type = 'textarea'; + + $imagefield = new \stdClass(); + $imagefield->name = 'image'; + $imagefield->type = 'picture'; + + $difffield = new \stdClass(); + $difffield->name = 'title'; + $difffield->type = 'textarea'; + + $newfield = new \stdClass(); + $newfield->name = 'number'; + $newfield->type = 'number'; + + return [ + 'Empty database / Empty importer' => [ + 'currentfields' => [], + 'newfields' => [], + 'pluginname' => '', + ], + 'Empty database / Importer with fields' => [ + 'currentfields' => [], + 'newfields' => [$titlefield, $descfield, $imagefield], + 'pluginname' => 'imagegallery', + ], + 'Database with fields / Empty importer' => [ + 'currentfields' => [$titlefield, $descfield, $imagefield], + 'newfields' => [], + 'pluginname' => '', + ], + 'Same fields' => [ + 'currentfields' => [$titlefield, $descfield, $imagefield], + 'newfields' => [$titlefield, $descfield, $imagefield], + 'pluginname' => 'imagegallery', + ], + 'Fields to create' => [ + 'currentfields' => [$titlefield, $descfield], + 'newfields' => [$titlefield, $descfield, $imagefield], + 'pluginname' => 'imagegallery', + ], + 'Fields to remove' => [ + 'currentfields' => [$titlefield, $descfield, $imagefield, $difffield], + 'newfields' => [$titlefield, $descfield, $imagefield], + 'pluginname' => 'imagegallery', + ], + 'Fields to update' => [ + 'currentfields' => [$difffield, $descfield, $imagefield], + 'newfields' => [$titlefield, $descfield, $imagefield], + 'pluginname' => 'imagegallery', + ], + 'Fields to create, remove and update' => [ + 'currentfields' => [$titlefield, $descfield, $imagefield, $difffield], + 'newfields' => [$titlefield, $descfield, $newfield], + 'pluginname' => '', + ], + ]; + } + + /** + * Data provider for needs_mapping(). + * + * @return array[] + */ + public function needs_mapping_provider(): array { + $basedprovider = $this->preset_importer_provider(); + + $basedprovider['Empty database / Empty importer']['needsmapping'] = false; + $basedprovider['Empty database / Importer with fields']['needsmapping'] = false; + $basedprovider['Database with fields / Empty importer']['needsmapping'] = true; + $basedprovider['Same fields']['needsmapping'] = false; + $basedprovider['Fields to create']['needsmapping'] = true; + $basedprovider['Fields to remove']['needsmapping'] = true; + $basedprovider['Fields to update']['needsmapping'] = true; + $basedprovider['Fields to create, remove and update']['needsmapping'] = true; + + return $basedprovider; + } + /** * Test for needs_mapping method. * + * @dataProvider needs_mapping_provider * @covers ::needs_mapping + * + * @param array $currentfields Fields of the current activity. + * @param array $newfields Fields to be imported. + * @param string $pluginname The plugin preset to be imported. + * @param bool $expectedresult Expected exception. */ - public function test_needs_mapping() { - global $CFG, $USER; + public function test_needs_mapping( + array $currentfields, + array $newfields, + string $pluginname, + bool $expectedresult + ) { + + global $USER; $this->resetAfterTest(); $this->setAdminUser(); + $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data'); // Create a course and a database activity. $course = $this->getDataGenerator()->create_course(); $activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]); + // Add current fields to the activity. + foreach ($currentfields as $field) { + $plugingenerator->create_field($field, $activity); + } $manager = manager::create_from_instance($activity); - // Create presets and importers. - $pluginname = 'imagegallery'; - $plugin = preset::create_from_plugin(null, $pluginname); - $pluginimporter = new preset_existing_importer($manager, '/' . $pluginname); + $presetactivity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]); + // Add current fields to the activity. + foreach ($newfields as $field) { + $plugingenerator->create_field($field, $presetactivity); + } - $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data'); $record = (object) [ 'name' => 'Testing preset name', 'description' => 'Testing preset description', ]; - $saved = $plugingenerator->create_preset($activity, $record); + $saved = $plugingenerator->create_preset($presetactivity, $record); $savedimporter = new preset_existing_importer($manager, $USER->id . '/Testing preset name'); + $this->assertEquals($savedimporter->needs_mapping(), $expectedresult); - $fixturepath = $CFG->dirroot . '/mod/data/tests/fixtures/image_gallery_preset.zip'; + // Create presets and importers. + if ($pluginname) { + $plugin = preset::create_from_plugin(null, $pluginname); + $pluginimporter = new preset_existing_importer($manager, '/' . $pluginname); + $this->assertEquals($pluginimporter->needs_mapping(), $expectedresult); + } + } - // Create a storage file. - $draftid = file_get_unused_draft_itemid(); - $filerecord = [ - 'component' => 'user', - 'filearea' => 'draft', - 'contextid' => \context_user::instance($USER->id)->id, - 'itemid' => $draftid, - 'filename' => 'image_gallery_preset.zip', - 'filepath' => '/' + /** + * Data provider for test_set_affected_fields(). + * + * @return array[] + */ + public function set_affected_provider(): array { + $basedprovider = $this->preset_importer_provider(); + + $basedprovider['Empty database / Empty importer']['fieldstocreate'] = 0; + $basedprovider['Empty database / Empty importer']['fieldstoremove'] = 0; + $basedprovider['Empty database / Empty importer']['fieldstoupdate'] = 0; + + $basedprovider['Empty database / Importer with fields']['fieldstocreate'] = 3; + $basedprovider['Empty database / Importer with fields']['fieldstoremove'] = 0; + $basedprovider['Empty database / Importer with fields']['fieldstoupdate'] = 0; + + $basedprovider['Database with fields / Empty importer']['fieldstocreate'] = 0; + $basedprovider['Database with fields / Empty importer']['fieldstoremove'] = 3; + $basedprovider['Database with fields / Empty importer']['fieldstoupdate'] = 0; + + $basedprovider['Same fields']['fieldstocreate'] = 0; + $basedprovider['Same fields']['fieldstoremove'] = 0; + $basedprovider['Same fields']['fieldstoupdate'] = 3; + + $basedprovider['Fields to create']['fieldstocreate'] = 1; + $basedprovider['Fields to create']['fieldstoremove'] = 0; + $basedprovider['Fields to create']['fieldstoupdate'] = 2; + + $basedprovider['Fields to remove']['fieldstocreate'] = 0; + $basedprovider['Fields to remove']['fieldstoremove'] = 1; + $basedprovider['Fields to remove']['fieldstoupdate'] = 3; + + $basedprovider['Fields to update']['fieldstocreate'] = 1; + $basedprovider['Fields to update']['fieldstoremove'] = 1; + $basedprovider['Fields to update']['fieldstoupdate'] = 2; + + $basedprovider['Fields to create, remove and update']['fieldstocreate'] = 1; + $basedprovider['Fields to create, remove and update']['fieldstoremove'] = 2; + $basedprovider['Fields to create, remove and update']['fieldstoupdate'] = 2; + + return $basedprovider; + } + + /** + * Test for set_affected_fields method. + * + * @dataProvider set_affected_provider + * @covers ::set_affected_fields + * + * @param array $currentfields Fields of the current activity. + * @param array $newfields Fields to be imported. + * @param string $pluginname The plugin preset to be imported. + * @param int $fieldstocreate Expected number of fields on $fieldstocreate. + * @param int $fieldstoremove Expected number of fields on $fieldstoremove. + * @param int $fieldstoupdate Expected number of fields on $fieldstoupdate. + */ + public function test_set_affected_fields( + array $currentfields, + array $newfields, + string $pluginname, + int $fieldstocreate, + int $fieldstoremove, + int $fieldstoupdate + ) { + global $USER; + + $this->resetAfterTest(); + $this->setAdminUser(); + $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data'); + + // Create a course and a database activity. + $course = $this->getDataGenerator()->create_course(); + $activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]); + // Add current fields to the activity. + foreach ($currentfields as $field) { + $plugingenerator->create_field($field, $activity); + } + $manager = manager::create_from_instance($activity); + + $presetactivity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]); + // Add current fields to the activity. + foreach ($newfields as $field) { + $plugingenerator->create_field($field, $presetactivity); + } + + $record = (object) [ + 'name' => 'Testing preset name', + 'description' => 'Testing preset description', ]; - $fs = get_file_storage(); - $file = $fs->create_file_from_pathname($filerecord, $fixturepath); - $uploadedimporter = new preset_upload_importer($manager, $file->get_filepath()); + $saved = $plugingenerator->create_preset($presetactivity, $record); + $savedimporter = new preset_existing_importer($manager, $USER->id . '/Testing preset name'); + $this->assertEquals(count($savedimporter->fieldstoremove), $fieldstoremove); + $this->assertEquals(count($savedimporter->fieldstocreate), $fieldstocreate); + $this->assertEquals(count($savedimporter->fieldstoupdate), $fieldstoupdate); - // Needs mapping returns false for empty databases. - $this->assertFalse($pluginimporter->needs_mapping()); - $this->assertFalse($savedimporter->needs_mapping()); - $this->assertFalse($uploadedimporter->needs_mapping()); - - // Add a field to the database. - $fieldrecord = new \stdClass(); - $fieldrecord->name = 'field1'; - $fieldrecord->type = 'text'; - $plugingenerator->create_field($fieldrecord, $activity); - - // Needs mapping returns true for non-empty databases. - $this->assertTrue($pluginimporter->needs_mapping()); - $this->assertTrue($savedimporter->needs_mapping()); - $this->assertTrue($uploadedimporter->needs_mapping()); + // Create presets and importers. + if ($pluginname) { + $plugin = preset::create_from_plugin(null, $pluginname); + $pluginimporter = new preset_existing_importer($manager, '/' . $pluginname); + $this->assertEquals(count($pluginimporter->fieldstoremove), $fieldstoremove); + $this->assertEquals(count($pluginimporter->fieldstocreate), $fieldstocreate); + $this->assertEquals(count($pluginimporter->fieldstoupdate), $fieldstoupdate); + } } }