From 0ec8266843a0774f52e0d2f7ca2ca6481bdf6209 Mon Sep 17 00:00:00 2001 From: dannylamb Date: Mon, 12 Mar 2018 16:59:19 +0000 Subject: [PATCH 1/3] Re-exporting tiff and jp2 bundle yml --- config/install/media_entity.bundle.image_tiff.yml | 4 ++-- config/install/media_entity.bundle.jp2.yml | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/config/install/media_entity.bundle.image_tiff.yml b/config/install/media_entity.bundle.image_tiff.yml index a038dc1..a1e4cea 100644 --- a/config/install/media_entity.bundle.image_tiff.yml +++ b/config/install/media_entity.bundle.image_tiff.yml @@ -8,7 +8,7 @@ dependencies: module: - media_entity_image _core: - default_config_hash: fi5draAS7BCkytyHpaeYa-daLPsuKPstOA0qVohDVcI + default_config_hash: xYLwsfDi0VZuixIK_k-w8y7hoRSS3S3gkZplXHHcWYY id: image_tiff label: 'Tiff image' description: 'Preservation master file for an Islandora Image' @@ -16,6 +16,6 @@ type: image queue_thumbnail_downloads: false new_revision: false type_configuration: - source_field: field_image + source_field: field_file gather_exif: false field_map: { } diff --git a/config/install/media_entity.bundle.jp2.yml b/config/install/media_entity.bundle.jp2.yml index 0d8be6b..d2b52f9 100644 --- a/config/install/media_entity.bundle.jp2.yml +++ b/config/install/media_entity.bundle.jp2.yml @@ -5,13 +5,17 @@ dependencies: enforced: module: - islandora_image + module: + - media_entity_image _core: - default_config_hash: EGGUWkUHg58NLhG5q1lIo8TsZhrU4PMYW9KoGAqPAzY + default_config_hash: iypCon-6qIVQAQncmw_EpG9FpBUL5WJVuwrQ5Nq0z2U id: jp2 label: JP2 description: 'JPEG2000 service file' -type: generic +type: image queue_thumbnail_downloads: false new_revision: false -type_configuration: { } +type_configuration: + source_field: field_file + gather_exif: false field_map: { } From d9c249f63d24b696afdb0ec0686c8e501c03f4d6 Mon Sep 17 00:00:00 2001 From: dannylamb Date: Tue, 20 Mar 2018 01:52:12 +0000 Subject: [PATCH 2/3] Derivatives --- config/install/context.context.tiff_image.yml | 30 +++ config/install/context.context.web_image.yml | 30 +++ ....generate_a_thumbnail_from_a_web_image.yml | 21 ++ ...ction.generate_a_web_image_from_a_tiff.yml | 21 ++ config/schema/islandora_image.schema.yml | 25 ++ src/Plugin/Action/GenerateImageDerivative.php | 218 ++++++++++++++++++ 6 files changed, 345 insertions(+) create mode 100644 config/install/context.context.tiff_image.yml create mode 100644 config/install/context.context.web_image.yml create mode 100644 config/install/system.action.generate_a_thumbnail_from_a_web_image.yml create mode 100644 config/install/system.action.generate_a_web_image_from_a_tiff.yml create mode 100644 config/schema/islandora_image.schema.yml create mode 100644 src/Plugin/Action/GenerateImageDerivative.php diff --git a/config/install/context.context.tiff_image.yml b/config/install/context.context.tiff_image.yml new file mode 100644 index 0000000..44e0f92 --- /dev/null +++ b/config/install/context.context.tiff_image.yml @@ -0,0 +1,30 @@ +uuid: 8172ac2f-9106-4f3a-a1ce-542c8a16fd4d +langcode: en +status: true +dependencies: + module: + - islandora + enforced: + module: + - islandora_image +name: tiff_image +label: 'Tiff Image' +group: Islandora +description: 'Reactions for Tiff Images' +requireAllConditions: false +disabled: false +conditions: + is_referenced_media: + id: is_referenced_media + field: islandora_image|field_tiff + negate: 0 + uuid: f1eafb7d-4f08-4908-99c6-d0bf9499bd25 + context_mapping: + media: '@islandora.media_route_context_provider:media' +reactions: + derivative: + id: derivative + actions: + generate_a_web_image_from_a_tiff: generate_a_web_image_from_a_tiff + saved: false +weight: 0 diff --git a/config/install/context.context.web_image.yml b/config/install/context.context.web_image.yml new file mode 100644 index 0000000..03560db --- /dev/null +++ b/config/install/context.context.web_image.yml @@ -0,0 +1,30 @@ +uuid: d7befc63-71cd-4974-a9a8-661c84a6b962 +langcode: en +status: true +dependencies: + module: + - islandora + enforced: + module: + - islandora_image +name: web_image +label: 'Web Image' +group: Islandora +description: 'Reactions for Web Images' +requireAllConditions: false +disabled: false +conditions: + is_referenced_media: + id: is_referenced_media + field: islandora_image|field_web_content + negate: 0 + uuid: 2efc9e63-1650-487d-af71-4bab234aa971 + context_mapping: + media: '@islandora.media_route_context_provider:media' +reactions: + derivative: + id: derivative + actions: + generate_a_thumbnail_from_a_web_image: generate_a_thumbnail_from_a_web_image + saved: false +weight: 2 diff --git a/config/install/system.action.generate_a_thumbnail_from_a_web_image.yml b/config/install/system.action.generate_a_thumbnail_from_a_web_image.yml new file mode 100644 index 0000000..6cae04e --- /dev/null +++ b/config/install/system.action.generate_a_thumbnail_from_a_web_image.yml @@ -0,0 +1,21 @@ +uuid: 6b734e72-3de9-4c55-86a2-4ec161b06df4 +langcode: en +status: true +dependencies: + module: + - islandora_image + enforced: + module: + - islandora_image +id: generate_a_thumbnail_from_a_web_image +label: 'Generate a Thumbnail from a Web Image' +type: node +plugin: generate_image_derivative +configuration: + queue: islandora-connector-houdini + event: 'Generate Derivative' + source: field_web_content + destination: field_tn + bundle: tn + mimetype: image/png + args: '-thumbnail 100x100' diff --git a/config/install/system.action.generate_a_web_image_from_a_tiff.yml b/config/install/system.action.generate_a_web_image_from_a_tiff.yml new file mode 100644 index 0000000..9748998 --- /dev/null +++ b/config/install/system.action.generate_a_web_image_from_a_tiff.yml @@ -0,0 +1,21 @@ +uuid: 211bc552-7592-4633-bbef-d682e190f6fa +langcode: en +status: true +dependencies: + module: + - islandora_image + enforced: + module: + - islandora_image +id: generate_a_web_image_from_a_tiff +label: 'Generate a Web Image from a Tiff' +type: node +plugin: generate_image_derivative +configuration: + queue: islandora-connector-houdini + event: 'Generate Derivative' + source: field_tiff + destination: field_web_content + bundle: web_content + mimetype: image/jpeg + args: '' diff --git a/config/schema/islandora_image.schema.yml b/config/schema/islandora_image.schema.yml new file mode 100644 index 0000000..001b82d --- /dev/null +++ b/config/schema/islandora_image.schema.yml @@ -0,0 +1,25 @@ +action.configuration.generate_image_derivative: + type: mapping + label: 'Generate an image derivative...' + mapping: + queue: + type: text + label: 'Queue' + event: + type: text + label: 'Event Type' + source: + type: text + label: 'Source field' + destination: + type: text + label: 'Destination field' + bundle: + type: text + label: 'Media Bundle' + mimetype: + type: text + label: 'Image Mimetype' + args: + type: text + label: 'Convert Arguments' diff --git a/src/Plugin/Action/GenerateImageDerivative.php b/src/Plugin/Action/GenerateImageDerivative.php new file mode 100644 index 0000000..08b3b89 --- /dev/null +++ b/src/Plugin/Action/GenerateImageDerivative.php @@ -0,0 +1,218 @@ +utils = $utils; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('current_user'), + $container->get('entity_type.manager'), + $container->get('islandora.eventgenerator'), + $container->get('islandora.stomp'), + $container->get('jwt.authentication.jwt'), + $container->get('islandora.derivative_utils') + ); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return [ + 'queue' => 'islandora-connector-houdini', + 'event' => 'Generate Derivative', + 'source' => '', + 'destination' => '', + 'bundle' => '', + 'mimetype' => 'image/jpeg', + 'args' => '', + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form = parent::buildConfigurationForm($form, $form_state); + $form['event']['#disabled'] = 'disabled'; + + $media_reference_fields = $this->generateFieldOptions(); + + $form['source'] = [ + '#type' => 'select', + '#title' => t('Source field'), + '#default_value' => $this->configuration['source'], + '#required' => TRUE, + '#options' => $this->generateFieldOptions(), + '#description' => t('Field referencing the Media to use as the source of the derivative.'), + ]; + $form['destination'] = [ + '#type' => 'select', + '#title' => t('Destination field'), + '#default_value' => $this->configuration['destination'], + '#required' => TRUE, + '#options' => $this->generateFieldOptions(), + '#description' => t('Entity reference field for media where derivative will be ingested.'), + ]; + $form['bundle'] = [ + '#type' => 'select', + '#title' => t('Bundle'), + '#default_value' => $this->configuration['bundle'], + '#required' => TRUE, + '#options' => $this->generateBundleOptions(), + '#description' => t('Bundle to create for derivative media'), + ]; + $form['mimetype'] = [ + '#type' => 'textfield', + '#title' => t('Mimetype'), + '#default_value' => $this->configuration['mimetype'], + '#required' => TRUE, + '#rows' => '8', + '#description' => t('Mimetype to convert to (e.g. image/jpeg, image/png, etc...)'), + ]; + $form['args'] = [ + '#type' => 'textfield', + '#title' => t('Additional arguments'), + '#default_value' => $this->configuration['args'], + '#rows' => '8', + '#description' => t('Additional command line arguments for ImageMagick convert (e.g. -resize 50%'), + ]; + return $form; + } + + protected function generateFieldOptions() { + $node_types = $this->entityTypeManager->getStorage('node_type')->loadMultiple(); + $node_types = array_map( + function (NodeType $node_type) { + return $node_type->label(); + }, + $node_types + ); + $node_types = array_flip($node_types); + $fields = array_map( + function (string $node_type_id) { + return $this->utils->getMediaReferenceFields('node', $node_type_id); + }, + $node_types + ); + $fields = array_filter( + $fields, + function(array $fields) { + return !empty($fields); + } + ); + foreach (array_keys($fields) as $key) { + $fields[$key] = array_map( + function (FieldConfig $field) { + return $field->label(); + }, + $fields[$key] + ); + } + return $fields; + } + + protected function generateBundleOptions() { + $bundles = $this->entityTypeManager->getStorage('media_bundle')->loadMultiple(); + return array_map( + function (MediaBundle $bundle) { + return $bundle->label(); + }, + $bundles + ); + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + parent::submitConfigurationForm($form, $form_state); + $this->configuration['source'] = $form_state->getValue('source'); + $this->configuration['destination'] = $form_state->getValue('destination'); + $this->configuration['bundle'] = $form_state->getValue('bundle'); + $this->configuration['mimetype'] = $form_state->getValue('mimetype'); + $this->configuration['args'] = $form_state->getValue('args'); + } +} From 4b0f3814fa00e96096a8e91f20756e4366914df0 Mon Sep 17 00:00:00 2001 From: dannylamb Date: Wed, 28 Mar 2018 16:18:24 +0000 Subject: [PATCH 3/3] tests --- config/install/node.type.islandora_image.yml | 5 - src/Plugin/Action/GenerateImageDerivative.php | 21 ++- .../GenerateImageDerivativeTest.php | 168 ++++++++++++++++++ 3 files changed, 185 insertions(+), 9 deletions(-) create mode 100644 tests/src/Functional/GenerateImageDerivativeTest.php diff --git a/config/install/node.type.islandora_image.yml b/config/install/node.type.islandora_image.yml index a954380..f13ea8e 100644 --- a/config/install/node.type.islandora_image.yml +++ b/config/install/node.type.islandora_image.yml @@ -7,11 +7,6 @@ dependencies: enforced: module: - islandora_image -third_party_settings: - menu_ui: - available_menus: - - main - parent: 'main:' name: 'Islandora Image' type: islandora_image description: 'Models images in Islandora' diff --git a/src/Plugin/Action/GenerateImageDerivative.php b/src/Plugin/Action/GenerateImageDerivative.php index 08b3b89..ddc0fe7 100644 --- a/src/Plugin/Action/GenerateImageDerivative.php +++ b/src/Plugin/Action/GenerateImageDerivative.php @@ -116,10 +116,10 @@ public function defaultConfiguration() { */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { $form = parent::buildConfigurationForm($form, $form_state); - $form['event']['#disabled'] = 'disabled'; + $form['event']['#disabled'] = 'disabled'; $media_reference_fields = $this->generateFieldOptions(); - + $form['source'] = [ '#type' => 'select', '#title' => t('Source field'), @@ -162,6 +162,12 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta return $form; } + /** + * Generates a 2D array for field options. + * + * @return array + * Array formatted for a 2d select form element. + */ protected function generateFieldOptions() { $node_types = $this->entityTypeManager->getStorage('node_type')->loadMultiple(); $node_types = array_map( @@ -175,11 +181,11 @@ function (NodeType $node_type) { function (string $node_type_id) { return $this->utils->getMediaReferenceFields('node', $node_type_id); }, - $node_types + $node_types ); $fields = array_filter( $fields, - function(array $fields) { + function (array $fields) { return !empty($fields); } ); @@ -194,6 +200,12 @@ function (FieldConfig $field) { return $fields; } + /** + * Generates an array for bundle options. + * + * @return array + * Media bundle labels, keyed by bundle ids. + */ protected function generateBundleOptions() { $bundles = $this->entityTypeManager->getStorage('media_bundle')->loadMultiple(); return array_map( @@ -215,4 +227,5 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s $this->configuration['mimetype'] = $form_state->getValue('mimetype'); $this->configuration['args'] = $form_state->getValue('args'); } + } diff --git a/tests/src/Functional/GenerateImageDerivativeTest.php b/tests/src/Functional/GenerateImageDerivativeTest.php new file mode 100644 index 0000000..4ea2050 --- /dev/null +++ b/tests/src/Functional/GenerateImageDerivativeTest.php @@ -0,0 +1,168 @@ +container->get('entity_type.manager')->getStorage('context')->load('tiff_image')->delete(); + $this->container->get('entity_type.manager')->getStorage('context')->load('web_image')->delete(); + + // Create a test user. + $account = $this->drupalCreateUser([ + 'bypass node access', + 'administer contexts', + 'administer actions', + 'view media', + 'create media', + 'update media', + ]); + $this->drupalLogin($account); + + // Create an action to generate a jpeg thumbnail. + $this->drupalGet('admin/config/system/actions'); + $this->getSession()->getPage()->findById("edit-action")->selectOption("Generate an image derivative..."); + $this->getSession()->getPage()->pressButton(t('Create')); + $this->assertSession()->statusCodeEquals(200); + + $this->getSession()->getPage()->fillField('edit-label', "Generate test derivative"); + $this->getSession()->getPage()->fillField('edit-id', "generate_test_derivative"); + $this->getSession()->getPage()->fillField('edit-queue', "generate-test-derivative"); + $this->getSession()->getPage()->findById("edit-source")->selectOption('field_media'); + $this->getSession()->getPage()->findById("edit-destination")->selectOption('field_media'); + $this->getSession()->getPage()->findById("edit-bundle")->selectOption('tn'); + $this->getSession()->getPage()->fillField('edit-mimetype', "image/jpeg"); + $this->getSession()->getPage()->fillField('edit-args', "-thumbnail 20x20"); + $this->getSession()->getPage()->pressButton(t('Save')); + $this->assertSession()->statusCodeEquals(200); + + // Create a context and add the action as a derivative reaction. + $this->createContext('Test', 'test'); + $this->drupalGet("admin/structure/context/test/condition/add/is_referenced_media"); + $this->getSession()->getPage()->findById("edit-conditions-is-referenced-media-field")->selectOption('test_type_with_reference|field_media'); + $this->getSession()->getPage()->pressButton('Save and continue'); + $this->addPresetReaction('test', 'derivative', "generate_test_derivative"); + $this->assertSession()->statusCodeEquals(200); + + // Create a new media. + $urls = $this->createThumbnailWithFile(); + + // Media is not referenced, so derivatives should not fire. + $this->checkNoMessage(); + + // Create a new node without referencing a media and confirm derivatives + // do not fire. + $this->postNodeAddForm('test_type_with_reference', ['title[0][value]' => 'Test Node'], 'Save'); + $this->checkNoMessage(); + + // Create a new node that does reference media and confirm derivatives + // do fire. + $this->postNodeAddForm( + 'test_type_with_reference', + [ + 'title[0][value]' => 'Test Node 2', + 'field_media[0][target_id]' => 'Test Media', + ], + 'Save' + ); + $this->checkMessage(); + + // Stash the node's url. + $url = $this->getUrl(); + + // Edit the node but not the media and confirm derivatives do not fire. + $this->postEntityEditForm($url, ['title[0][value]' => 'Test Node Changed'], 'Save'); + $this->checkNoMessage(); + + // Edit the Media now that it's referenced. + $this->postEntityEditForm($urls['media'], ['field_image[0][alt]' => 'alt text changed'], 'Save'); + $this->checkMessage(); + } + + /** + * Asserts that no message was delivered. + */ + protected function checkNoMessage() { + // Verify no message is sent. + $stomp = $this->container->get('islandora.stomp'); + try { + $stomp->subscribe('generate-test-derivative'); + $this->assertTrue(!$stomp->read()); + $stomp->unsubscribe(); + } + catch (StompException $e) { + $this->assertTrue(FALSE, "There was an error connecting to the stomp broker"); + } + } + + /** + * Asserts a derivative event was delivered. + */ + protected function checkMessage() { + // Verify message is sent. + $stomp = $this->container->get('islandora.stomp'); + try { + $stomp->subscribe('generate-test-derivative'); + while ($msg = $stomp->read()) { + $headers = $msg->getHeaders(); + $this->assertTrue( + isset($headers['Authorization']), + "Authorization header must be set" + ); + $matches = []; + $this->assertTrue( + preg_match('/^Bearer (.*)/', $headers['Authorization'], $matches), + "Authorization header must be a bearer token" + ); + $this->assertTrue( + count($matches) == 2 && !empty($matches[1]), + "Bearer token must not be empty" + ); + + $body = $msg->getBody(); + $body = json_decode($body, TRUE); + + $type = $body['type']; + $this->assertTrue($type == 'Activity', "Expected 'Activity', received $type"); + + $summary = $body['summary']; + $this->assertTrue($summary == 'Generate Derivative', "Expected 'Generate Derivative', received $summary"); + + $content = $body['attachment']['content']; + $this->assertTrue($content['source'] == 'field_media', "Expected source 'field_media', received {$content['source']}"); + $this->assertTrue($content['destination'] == 'field_media', "Expected destination 'field_media', received {$content['destination']}"); + $this->assertTrue($content['bundle'] == 'tn', "Expected bundle 'tn', received {$content['bundle']}"); + $this->assertTrue($content['mimetype'] == 'image/jpeg', "Expected bundle 'image/jpeg', received {$content['mimetype']}"); + $this->assertTrue($content['args'] == '-thumbnail 20x20', "Expected bundle '-thumbnail 20x20', received {$content['args']}"); + } + $stomp->unsubscribe(); + } + catch (StompException $e) { + $this->assertTrue(FALSE, "There was an error connecting to the stomp broker"); + } + } + +}