From 845efd3520de92be9ded9e58f8be66c3febd66af Mon Sep 17 00:00:00 2001 From: dannylamb Date: Thu, 23 May 2019 15:55:06 -0300 Subject: [PATCH] Simpler upload form to link media and node in one shot (#135) --- composer.json | 1 + islandora.info.yml | 1 + .../webform.webform.islandora_upload.yml | 178 +++++++++++++++++ .../IslandoraUploadWebformHandler.php | 183 ++++++++++++++++++ tests/src/Kernel/IslandoraKernelTestBase.php | 5 + .../IslandoraUploadWebformHandlerTest.php | 158 +++++++++++++++ 6 files changed, 526 insertions(+) create mode 100644 modules/islandora_core_feature/config/install/webform.webform.islandora_upload.yml create mode 100644 src/Plugin/WebformHandler/IslandoraUploadWebformHandler.php create mode 100644 tests/src/Kernel/IslandoraUploadWebformHandlerTest.php diff --git a/composer.json b/composer.json index 68cc2c71..478474aa 100644 --- a/composer.json +++ b/composer.json @@ -29,6 +29,7 @@ "drupal/migrate_source_csv" : "^2.1", "drupal/token" : "^1.3", "drupal/flysystem" : "^1.0", + "drupal/webform" : "^5.2", "islandora/crayfish-commons": "^0.0" }, "require-dev": { diff --git a/islandora.info.yml b/islandora.info.yml index 0e0a265e..fc0dd9f1 100644 --- a/islandora.info.yml +++ b/islandora.info.yml @@ -31,3 +31,4 @@ dependencies: - content_translation - flysystem - token + - webform diff --git a/modules/islandora_core_feature/config/install/webform.webform.islandora_upload.yml b/modules/islandora_core_feature/config/install/webform.webform.islandora_upload.yml new file mode 100644 index 00000000..76a7687a --- /dev/null +++ b/modules/islandora_core_feature/config/install/webform.webform.islandora_upload.yml @@ -0,0 +1,178 @@ +langcode: en +status: open +dependencies: + module: + - islandora +open: null +close: null +weight: 0 +uid: 1 +template: false +archive: false +id: islandora_upload +title: 'Islandora Upload' +description: '' +category: '' +elements: "content_type:\n '#type': webform_entity_select\n '#title': 'Content Type'\n '#required': true\n '#target_type': node_type\n '#selection_handler': 'default:node_type'\nmodel:\n '#type': webform_term_select\n '#title': Model\n '#required': true\n '#vocabulary': islandora_models\n '#breadcrumb_delimiter': ''\nmedia_type:\n '#type': webform_entity_select\n '#title': 'Media Type'\n '#required': true\n '#target_type': media_type\n '#selection_handler': 'default:media_type'\nmedia_use:\n '#type': webform_term_select\n '#title': 'Media Use'\n '#required': true\n '#vocabulary': islandora_media_use\n '#breadcrumb_delimiter': ''\nfile:\n '#type': managed_file\n '#title': File\n '#required': true\n '#uri_scheme': fedora\n '#file_extensions': 'gif jpg jpeg png bmp eps tif pict psd txt rtf html odf pdf doc docx ppt pptx xls xlsx xml avi mov mp3 ogg wav bz2 dmg gz jar rar sit svg tar zip'" +css: '' +javascript: '' +settings: + ajax: false + ajax_scroll_top: form + page: true + page_submit_path: '' + page_confirm_path: '' + form_title: both + form_submit_once: false + form_exception_message: '' + form_open_message: '' + form_close_message: '' + form_previous_submissions: true + form_confidential: false + form_confidential_message: '' + form_remote_addr: true + form_convert_anonymous: false + form_prepopulate: false + form_prepopulate_source_entity: false + form_prepopulate_source_entity_required: false + form_prepopulate_source_entity_type: '' + form_reset: false + form_disable_autocomplete: false + form_novalidate: false + form_disable_inline_errors: false + form_required: false + form_unsaved: false + form_disable_back: false + form_submit_back: false + form_autofocus: false + form_details_toggle: false + form_access_denied: default + form_access_denied_title: '' + form_access_denied_message: '' + form_access_denied_attributes: { } + form_file_limit: '' + submission_label: '' + submission_log: false + submission_views: { } + submission_views_replace: { } + submission_user_columns: { } + submission_user_duplicate: false + submission_access_denied: default + submission_access_denied_title: '' + submission_access_denied_message: '' + submission_access_denied_attributes: { } + submission_exception_message: '' + submission_locked_message: '' + submission_excluded_elements: { } + submission_exclude_empty: false + submission_exclude_empty_checkbox: false + previous_submission_message: '' + previous_submissions_message: '' + autofill: false + autofill_message: '' + autofill_excluded_elements: { } + wizard_progress_bar: true + wizard_progress_pages: false + wizard_progress_percentage: false + wizard_progress_link: false + wizard_start_label: '' + wizard_preview_link: false + wizard_confirmation: true + wizard_confirmation_label: '' + wizard_track: '' + preview: 0 + preview_label: '' + preview_title: '' + preview_message: '' + preview_attributes: { } + preview_excluded_elements: { } + preview_exclude_empty: true + preview_exclude_empty_checkbox: false + draft: none + draft_multiple: false + draft_auto_save: false + draft_saved_message: '' + draft_loaded_message: '' + confirmation_type: page + confirmation_title: '' + confirmation_message: '' + confirmation_url: '' + confirmation_attributes: { } + confirmation_back: true + confirmation_back_label: '' + confirmation_back_attributes: { } + confirmation_exclude_query: false + confirmation_exclude_token: false + limit_total: null + limit_total_interval: null + limit_total_message: '' + limit_total_unique: false + limit_user: null + limit_user_interval: null + limit_user_message: '' + limit_user_unique: false + entity_limit_total: null + entity_limit_total_interval: null + entity_limit_user: null + entity_limit_user_interval: null + purge: none + purge_days: null + results_disabled: false + results_disabled_ignore: false + token_update: false +access: + create: + roles: + - anonymous + - authenticated + users: { } + permissions: { } + view_any: + roles: { } + users: { } + permissions: { } + update_any: + roles: { } + users: { } + permissions: { } + delete_any: + roles: { } + users: { } + permissions: { } + purge_any: + roles: { } + users: { } + permissions: { } + view_own: + roles: { } + users: { } + permissions: { } + update_own: + roles: { } + users: { } + permissions: { } + delete_own: + roles: { } + users: { } + permissions: { } + administer: + roles: { } + users: { } + permissions: { } + test: + roles: { } + users: { } + permissions: { } + configuration: + roles: { } + users: { } + permissions: { } +handlers: + create_a_node_media_combo_: + id: islandora_upload + label: 'Create a node/media combo.' + handler_id: create_a_node_media_combo_ + status: true + conditions: { } + weight: 0 + settings: { } diff --git a/src/Plugin/WebformHandler/IslandoraUploadWebformHandler.php b/src/Plugin/WebformHandler/IslandoraUploadWebformHandler.php new file mode 100644 index 00000000..2f5ba26c --- /dev/null +++ b/src/Plugin/WebformHandler/IslandoraUploadWebformHandler.php @@ -0,0 +1,183 @@ +entityTypeManager = $entity_type_manager; + $this->entityFieldManager = $entity_field_manager; + $this->mediaSource = $media_source; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('logger.factory'), + $container->get('config.factory'), + $container->get('entity_type.manager'), + $container->get('webform_submission.conditions_validator'), + $container->get('entity_field.manager'), + $container->get('islandora.media_source_service') + ); + } + + /** + * {@inheritdoc} + */ + public function alterForm(array &$form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission) { + // Strip out content types that don't have the required fields. + foreach ($form['elements']['content_type']['#options'] as $k => $v) { + if (!$this->bundleHasField('node', $k, 'field_model')) { + unset($form['elements']['content_type']['#options'][$k]); + } + } + + // Strip out media types that don't have the required fields. + foreach ($form['elements']['media_type']['#options'] as $k => $v) { + if (!$this->bundleHasField('media', $k, 'field_media_of') || + !$this->bundleHasField('media', $k, 'field_media_use')) { + unset($form['elements']['media_type']['#options'][$k]); + } + } + + } + + /** + * Utility function for filtering out bundles without the required fields. + * + * @param string $entity_type + * Entity type. + * @param string $bundle + * Bundle. + * @param string $field_name + * Field name. + * + * @return bool + * TRUE if the bundle has the specified field. + */ + protected function bundleHasField($entity_type, $bundle, $field_name) { + $all_bundle_fields = $this->entityFieldManager->getFieldDefinitions($entity_type, $bundle); + return isset($all_bundle_fields[$field_name]); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission) { + // Get form field values. + $submission_array = $webform_submission->getData(); + $file = $this->entityTypeManager->getStorage('file')->load($submission_array['file']); + $model = $this->entityTypeManager->getStorage('taxonomy_term')->load($submission_array['model']); + $use = $this->entityTypeManager->getStorage('taxonomy_term')->load($submission_array['media_use']); + + // Make the node. + $this->node = $this->entityTypeManager->getStorage('node')->create([ + 'type' => $submission_array['content_type'], + 'status' => TRUE, + 'title' => $file->getFileName(), + 'field_model' => [$model], + ]); + $this->node->save(); + + // Make the media. + $source_field = $this->mediaSource->getSourceFieldName($submission_array['media_type']); + $media = $this->entityTypeManager->getStorage('media')->create([ + 'bundle' => $submission_array['media_type'], + 'status' => TRUE, + 'name' => $file->getFileName(), + 'field_media_use' => [$use], + 'field_media_of' => [$this->node], + $source_field => [$file], + ]); + $media->save(); + } + + /** + * {@inheritdoc} + */ + public function confirmForm(array &$form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission) { + // Redirect the user to the node's edit form. + $form_state->setRedirect('entity.node.edit_form', ['node' => $this->node->id()]); + } + +} diff --git a/tests/src/Kernel/IslandoraKernelTestBase.php b/tests/src/Kernel/IslandoraKernelTestBase.php index 5690bc1f..71a435c0 100644 --- a/tests/src/Kernel/IslandoraKernelTestBase.php +++ b/tests/src/Kernel/IslandoraKernelTestBase.php @@ -34,8 +34,10 @@ abstract class IslandoraKernelTestBase extends KernelTestBase { 'key', 'jwt', 'file', + 'taxonomy', 'image', 'media', + 'webform', 'islandora', 'flysystem', ]; @@ -49,10 +51,13 @@ abstract class IslandoraKernelTestBase extends KernelTestBase { // Bootstrap minimal Drupal environment to run the tests. $this->installSchema('system', 'sequences'); $this->installSchema('node', 'node_access'); + $this->installSchema('file', 'file_usage'); $this->installEntitySchema('user'); $this->installEntitySchema('node'); $this->installEntitySchema('context'); $this->installEntitySchema('file'); + $this->installEntitySchema('media'); + $this->installEntitySchema('taxonomy_term'); $this->installConfig('filter'); } diff --git a/tests/src/Kernel/IslandoraUploadWebformHandlerTest.php b/tests/src/Kernel/IslandoraUploadWebformHandlerTest.php new file mode 100644 index 00000000..bec1ee70 --- /dev/null +++ b/tests/src/Kernel/IslandoraUploadWebformHandlerTest.php @@ -0,0 +1,158 @@ +testType = $this->container->get('entity_type.manager')->getStorage('node_type')->create([ + 'type' => 'test_type', + 'name' => 'Test Type', + ]); + $this->testType->save(); + $this->createEntityReferenceField('node', 'test_type', 'field_member_of', 'Member Of', 'node', 'default', [], 2); + $this->createEntityReferenceField('node', 'test_type', 'field_model', 'Model', 'taxonomy_term', 'default', [], 2); + + // Create a media type. + $this->testMediaType = $this->createMediaType('file', ['id' => 'test_media_type']); + $this->testMediaType->save(); + $this->createEntityReferenceField('media', $this->testMediaType->id(), 'field_media_of', 'Media Of', 'node', 'default', [], 2); + $this->createEntityReferenceField('media', $this->testMediaType->id(), 'field_media_use', 'Media Use', 'taxonomy_term', 'default', [], 2); + + // Create a vocabulary. + $this->testVocabulary = $this->container->get('entity_type.manager')->getStorage('taxonomy_vocabulary')->create([ + 'name' => 'Test Vocabulary', + 'vid' => 'test_vocabulary', + ]); + $this->testVocabulary->save(); + + // Create two terms. + $this->modelTerm = $this->container->get('entity_type.manager')->getStorage('taxonomy_term')->create([ + 'name' => 'Binary', + 'vid' => $this->testVocabulary->id(), + ]); + $this->modelTerm->save(); + + $this->useTerm = $this->container->get('entity_type.manager')->getStorage('taxonomy_term')->create([ + 'name' => 'Original File', + 'vid' => $this->testVocabulary->id(), + ]); + $this->useTerm->save(); + + // Pretend this is the user uploaded file. + $this->file = $this->container->get('entity_type.manager')->getStorage('file')->create([ + 'uri' => "public://test_file.txt", + 'filename' => "test_file.txt", + 'filemime' => "text/plain", + 'status' => FILE_STATUS_PERMANENT, + ]); + $this->file->save(); + } + + /** + * @covers \Drupal\islandora\Plugin\WebformHandler\IslandoraUploadWebformHandler::submitForm + * @covers \Drupal\islandora\Plugin\WebformHandler\IslandoraUploadWebformHandler::confirmForm + */ + public function testSubmitForm() { + $handler_manager = $this->container->get('plugin.manager.webform.handler'); + $handler = $handler_manager->createInstance('islandora_upload'); + + $form = []; + + $prophecy = $this->prophesize(FormStateInterface::class); + $prophecy->setRedirect('entity.node.edit_form', ['node' => 1])->shouldBeCalled(); + $form_state = $prophecy->reveal(); + + $prophecy = $this->prophesize(WebformSubmissionInterface::class); + $prophecy->getData()->willReturn([ + 'content_type' => 'test_type', + 'media_type' => 'test_media_type', + 'model' => "{$this->modelTerm->id()}", + 'media_use' => "{$this->useTerm->id()}", + 'file' => $this->file->id(), + ]); + $webform_submission = $prophecy->reveal(); + + $handler->submitForm($form, $form_state, $webform_submission); + + $node = $this->container->get('entity_type.manager')->getStorage('node')->load(1); + $media = $this->container->get('entity_type.manager')->getStorage('media')->load(1); + + // Assert content type. + $actual = $node->bundle(); + $expected = $this->testType->id(); + $this->assertTrue($actual == $expected, "Incorrect Content Type: Expected $expected, received $actual"); + + // Assert model. + $actual = $node->get('field_model')->referencedEntities()[0]->id(); + $expected = $this->modelTerm->id(); + $this->assertTrue($actual == $expected, "Incorrect Model: Expected $expected, received $actual"); + + // Assert title. + $actual = $node->getTitle(); + $expected = $this->file->getFileName(); + $this->assertTrue($actual == $expected, "Incorrect Title: Expected $expected, received $actual"); + + // Assert media type. + $actual = $media->bundle(); + $expected = $this->testMediaType->id(); + $this->assertTrue($actual == $expected, "Incorrect Media Type: Expected $expected, received $actual"); + + // Assert media use. + $actual = $media->get('field_media_use')->referencedEntities()[0]->id(); + $expected = $this->useTerm->id(); + $this->assertTrue($actual == $expected, "Incorrect Media Use: Expected $expected, received $actual"); + + // Assert media name. + $actual = $media->getName(); + $expected = $this->file->getFileName(); + $this->assertTrue($actual == $expected, "Incorrect Name: Expected $expected, received $actual"); + + // Assert media is using file. + $actual = $media->get('field_media_file')->referencedEntities()[0]->id(); + $expected = $this->file->id(); + $this->assertTrue($actual == $expected, "Incorrect File: Expected $expected, received $actual"); + + // Assert media is linked to node. + $actual = $media->get('field_media_of')->referencedEntities()[0]->id(); + $expected = $node->id(); + $this->assertTrue($actual == $expected, "Linked to incorrect node: Expected $expected, received $actual"); + + // Assert that setRedirect is called in the confirm form function. + $handler->confirmForm($form, $form_state, $webform_submission); + } + +}