diff --git a/config/schema/islandora.schema.yml b/config/schema/islandora.schema.yml index 4f9cbda8..b0bec045 100644 --- a/config/schema/islandora.schema.yml +++ b/config/schema/islandora.schema.yml @@ -53,6 +53,13 @@ condition.plugin.is_media: type: condition.plugin mapping: +condition.plugin.is_referenced_media: + type: condition.plugin + mapping: + field: + type: text + label: 'Reference Field' + condition.plugin.is_file: type: condition.plugin mapping: diff --git a/src/Plugin/Condition/IsReferencedMedia.php b/src/Plugin/Condition/IsReferencedMedia.php new file mode 100644 index 00000000..292cbe29 --- /dev/null +++ b/src/Plugin/Condition/IsReferencedMedia.php @@ -0,0 +1,189 @@ +contentTypeStorage = $content_type_storage; + $this->entityFieldManager = $entity_field_manager; + $this->entityQuery = $entity_query; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $container->get('entity_type.manager')->getStorage('node_type'), + $container->get('entity_field.manager'), + $container->get('entity.query'), + $configuration, + $plugin_id, + $plugin_definition + ); + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + // Get all entity reference fields that target Media. + $media_reference_fields = $this->entityQuery->get('field_storage_config') + ->condition('entity_type', 'node') + ->condition('type', 'entity_reference') + ->condition('settings.target_type', 'media') + ->execute(); + + // Trim off the preceding 'node.'. + $media_reference_fields = array_map( + function ($field) { + return ltrim($field, 'node.'); + }, + $media_reference_fields + ); + + // Flip the results so it can be used in an array_intersect_key later on. + $media_reference_fields = array_flip($media_reference_fields); + + // Get all content types. + $content_types = $this->contentTypeStorage->loadMultiple(); + + // Build up the 2D options array. + $options = []; + foreach ($content_types as $content_type) { + // Filter fields to those we know are media references. + $all_fields = $this->entityFieldManager->getFieldDefinitions('node', $content_type->id()); + $reference_fields = array_intersect_key($all_fields, $media_reference_fields); + + // First dimension is keyed by the content type label. + // Second dimension is keyed by the content_type machine name concatenated + // with the field name. The content type machine name is needed for + // disambiguation, otherwise fields attached to multiple content types + // have unexpected behaviour when submitting the form. + foreach ($reference_fields as $field_name => $field_definition) { + $content_type_label = $content_type->label(); + $field_key = $content_type->id() . '|' . $field_name; + $field_label = $field_definition->getLabel(); + $options[$content_type_label][$field_key] = $field_label; + } + } + + // Create the 2D select. + $form['field'] = [ + '#title' => $this->t('Referenced As'), + '#description' => $this->t('The field that references the Media.'), + '#type' => 'select', + '#options' => $options, + '#default_value' => isset($this->configuration['field']) ? $this->configuration['field'] : '', + '#size' => 10, + ]; + + return parent::buildConfigurationForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + $this->configuration['field'] = $form_state->getValue('field'); + parent::submitConfigurationForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return ['field' => ''] + parent::defaultConfiguration(); + } + + /** + * {@inheritdoc} + */ + public function summary() { + return $this->t('The Media is referenced by a Node using the specified field.'); + } + + /** + * {@inheritdoc} + */ + public function evaluate() { + // Check to see that the media is referenced by a node of the specified + // type using the specified field. + $media = $this->getContextValue('media'); + $field_key = $this->configuration['field']; + list($content_type, $field) = explode('|', $field_key); + return $this->entityQuery->get('node') + ->condition('type', $content_type) + ->condition("$field.target_id", $media->id()) + ->execute(); + } + +} diff --git a/tests/src/Functional/IsReferencedMediaTest.php b/tests/src/Functional/IsReferencedMediaTest.php new file mode 100644 index 00000000..84fbfc30 --- /dev/null +++ b/tests/src/Functional/IsReferencedMediaTest.php @@ -0,0 +1,144 @@ +container->get('entity_type.manager')->getStorage('node_type')->create([ + 'type' => 'test_type_with_reference', + 'label' => 'Test Type With Reference', + ]); + $test_type_with_reference->save(); + $this->createEntityReferenceField('node', 'test_type_with_reference', 'field_media', 'Media Entity', 'media', 'default', [], 2); + + $another_test_type_with_reference = $this->container->get('entity_type.manager')->getStorage('node_type')->create([ + 'type' => 'another_test_type_with_reference', + 'label' => 'Another Test Type With Reference', + ]); + $another_test_type_with_reference->save(); + $this->createEntityReferenceField('node', 'another_test_type_with_reference', 'field_media', 'Media Entity', 'media', 'default', [], 2); + + // Create the media. + $media_bundle = $this->drupalCreateMediaBundle(); + $this->referenced = $this->container->get('entity_type.manager')->getStorage('media')->create([ + 'bundle' => $media_bundle->id(), + 'name' => 'Referenced Media', + ]); + $this->referenced->save(); + + $this->notReferenced = $this->container->get('entity_type.manager')->getStorage('media')->create([ + 'bundle' => $media_bundle->id(), + 'name' => 'Unreferenced Media', + ]); + $this->notReferenced->save(); + + $this->referencedByAnother = $this->container->get('entity_type.manager')->getStorage('media')->create([ + 'bundle' => $media_bundle->id(), + 'name' => 'Referenced By Another', + ]); + $this->referencedByAnother->save(); + + // Reference one by a node of the type we're expecting. + $node = $this->container->get('entity_type.manager')->getStorage('node')->create([ + 'type' => 'test_type_with_reference', + 'title' => 'Referencer', + 'field_media' => [$this->referenced->id()], + ]); + $node->save(); + + // Reference one by a node of the type we're not expecting. + $another_node = $this->container->get('entity_type.manager')->getStorage('node')->create([ + 'type' => 'another_test_type_with_reference', + 'title' => 'Another Referencer', + 'field_media' => [$this->referencedByAnother->id()], + ]); + $node->save(); + } + + /** + * @covers \Drupal\islandora\ContextProvider\MediaContextProvider::__construct + * @covers \Drupal\islandora\ContextProvider\MediaContextProvider::getRuntimeContexts + * @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts + * @covers \Drupal\islandora\IslandoraContextManager::applyContexts + * @covers \Drupal\islandora\Plugin\Condition\IsReferencedMedia::buildConfigurationForm + * @covers \Drupal\islandora\Plugin\Condition\IsReferencedMedia::submitConfigurationForm + * @covers \Drupal\islandora\Plugin\Condition\IsReferencedMedia::evaluate + * @covers \Drupal\islandora\PresetReaction\PresetReaction::buildConfigurationForm + * @covers \Drupal\islandora\PresetReaction\PresetReaction::submitConfigurationForm + * @covers \Drupal\islandora\PresetReaction\PresetReaction::execute + * @covers \Drupal\islandora\IslandoraServiceProvider::alter + */ + public function testIsReferencedMedia() { + // Create a test user. + $account = $this->drupalCreateUser([ + 'administer contexts', + 'view media', + 'update any media', + ]); + $this->drupalLogin($account); + + $this->createContext('Test', 'test'); + + // Add the condition. + $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'); + + // Add the reaction to say "Hello World!". + $this->addPresetReaction('test', 'index', 'hello_world'); + + // Edit the referenced node. "Hello World!" should be output to the screen. + $this->postEntityEditForm("media/{$this->referenced->id()}", ['name[0][value]' => 'Referenced Media Changed'], 'Save and keep published'); + $this->assertSession()->pageTextContains("Hello World!"); + + // Edit the unreferenced node. "Hello World!" should not be output to the + // screen. + $this->postEntityEditForm("media/{$this->notReferenced->id()}", ['name[0][value]' => 'Unreferenced Media Changed'], 'Save and keep published'); + $this->assertSession()->pageTextNotContains("Hello World!"); + + // Edit the node referenced by a different type. "Hello World!" should not + // be output to the screen. + $this->postEntityEditForm("media/{$this->referencedByAnother->id()}", ['name[0][value]' => 'Referenced By Another Changed'], 'Save and keep published'); + $this->assertSession()->pageTextNotContains("Hello World!"); + } + +}