From b8abc327b01c9232cefa08ad75e6670a34ee5beb Mon Sep 17 00:00:00 2001 From: dannylamb Date: Fri, 15 Nov 2019 18:46:01 +0000 Subject: [PATCH 1/5] Adding OR logic to NodeHasTerm and ilk --- config/schema/islandora.schema.yml | 9 +++++ src/Plugin/Condition/NodeHasTerm.php | 42 ++++++++++++++++++++---- tests/src/Functional/NodeHasTermTest.php | 26 +++++++++++++-- 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/config/schema/islandora.schema.yml b/config/schema/islandora.schema.yml index 8fd719ce..f63e4341 100644 --- a/config/schema/islandora.schema.yml +++ b/config/schema/islandora.schema.yml @@ -78,6 +78,9 @@ condition.plugin.node_has_term: uri: type: text label: 'Taxonomy Term URI' + logic: + type: string + label: 'Logic (AND or OR)' condition.plugin.node_has_parent: type: condition.plugin @@ -95,6 +98,9 @@ condition.plugin.media_has_term: uri: type: text label: 'Taxonomy Term URI' + logic: + type: string + label: 'Logic (AND or OR)' condition.plugin.parent_node_has_term: type: condition.plugin @@ -102,6 +108,9 @@ condition.plugin.parent_node_has_term: uri: type: text label: 'Taxonomy Term URI' + logic: + type: string + label: 'Logic (AND or OR)' condition.plugin.file_uses_filesystem: type: condition.plugin diff --git a/src/Plugin/Condition/NodeHasTerm.php b/src/Plugin/Condition/NodeHasTerm.php index dae52ad0..c912ea9b 100644 --- a/src/Plugin/Condition/NodeHasTerm.php +++ b/src/Plugin/Condition/NodeHasTerm.php @@ -79,6 +79,16 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI ); } + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return array_merge( + ['logic' => 'and'], + parent::defaultConfiguration() + ); + } + /** * {@inheritdoc} */ @@ -100,6 +110,16 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI '#required' => TRUE, ]; + $form['logic'] = [ + '#type' => 'radios', + '#title' => $this->t('Logic'), + '#description' => $this->t('Whether to use AND or OR logic to evaluate multiple terms'), + '#options' => [ + 'and' => 'And', + 'or' => 'Or', + ], + '#default_value' => $this->configuration['logic'], + ]; return parent::buildConfigurationForm($form, $form_state); } @@ -124,6 +144,9 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI $this->configuration['uri'] = implode(',', $uris); } } + + $this->configuration['logic'] = $form_state->getValue('logic'); + parent::submitConfigurationForm($form, $form_state); } @@ -168,7 +191,7 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI // FALSE if there's no URIs on the node. if (empty($haystack)) { - return $this->isNegated() ? TRUE : FALSE; + return FALSE; } // Get the URIs to look for. It's a required field, so there @@ -176,12 +199,19 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI $needles = explode(',', $this->configuration['uri']); // TRUE if every needle is in the haystack. - if (count(array_intersect($needles, $haystack)) == count($needles)) { - return $this->isNegated() ? FALSE : TRUE; + if ($this->configuration['logic'] == 'and') { + if (count(array_intersect($needles, $haystack)) == count($needles)) { + return TRUE; + } + return FALSE; + } + // TRUE if any needle is in the haystack. + else { + if (count(array_intersect($needles, $haystack)) > 0) { + return TRUE; + } + return FALSE; } - - // Otherwise, FALSE. - return $this->isNegated() ? TRUE : FALSE; } /** diff --git a/tests/src/Functional/NodeHasTermTest.php b/tests/src/Functional/NodeHasTermTest.php index a27cf474..7d336de0 100644 --- a/tests/src/Functional/NodeHasTermTest.php +++ b/tests/src/Functional/NodeHasTermTest.php @@ -77,14 +77,36 @@ class NodeHasTermTest extends IslandoraFunctionalTestBase { $condition->setContextValue('node', $node); $this->assertFalse($condition->execute(), "Condition should fail if node does not have both terms"); - // Create a node with both tags. + // Check for two tags this time. + // Node still only has one. + $condition = $condition_manager->createInstance( + 'node_has_term', + [ + 'uri' => 'http://purl.org/coar/resource_type/c_c513,http://pcdm.org/use#PreservationMasterFile', + 'logic' => 'or', + ] + ); + $condition->setContextValue('node', $node); + $this->assertTrue($condition->execute(), "Condition should pass if has one of two terms using OR logic."); + + // Create a node with both tags and try it with OR. $node = $this->container->get('entity_type.manager')->getStorage('node')->create([ 'type' => 'test_type', 'title' => 'Test Node', 'field_tags' => [$this->imageTerm->id(), $this->preservationMasterTerm->id()], ]); $condition->setContextValue('node', $node); - $this->assertTrue($condition->execute(), "Condition should pass if node has both terms"); + $this->assertTrue($condition->execute(), "Condition should pass if node has both terms using OR logic"); + + // Try it with AND. + $condition = $condition_manager->createInstance( + 'node_has_term', + [ + 'uri' => 'http://purl.org/coar/resource_type/c_c513,http://pcdm.org/use#PreservationMasterFile', + ] + ); + $condition->setContextValue('node', $node); + $this->assertTrue($condition->execute(), "Condition should pass if node has both terms using AND logic"); } } From 12dfcf7be3eceab691612a381128aec83cb9f0e6 Mon Sep 17 00:00:00 2001 From: dannylamb Date: Mon, 30 Dec 2019 17:27:50 +0000 Subject: [PATCH 2/5] Using Authority Link fields in addition to field_external_uri --- src/IslandoraUtils.php | 51 ++++++++++++++++++++++++---- src/Plugin/Condition/NodeHasTerm.php | 36 ++++++++++++++++---- 2 files changed, 74 insertions(+), 13 deletions(-) diff --git a/src/IslandoraUtils.php b/src/IslandoraUtils.php index e98766c7..e78dd972 100644 --- a/src/IslandoraUtils.php +++ b/src/IslandoraUtils.php @@ -235,12 +235,32 @@ class IslandoraUtils { * Calling getStorage() throws if the storage handler couldn't be loaded. */ public function getTermForUri($uri) { - $results = $this->entityQuery->get('taxonomy_term') - ->condition(self::EXTERNAL_URI_FIELD . '.uri', $uri) + // Get authority link fields to search. + $field_map = $this->entityFieldManager->getFieldMap(); + $fields = []; + foreach ($field_map['taxonomy_term'] as $field_name => $field_data) { + if ($field_data['type'] == 'authority_link') { + $fields[] = $field_name; + } + } + // Add field_external_uri. + $fields[] = self::EXTERNAL_URI_FIELD; + + $query = $this->entityQuery->get('taxonomy_term'); + + $orGroup = $query->orConditionGroup(); + foreach ($fields as $field) { + $orGroup->condition("$field.uri", $uri); + } + + $results = $query + ->condition($orGroup) ->execute(); + if (empty($results)) { return NULL; } + return $this->entityTypeManager->getStorage('taxonomy_term')->load(reset($results)); } @@ -258,16 +278,33 @@ class IslandoraUtils { * be created. */ public function getUriForTerm(TermInterface $term) { - if ($term && $term->hasField(self::EXTERNAL_URI_FIELD)) { - $field = $term->get(self::EXTERNAL_URI_FIELD); - if (!$field->isEmpty()) { - $link = $field->first()->getValue(); - return $link['uri']; + $fields = $this->getUriFieldNamesForTerms(); + foreach ($fields as $field_name) { + if ($term && $term->hasField($field_name)) { + $field = $term->get($field_name); + if (!$field->isEmpty()) { + $link = $field->first()->getValue(); + return $link['uri']; + } } } return NULL; } + public function getUriFieldNamesForTerms() { + // Get authority link fields to search. + $field_map = $this->entityFieldManager->getFieldMap(); + $fields = []; + foreach ($field_map['taxonomy_term'] as $field_name => $field_data) { + if ($field_data['type'] == 'authority_link') { + $fields[] = $field_name; + } + } + // Add field_external_uri. + $fields[] = self::EXTERNAL_URI_FIELD; + return $fields; + } + /** * Executes context reactions for a Node. * diff --git a/src/Plugin/Condition/NodeHasTerm.php b/src/Plugin/Condition/NodeHasTerm.php index c912ea9b..41d20676 100644 --- a/src/Plugin/Condition/NodeHasTerm.php +++ b/src/Plugin/Condition/NodeHasTerm.php @@ -123,6 +123,22 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI return parent::buildConfigurationForm($form, $form_state); } + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + parent::validateConfigurationForm($form, $form_state); + $value = $form_state->getValue('term'); + foreach ($value as $target) { + $tid = $target['target_id']; + $term = $this->entityTypeManager->getStorage('taxonomy_term')->load($tid); + $uri = $this->utils->getUriForTerm($term); + if (empty($uri)) { + $form_state->setErrorByName( + 'term', + $this->t('@name does not have an external URI. Give it an Authority Link or the External Uri field.', ['@name' => $term->label()]) + ); + } + } + } + /** * {@inheritdoc} */ @@ -176,16 +192,24 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI */ protected function evaluateEntity(EntityInterface $entity) { // Find the terms on the node. - $terms = array_filter($entity->referencedEntities(), function ($entity) { - return $entity->getEntityTypeId() == 'taxonomy_term' && - $entity->hasField(IslandoraUtils::EXTERNAL_URI_FIELD) && - !$entity->get(IslandoraUtils::EXTERNAL_URI_FIELD)->isEmpty(); + $field_names = $this->utils->getUriFieldNamesForTerms(); + $terms = array_filter($entity->referencedEntities(), function ($entity) use ($field_names) { + if ($entity->getEntityTypeId() != 'taxonomy_term') { + return FALSE; + } + + foreach ($field_names as $field_name) { + if ($entity->hasField($field_name) && !$entity->get($field_name)->isEmpty()) { + return TRUE; + } + } + return FALSE; }); // Get their URIs. $haystack = array_map(function ($term) { - return $term->get(IslandoraUtils::EXTERNAL_URI_FIELD)->first()->getValue()['uri']; - }, + return $this->utils->getUriForTerm($term); + }, $terms ); From f8dd5843907660a0a1d77b2d31ce513f4b59fa48 Mon Sep 17 00:00:00 2001 From: dannylamb Date: Mon, 30 Dec 2019 19:43:07 +0000 Subject: [PATCH 3/5] Coding standards --- src/IslandoraUtils.php | 6 ++++++ src/Plugin/Condition/NodeHasTerm.php | 9 ++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/IslandoraUtils.php b/src/IslandoraUtils.php index e78dd972..931e20d3 100644 --- a/src/IslandoraUtils.php +++ b/src/IslandoraUtils.php @@ -291,6 +291,12 @@ class IslandoraUtils { return NULL; } + /** + * Gets every field name that might contain an external uri for a term. + * + * @return string[] + * Field names for fields that a term may have as an external uri. + */ public function getUriFieldNamesForTerms() { // Get authority link fields to search. $field_map = $this->entityFieldManager->getFieldMap(); diff --git a/src/Plugin/Condition/NodeHasTerm.php b/src/Plugin/Condition/NodeHasTerm.php index 41d20676..22f87761 100644 --- a/src/Plugin/Condition/NodeHasTerm.php +++ b/src/Plugin/Condition/NodeHasTerm.php @@ -123,6 +123,9 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI return parent::buildConfigurationForm($form, $form_state); } + /** + * {@inheritdoc} + */ public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { parent::validateConfigurationForm($form, $form_state); $value = $form_state->getValue('term'); @@ -199,9 +202,9 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI } foreach ($field_names as $field_name) { - if ($entity->hasField($field_name) && !$entity->get($field_name)->isEmpty()) { + if ($entity->hasField($field_name) && !$entity->get($field_name)->isEmpty()) { return TRUE; - } + } } return FALSE; }); @@ -209,7 +212,7 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI // Get their URIs. $haystack = array_map(function ($term) { return $this->utils->getUriForTerm($term); - }, + }, $terms ); From e417b0a84c3e0a2e04839d14010d5cbe0b27b032 Mon Sep 17 00:00:00 2001 From: dannylamb Date: Thu, 2 Jan 2020 19:57:16 +0000 Subject: [PATCH 4/5] Filtering out terms that don't have external uris from the autocomplete --- src/Plugin/Condition/NodeHasTerm.php | 21 +-- .../ExternalUriSelection.php | 125 ++++++++++++++++++ 2 files changed, 127 insertions(+), 19 deletions(-) create mode 100644 src/Plugin/EntityReferenceSelection/ExternalUriSelection.php diff --git a/src/Plugin/Condition/NodeHasTerm.php b/src/Plugin/Condition/NodeHasTerm.php index 22f87761..1e85dea4 100644 --- a/src/Plugin/Condition/NodeHasTerm.php +++ b/src/Plugin/Condition/NodeHasTerm.php @@ -104,10 +104,12 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI $form['term'] = [ '#type' => 'entity_autocomplete', '#title' => $this->t('Term'), + '#description' => $this->t('Terms need an Authority Link or External Uri to be compatible with this Condition. If the term you are looking for is not appearing in the autocomplete, please ensure it has a value for its Authority Link or External Uri field.'), '#tags' => TRUE, '#default_value' => $default, '#target_type' => 'taxonomy_term', '#required' => TRUE, + '#selection_handler' => 'islandora:external_uri', ]; $form['logic'] = [ @@ -123,25 +125,6 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI return parent::buildConfigurationForm($form, $form_state); } - /** - * {@inheritdoc} - */ - public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { - parent::validateConfigurationForm($form, $form_state); - $value = $form_state->getValue('term'); - foreach ($value as $target) { - $tid = $target['target_id']; - $term = $this->entityTypeManager->getStorage('taxonomy_term')->load($tid); - $uri = $this->utils->getUriForTerm($term); - if (empty($uri)) { - $form_state->setErrorByName( - 'term', - $this->t('@name does not have an external URI. Give it an Authority Link or the External Uri field.', ['@name' => $term->label()]) - ); - } - } - } - /** * {@inheritdoc} */ diff --git a/src/Plugin/EntityReferenceSelection/ExternalUriSelection.php b/src/Plugin/EntityReferenceSelection/ExternalUriSelection.php new file mode 100644 index 00000000..6715fb23 --- /dev/null +++ b/src/Plugin/EntityReferenceSelection/ExternalUriSelection.php @@ -0,0 +1,125 @@ +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('entity_type.manager'), + $container->get('module_handler'), + $container->get('current_user'), + $container->get('entity_field.manager'), + $container->get('entity_type.bundle.info'), + $container->get('entity.repository'), + $container->get('islandora.utils') + ); + } + + /** + * {@inheritdoc} + */ + public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) { + $options = parent::getReferenceableEntities($match, $match_operator, $limit); + + foreach (array_keys($options) as $vid) { + foreach (array_keys($options[$vid]) as $tid) { + $term = $this->entityTypeManager->getStorage('taxonomy_term')->load($tid); + $uri = $this->utils->getUriForTerm($term); + if (empty($uri)) { + unset($options[$vid][$tid]); + } + } + if (empty($options[$vid])) { + unset($options[$vid]); + } + } + + return $options; + } + +} From 93a84c620e32ec505ce4d04bb27ce7b583723ce2 Mon Sep 17 00:00:00 2001 From: dannylamb Date: Fri, 3 Jan 2020 17:57:41 +0000 Subject: [PATCH 5/5] Updating language --- src/Plugin/Condition/MediaHasTerm.php | 2 +- src/Plugin/Condition/NodeHasTerm.php | 4 ++-- src/Plugin/Condition/ParentNodeHasTerm.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Plugin/Condition/MediaHasTerm.php b/src/Plugin/Condition/MediaHasTerm.php index 67d4db3c..b170c7a6 100644 --- a/src/Plugin/Condition/MediaHasTerm.php +++ b/src/Plugin/Condition/MediaHasTerm.php @@ -7,7 +7,7 @@ namespace Drupal\islandora\Plugin\Condition; * * @Condition( * id = "media_has_term", - * label = @Translation("Media has term"), + * label = @Translation("Media has term with URI"), * context = { * "media" = @ContextDefinition("entity:media", required = TRUE , label = @Translation("media")) * } diff --git a/src/Plugin/Condition/NodeHasTerm.php b/src/Plugin/Condition/NodeHasTerm.php index 1e85dea4..bfa8e9e8 100644 --- a/src/Plugin/Condition/NodeHasTerm.php +++ b/src/Plugin/Condition/NodeHasTerm.php @@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * * @Condition( * id = "node_has_term", - * label = @Translation("Node has term"), + * label = @Translation("Node has term with URI"), * context = { * "node" = @ContextDefinition("entity:node", required = TRUE , label = @Translation("node")) * } @@ -104,7 +104,7 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI $form['term'] = [ '#type' => 'entity_autocomplete', '#title' => $this->t('Term'), - '#description' => $this->t('Terms need an Authority Link or External Uri to be compatible with this Condition. If the term you are looking for is not appearing in the autocomplete, please ensure it has a value for its Authority Link or External Uri field.'), + '#description' => $this->t('Only terms that have external URIs/URLs will appear here.'), '#tags' => TRUE, '#default_value' => $default, '#target_type' => 'taxonomy_term', diff --git a/src/Plugin/Condition/ParentNodeHasTerm.php b/src/Plugin/Condition/ParentNodeHasTerm.php index 098a80c9..3bc08641 100644 --- a/src/Plugin/Condition/ParentNodeHasTerm.php +++ b/src/Plugin/Condition/ParentNodeHasTerm.php @@ -7,7 +7,7 @@ namespace Drupal\islandora\Plugin\Condition; * * @Condition( * id = "parent_node_has_term", - * label = @Translation("Parent node for media has term"), + * label = @Translation("Parent node for media has term with URI"), * context = { * "media" = @ContextDefinition("entity:media", required = TRUE , label = @Translation("media")) * }