Browse Source

Using Context instead of Rules (#68)

* Committing to test on new environment

* Can execute pre-configured reactions through Context UI

* Exporting actions

* Adding contexts

* Renaming

* Using switch in hook implementations

* Ready to test without rules and just context

* Trimming down hooks and removing dsm() calls

* Fixes after testing large batch operations

* Functional tests and more testing

* Coding standards

* Removing duplicate

* Config export

* Removing media bundle condition for now.

* Tightened conditions.  Fixed context caching bug.

* Ignoring schema exceptions and disabling module provided contexts in functional tests

* Oh that's hilarious

* Responding to @whikoj's feedback

* s/deindex/delete

* Coding standards
pull/756/head
dannylamb 7 years ago committed by Jared Whiklo
parent
commit
c807bab123
  1. 2
      composer.json
  2. 34
      config/install/context.context.file.yml
  3. 35
      config/install/context.context.media.yml
  4. 36
      config/install/context.context.node.yml
  5. 51
      config/install/rules.reaction.broadcast_content_create_event.yml
  6. 51
      config/install/rules.reaction.broadcast_content_delete_event.yml
  7. 51
      config/install/rules.reaction.broadcast_content_update_event.yml
  8. 50
      config/install/rules.reaction.broadcast_file_create_event.yml
  9. 50
      config/install/rules.reaction.broadcast_file_delete_event.yml
  10. 50
      config/install/rules.reaction.broadcast_file_update_event.yml
  11. 51
      config/install/rules.reaction.broadcast_media_create_event.yml
  12. 50
      config/install/rules.reaction.broadcast_media_delete_event.yml
  13. 51
      config/install/rules.reaction.broadcast_media_update_event.yml
  14. 16
      config/install/system.action.delete_file_from_fedora.yml
  15. 16
      config/install/system.action.delete_media_from_triplestore.yml
  16. 16
      config/install/system.action.delete_node_from_fedora.yml
  17. 16
      config/install/system.action.delete_node_from_triplestore.yml
  18. 16
      config/install/system.action.index_file_in_fedora.yml
  19. 16
      config/install/system.action.index_media_in_fedora.yml
  20. 16
      config/install/system.action.index_media_in_triplestore.yml
  21. 16
      config/install/system.action.index_node_in_fedora.yml
  22. 16
      config/install/system.action.index_node_in_triplestore.yml
  23. 3
      config/install/views.view.file_checksum.yml
  24. 45
      config/schema/islandora.schema.yml
  25. 3
      islandora.info.yml
  26. 113
      islandora.module
  27. 10
      islandora.services.yml
  28. 52
      src/ContextProvider/FileContextProvider.php
  29. 71
      src/ContextProvider/FileRouteContextProvider.php
  30. 52
      src/ContextProvider/MediaContextProvider.php
  31. 77
      src/ContextProvider/MediaRouteContextProvider.php
  32. 52
      src/ContextProvider/NodeContextProvider.php
  33. 220
      src/EventGenerator/EmitEvent.php
  34. 50
      src/EventGenerator/EventGeneratorActionBase.php
  35. 17
      src/Form/IslandoraSettingsForm.php
  36. 99
      src/IslandoraContextManager.php
  37. 21
      src/IslandoraServiceProvider.php
  38. 16
      src/Plugin/Action/EmitFileEvent.php
  39. 16
      src/Plugin/Action/EmitMediaEvent.php
  40. 16
      src/Plugin/Action/EmitNodeEvent.php
  41. 35
      src/Plugin/Condition/IsFile.php
  42. 35
      src/Plugin/Condition/IsMedia.php
  43. 35
      src/Plugin/Condition/IsNode.php
  44. 15
      src/Plugin/ContextReaction/DeleteReaction.php
  45. 15
      src/Plugin/ContextReaction/IndexReaction.php
  46. 160
      src/Plugin/RulesAction/Broadcaster.php
  47. 42
      src/Plugin/RulesAction/CreateEventGenerator.php
  48. 42
      src/Plugin/RulesAction/DeleteEventGenerator.php
  49. 42
      src/Plugin/RulesAction/UpdateEventGenerator.php
  50. 88
      src/PresetReaction/PresetReaction.php
  51. 5
      src/Tests/Web/IslandoraWebTestBase.php
  52. 6
      src/VersionCounter/VersionCounter.php
  53. 508
      tests/src/Functional/EmitNodeEventTest.php
  54. 75
      tests/src/Functional/IndexingTest.php
  55. 58
      tests/src/Functional/IsFileTest.php
  56. 52
      tests/src/Functional/IsMediaTest.php
  57. 50
      tests/src/Functional/IsNodeTest.php
  58. 123
      tests/src/Functional/IslandoraFunctionalTestBase.php
  59. 138
      tests/src/Kernel/BroadcasterTest.php
  60. 36
      tests/src/Kernel/CreateEventGeneratorTest.php
  61. 36
      tests/src/Kernel/DeleteEventGeneratorTest.php
  62. 59
      tests/src/Kernel/EventGeneratorActionTestBase.php
  63. 40
      tests/src/Kernel/EventGeneratorTest.php
  64. 56
      tests/src/Kernel/EventGeneratorTestBase.php
  65. 6
      tests/src/Kernel/IslandoraKernelTestBase.php
  66. 36
      tests/src/Kernel/UpdateEventGeneratorTest.php
  67. 109
      tests/src/Kernel/VersionCounterTest.php

2
composer.json

@ -16,7 +16,7 @@
], ],
"require": { "require": {
"drupal/inline_entity_form": "^1.0@beta", "drupal/inline_entity_form": "^1.0@beta",
"drupal/rules": "3.x-dev", "drupal/context": "^4.0",
"drupal/search_api": "^1.0@beta", "drupal/search_api": "^1.0@beta",
"islandora/jsonld": "dev-8.x-1.x", "islandora/jsonld": "dev-8.x-1.x",
"stomp-php/stomp-php": "4.*", "stomp-php/stomp-php": "4.*",

34
config/install/context.context.file.yml

@ -0,0 +1,34 @@
uuid: 6804c3b4-a33e-448c-97cc-cfcab3afe7c6
langcode: en
status: true
dependencies:
enforced:
module:
- islandora
module:
- islandora
name: file
label: File
group: Islandora
description: 'Reactions for all Files'
requireAllConditions: false
disabled: false
conditions:
is_file:
id: is_file
negate: 0
uuid: a98d439f-5512-4e60-965c-b215b8f78438
context_mapping:
file: '@islandora.file_route_context_provider:file'
reactions:
index:
id: index
actions:
index_file_in_fedora: index_file_in_fedora
saved: false
delete:
id: delete
actions:
delete_file_from_fedora: delete_file_from_fedora
saved: false
weight: 0

35
config/install/context.context.media.yml

@ -0,0 +1,35 @@
d: c14bfcb1-7e62-4a3c-98e9-0cdbadbe5c45
langcode: en
status: true
dependencies:
enforced:
module:
- islandora
module:
- islandora
name: media
label: Media
group: Islandora
description: 'Reactions for all Media'
requireAllConditions: false
disabled: false
conditions:
is_media:
id: is_media
negate: 0
uuid: 2e291ec1-11ee-4c36-ba7e-cb05128bf27d
context_mapping:
media: '@islandora.media_route_context_provider:media'
reactions:
index:
id: index
actions:
index_media_in_fedora: index_media_in_fedora
index_media_in_triplestore: index_media_in_triplestore
saved: false
delete:
id: delete
actions:
delete_media_from_triplestore: delete_media_from_triplestore
saved: false
weight: 0

36
config/install/context.context.node.yml

@ -0,0 +1,36 @@
d: 48525bb3-36a2-449a-92ed-1ab3e94b3da0
langcode: en
status: true
dependencies:
enforced:
module:
- islandora
module:
- islandora
name: node
label: Node
group: Islandora
description: 'Reactions for all Nodes'
requireAllConditions: false
disabled: false
conditions:
is_node:
id: is_node
negate: 0
uuid: f25d55aa-1664-4202-b846-afd20b527da2
context_mapping:
node: '@node.node_route_context:node'
reactions:
index:
id: index
actions:
index_node_in_fedora: index_node_in_fedora
index_node_in_triplestore: index_node_in_triplestore
saved: false
delete:
id: delete
actions:
delete_node_from_fedora: delete_node_from_fedora
delete_node_from_triplestore: delete_node_from_triplestore
saved: false
weight: 0

51
config/install/rules.reaction.broadcast_content_create_event.yml

@ -1,51 +0,0 @@
uuid: b8d1db0c-be8f-47a7-889f-ac710db2e18a
langcode: en
status: true
dependencies:
enforced:
module:
- islandora
id: broadcast_content_create_event
label: 'Broadcast Content Create Event'
events:
-
event_name: 'rules_entity_insert:node'
description: 'Broadcasts an AS2 event to a message broker when Content is created.'
tags:
- ''
config_version: '3'
expression:
id: rules_rule
uuid: 40cabb74-fcc7-4498-90f9-3a4ec71b3710
conditions:
id: rules_and
uuid: 8e0d7b29-15f4-448d-b79a-d85ef97d274e
conditions: { }
actions:
id: rules_action_set
uuid: 0f832259-2aa1-45a4-83df-d9e0ff4c7edd
actions:
-
id: rules_action
uuid: 922264b9-3db1-42eb-9e01-e781a58a3695
context_values: { }
context_mapping:
entity: node
user: '@user.current_user_context:current_user'
context_processors: { }
provides_mapping: { }
action_id: islandora_create_event_generator
-
id: rules_action
uuid: 0e953790-c751-4de8-99b3-c729ba1e09e3
context_values:
recipients:
- "activemq:queue:islandora-indexing-fcrepo-content\r"
- 'activemq:queue:islandora-indexing-triplestore-index'
context_mapping:
message: event_message
context_processors:
recipients:
rules_tokens: { }
provides_mapping: { }
action_id: islandora_broadcast

51
config/install/rules.reaction.broadcast_content_delete_event.yml

@ -1,51 +0,0 @@
uuid: 752003c1-8d98-4d33-b6d3-2bdde52e77bf
langcode: en
status: true
dependencies:
enforced:
module:
- islandora
id: broadcast_content_delete_event
label: 'Broadcast Content Delete Event'
events:
-
event_name: 'rules_entity_delete:node'
description: 'Broadcasts an AS2 event to a message broker when Content is deleted.'
tags:
- ''
config_version: '3'
expression:
id: rules_rule
uuid: b5ca11c1-f80c-4dce-a9f7-9d5db0fec971
conditions:
id: rules_and
uuid: ede4e2b7-ce4c-4a9d-a5ff-a18a248ccd4c
conditions: { }
actions:
id: rules_action_set
uuid: 7c96bc08-7b7e-499d-8535-a2c735f6b98b
actions:
-
id: rules_action
uuid: 36647c5b-3355-4d4b-8550-5b0170b98ad1
context_values: { }
context_mapping:
entity: node
user: '@user.current_user_context:current_user'
context_processors: { }
provides_mapping: { }
action_id: islandora_delete_event_generator
-
id: rules_action
uuid: 9257b59c-466c-43d7-8690-62dd87a053eb
context_values:
recipients:
- "activemq:queue:islandora-indexing-fcrepo-delete\r"
- 'activemq:queue:islandora-indexing-triplestore-delete'
context_mapping:
message: event_message
context_processors:
recipients:
rules_tokens: { }
provides_mapping: { }
action_id: islandora_broadcast

51
config/install/rules.reaction.broadcast_content_update_event.yml

@ -1,51 +0,0 @@
uuid: 9eeb9a6a-d3ba-41b3-9f1f-68bd016fe8b1
langcode: en
status: true
dependencies:
enforced:
module:
- islandora
id: broadcast_content_update_event
label: 'Broadcast Content Update Event'
events:
-
event_name: 'rules_entity_update:node'
description: 'Broadcasts an AS2 event to a message broker when Content is updated.'
tags:
- ''
config_version: '3'
expression:
id: rules_rule
uuid: 55f8a158-3fe4-48bb-92f2-ba53630d56f3
conditions:
id: rules_and
uuid: 8269d20f-cb56-4399-8c87-17eb64bfe5eb
conditions: { }
actions:
id: rules_action_set
uuid: 567aca08-456f-449e-a91b-b17a21366bdf
actions:
-
id: rules_action
uuid: ee51523e-3bba-43a5-95ce-e998fb3392d3
context_values: { }
context_mapping:
entity: node
user: '@user.current_user_context:current_user'
context_processors: { }
provides_mapping: { }
action_id: islandora_update_event_generator
-
id: rules_action
uuid: 7b9cf7be-40e4-4490-ae6f-07a84eb112c4
context_values:
recipients:
- "activemq:queue:islandora-indexing-fcrepo-content\r"
- 'activemq:queue:islandora-indexing-triplestore-index'
context_mapping:
message: event_message
context_processors:
recipients:
rules_tokens: { }
provides_mapping: { }
action_id: islandora_broadcast

50
config/install/rules.reaction.broadcast_file_create_event.yml

@ -1,50 +0,0 @@
uuid: 5a444d61-ac7f-4cde-a8b7-2693bd825753
langcode: en
status: true
dependencies:
enforced:
module:
- islandora
id: broadcast_file_create_event
label: 'Broadcast File Create Event'
events:
-
event_name: 'rules_entity_insert:file'
description: 'Broadcasts an AS2 event to a message broker when a File is created.'
tags:
- ''
config_version: '3'
expression:
id: rules_rule
uuid: 54c6585d-453d-4143-8e54-760019f251d5
conditions:
id: rules_and
uuid: d82f7041-6522-4127-9676-d5534f7c1684
conditions: { }
actions:
id: rules_action_set
uuid: 0dc80d3b-2a60-4a47-afd6-4026d0fdffe6
actions:
-
id: rules_action
uuid: 0e42b62d-39ca-4f51-baa5-fdd1b37605fe
context_values: { }
context_mapping:
entity: file
user: '@user.current_user_context:current_user'
context_processors: { }
provides_mapping: { }
action_id: islandora_create_event_generator
-
id: rules_action
uuid: bcbbe914-54a9-4ff2-9d4b-98945682e087
context_values:
recipients:
- 'activemq:queue:islandora-indexing-fcrepo-file'
context_mapping:
message: event_message
context_processors:
recipients:
rules_tokens: { }
provides_mapping: { }
action_id: islandora_broadcast

50
config/install/rules.reaction.broadcast_file_delete_event.yml

@ -1,50 +0,0 @@
uuid: d35441d4-62d6-4e0d-bf6f-23ff32eab457
langcode: en
status: true
dependencies:
enforced:
module:
- islandora
id: broadcast_file_delete_event
label: 'Broadcast File Delete Event'
events:
-
event_name: 'rules_entity_delete:file'
description: 'Broadcasts an AS2 event to a message broker when a File is deleted.'
tags:
- ''
config_version: '3'
expression:
id: rules_rule
uuid: 69e8870c-9d20-4cc3-9a17-de940d4c9932
conditions:
id: rules_and
uuid: f465ad23-88a2-4026-a7ca-dbfc2d3aa031
conditions: { }
actions:
id: rules_action_set
uuid: 65ce1b3b-7986-4cf2-ab03-b950212f505e
actions:
-
id: rules_action
uuid: 2d6b2435-e986-4e53-8831-e4328aa63af3
context_values: { }
context_mapping:
entity: file
user: '@user.current_user_context:current_user'
context_processors: { }
provides_mapping: { }
action_id: islandora_delete_event_generator
-
id: rules_action
uuid: f1a6a455-5486-4a4a-a3c9-4e9b3867cf64
context_values:
recipients:
- 'activemq:queue:islandora-indexing-fcrepo-delete'
context_mapping:
message: event_message
context_processors:
recipients:
rules_tokens: { }
provides_mapping: { }
action_id: islandora_broadcast

50
config/install/rules.reaction.broadcast_file_update_event.yml

@ -1,50 +0,0 @@
d: 129d52bf-c67f-41fc-81fc-83f126f59a66
langcode: en
status: true
dependencies:
enforced:
module:
- islandora
id: broadcast_file_update_event
label: 'Broadcast File Update Event'
events:
-
event_name: 'rules_entity_update:file'
description: 'Broadcasts an AS2 event to a message broker when a File is updated.'
tags:
- ''
config_version: '3'
expression:
id: rules_rule
uuid: 5355a324-0af0-48bb-8168-e2810a9ee0a2
conditions:
id: rules_and
uuid: 0b266024-65ea-4e91-b0e6-ae88b8bfd5ed
conditions: { }
actions:
id: rules_action_set
uuid: 85a784ad-7f03-4ba9-8c01-f87f33f15d5e
actions:
-
id: rules_action
uuid: a7dcf8e2-7e2d-4c9b-b658-e4f3f28898a1
context_values: { }
context_mapping:
entity: file
user: '@user.current_user_context:current_user'
context_processors: { }
provides_mapping: { }
action_id: islandora_update_event_generator
-
id: rules_action
uuid: 60aa1b60-9ea4-40d1-bfbf-d036a617179a
context_values:
recipients:
- 'activemq:queue:islandora-indexing-fcrepo-file'
context_mapping:
message: event_message
context_processors:
recipients:
rules_tokens: { }
provides_mapping: { }
action_id: islandora_broadcast

51
config/install/rules.reaction.broadcast_media_create_event.yml

@ -1,51 +0,0 @@
uuid: 4474df44-01ee-4abf-84f9-9aa41d787d27
langcode: en
status: true
dependencies:
enforced:
module:
- islandora
id: broadcast_media_create_event
label: 'Broadcast Media Create Event'
events:
-
event_name: 'rules_entity_insert:media'
description: 'Broadcasts an AS2 event to a message broker when Media is created.'
tags:
- ''
config_version: '3'
expression:
id: rules_rule
uuid: d4ba4cc1-3b18-400d-867f-1605d72d9717
conditions:
id: rules_and
uuid: 9ccf9d29-89be-4b1d-8417-230e33f8fd6f
conditions: { }
actions:
id: rules_action_set
uuid: 47afb6b3-e39e-4c3a-af47-669e963353a2
actions:
-
id: rules_action
uuid: dff4ebfb-84c5-4d0f-acf7-62e3edbab143
context_values: { }
context_mapping:
entity: media
user: '@user.current_user_context:current_user'
context_processors: { }
provides_mapping: { }
action_id: islandora_create_event_generator
-
id: rules_action
uuid: 6b0d07b3-048e-43ac-8a9f-2c5203f8080e
context_values:
recipients:
- "activemq:queue:islandora-indexing-fcrepo-media\r"
- "activemq:queue:islandora-indexing-triplestore-index"
context_mapping:
message: event_message
context_processors:
recipients:
rules_tokens: { }
provides_mapping: { }
action_id: islandora_broadcast

50
config/install/rules.reaction.broadcast_media_delete_event.yml

@ -1,50 +0,0 @@
uuid: af85f943-2cc0-455c-97b0-4035d15f55ec
langcode: en
status: true
dependencies:
enforced:
module:
- islandora
id: broadcast_media_delete_event
label: 'Broadcast Media Delete Event'
events:
-
event_name: 'rules_entity_delete:media'
description: 'Broadcasts an AS2 event to a message broker when Media is deleted.'
tags:
- ''
config_version: '3'
expression:
id: rules_rule
uuid: abb78ad6-5f27-43e7-890c-c2a12e76ce51
conditions:
id: rules_and
uuid: 8c386012-6796-4138-b4e7-d438916c713f
conditions: { }
actions:
id: rules_action_set
uuid: 6599d3f0-dcd7-482c-bcb4-fa76b012da6a
actions:
-
id: rules_action
uuid: 7003fe72-49f7-4a3a-b95b-781ef1c61615
context_values: { }
context_mapping:
entity: media
user: '@user.current_user_context:current_user'
context_processors: { }
provides_mapping: { }
action_id: islandora_delete_event_generator
-
id: rules_action
uuid: 8a5dc149-9629-4e94-a8d5-3ff6069d6af6
context_values:
recipients:
- 'activemq:queue:islandora-indexing-triplestore-delete'
context_mapping:
message: event_message
context_processors:
recipients:
rules_tokens: { }
provides_mapping: { }
action_id: islandora_broadcast

51
config/install/rules.reaction.broadcast_media_update_event.yml

@ -1,51 +0,0 @@
uuid: d5d823ce-cfd0-4e9c-85c9-9090660d831e
langcode: en
status: true
dependencies:
enforced:
module:
- islandora
id: broadcast_media_update_event
label: 'Broadcast Media Update Event'
events:
-
event_name: 'rules_entity_update:media'
description: 'Broadcasts an AS2 event to a message broker when Media is updated.'
tags:
- ''
config_version: '3'
expression:
id: rules_rule
uuid: 3fb05cc7-f27a-4a79-8e98-544e4aabaf15
conditions:
id: rules_and
uuid: 585b55af-847d-447a-afee-01b855807c4a
conditions: { }
actions:
id: rules_action_set
uuid: bb96d589-0d63-4b1b-89d7-faf358171d40
actions:
-
id: rules_action
uuid: 60a1f348-f85f-45fc-a785-b279b522863a
context_values: { }
context_mapping:
entity: media
user: '@user.current_user_context:current_user'
context_processors: { }
provides_mapping: { }
action_id: islandora_update_event_generator
-
id: rules_action
uuid: ee50c8ae-a9cd-479a-aa86-3567629a01b8
context_values:
recipients:
- "activemq:queue:islandora-indexing-fcrepo-media\r"
- "activemq:queue:islandora-indexing-triplestore-index"
context_mapping:
message: event_message
context_processors:
recipients:
rules_tokens: { }
provides_mapping: { }
action_id: islandora_broadcast

16
config/install/system.action.delete_file_from_fedora.yml

@ -0,0 +1,16 @@
uuid: 76d619dd-54f1-4047-8e89-54d5f1a7c16a
langcode: en
status: true
dependencies:
module:
- islandora
enforced:
module:
- islandora
id: delete_file_from_fedora
label: 'Delete File from Fedora'
type: file
plugin: emit_file_event
configuration:
queue: 'islandora-indexing-fcrepo-delete'
event: delete

16
config/install/system.action.delete_media_from_triplestore.yml

@ -0,0 +1,16 @@
uuid: a5f0a427-9c30-41ec-92b8-04b47855aee1
langcode: en
status: true
dependencies:
module:
- islandora
enforced:
module:
- islandora
id: delete_media_from_triplestore
label: 'Delete Media from Triplestore'
type: media
plugin: emit_media_event
configuration:
queue: 'islandora-indexing-triplestore-delete'
event: delete

16
config/install/system.action.delete_node_from_fedora.yml

@ -0,0 +1,16 @@
uuid: 23ad55f4-301b-4ffb-b542-4cf2c0c291d8
langcode: en
status: true
dependencies:
module:
- islandora
enforced:
module:
- islandora
id: delete_node_from_fedora
label: 'Delete Node from Fedora'
type: node
plugin: emit_node_event
configuration:
queue: 'islandora-indexing-fcrepo-delete'
event: delete

16
config/install/system.action.delete_node_from_triplestore.yml

@ -0,0 +1,16 @@
uuid: 4340c20a-d314-4cbf-a26f-c36d7180ede5
langcode: en
status: true
dependencies:
module:
- islandora
enforced:
module:
- islandora
id: delete_node_from_triplestore
label: 'Delete Node from Triplestore'
type: node
plugin: emit_node_event
configuration:
queue: 'islandora-indexing-triplestore-delete'
event: delete

16
config/install/system.action.index_file_in_fedora.yml

@ -0,0 +1,16 @@
uuid: 526d2857-de6f-472d-87e2-d8726db72301
langcode: en
status: true
dependencies:
module:
- islandora
enforced:
module:
- islandora
id: index_file_in_fedora
label: 'Index File in Fedora'
type: file
plugin: emit_file_event
configuration:
queue: 'islandora-indexing-fcrepo-file'
event: update

16
config/install/system.action.index_media_in_fedora.yml

@ -0,0 +1,16 @@
d: 27ce85fc-a9fe-46f5-b518-5af1126e2368
langcode: en
status: true
dependencies:
module:
- islandora
enforced:
module:
- islandora
id: index_media_in_fedora
label: 'Index Media in Fedora'
type: media
plugin: emit_media_event
configuration:
queue: 'islandora-indexing-fcrepo-media'
event: update

16
config/install/system.action.index_media_in_triplestore.yml

@ -0,0 +1,16 @@
uuid: f7c9169d-6556-4720-b225-11a09581272e
langcode: en
status: true
dependencies:
module:
- islandora
enforced:
module:
- islandora
id: index_media_in_triplestore
label: 'Index Media in Triplestore'
type: media
plugin: emit_media_event
configuration:
queue: 'islandora-indexing-triplestore-index'
event: update

16
config/install/system.action.index_node_in_fedora.yml

@ -0,0 +1,16 @@
uuid: edb74080-7cf3-45e7-97e2-78cd7494ea92
langcode: en
status: true
dependencies:
module:
- islandora
enforced:
module:
- islandora
id: index_node_in_fedora
label: 'Index Node in Fedora'
type: node
plugin: emit_node_event
configuration:
queue: 'islandora-indexing-fcrepo-content'
event: update

16
config/install/system.action.index_node_in_triplestore.yml

@ -0,0 +1,16 @@
uuid: 5012a177-307c-4ee0-a986-cd35d76f2a58
langcode: en
status: true
dependencies:
module:
- islandora
enforced:
module:
- islandora
id: index_node_in_triplestore
label: 'Index Node in Triplestore'
type: node
plugin: emit_node_event
configuration:
queue: 'islandora-indexing-triplestore-index'
event: update

3
config/install/views.view.file_checksum.yml

@ -9,6 +9,9 @@ dependencies:
- rest - rest
- serialization - serialization
- user - user
enforced:
module:
- islandora
_core: _core:
default_config_hash: e7XqQa-N16XDqeDTA7Ohgx6iLb6mIQ1RUs0krFFIbAw default_config_hash: e7XqQa-N16XDqeDTA7Ohgx6iLb6mIQ1RUs0krFFIbAw
id: file_checksum id: file_checksum

45
config/schema/islandora.schema.yml

@ -11,3 +11,48 @@ islandora.settings:
broadcast_queue: broadcast_queue:
type: string type: string
label: 'Queue that handles distributing messages amongst multiple recipients' label: 'Queue that handles distributing messages amongst multiple recipients'
action.configuration.emit_node_event:
type: mapping
label: 'Emit a Node event to a queue/topic'
mapping:
queue:
type: text
label: 'Queue'
event:
type: text
label: 'Event Type'
action.configuration.emit_media_event:
type: mapping
label: 'Emit a Media event to a queue/topic'
mapping:
queue:
type: text
label: 'Queue'
event:
type: text
label: 'Event Type'
action.configuration.emit_file_event:
type: mapping
label: 'Emit a Flie event to a queue/topic'
mapping:
queue:
type: text
label: 'Queue'
event:
type: text
label: 'Event Type'
condition.plugin.is_node:
type: condition.plugin
mapping:
condition.plugin.is_media:
type: condition.plugin
mapping:
condition.plugin.is_file:
type: condition.plugin
mapping:

3
islandora.info.yml

@ -13,10 +13,11 @@ dependencies:
- options - options
- inline_entity_form - inline_entity_form
- jsonld - jsonld
- rules
- search_api - search_api
- jwt - jwt
- media_entity_image - media_entity_image
- rest - rest
- filehash - filehash
- basic_auth - basic_auth
- context
- action

113
islandora.module

@ -14,8 +14,10 @@
* @author Diego Pino Navarro <dpino@metro.org> https://github.com/diegopino * @author Diego Pino Navarro <dpino@metro.org> https://github.com/diegopino
*/ */
use Drupal\Core\Database\IntegrityConstraintViolationException;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityInterface;
use Drupal\islandora\ContextProvider\NodeContextProvider;
use Drupal\islandora\ContextProvider\MediaContextProvider;
use Drupal\islandora\ContextProvider\FileContextProvider;
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
/** /**
@ -82,39 +84,102 @@ function islandora_rdf_namespaces() {
} }
/** /**
* Implements hook_ENTITY_TYPE_insert(). * Implements hook_entity_insert().
*/ */
function islandora_node_insert(EntityInterface $entity) { function islandora_entity_insert(EntityInterface $entity) {
// Creates a record in the db to track the number of changes to the entity. switch ($entity->getEntityType()->id()) {
$versionCounter = \Drupal::service('islandora.versioncounter'); case "node":
try { $provider = new NodeContextProvider($entity);
$versionCounter->create($entity->uuid()); break;
}
catch (IntegrityConstraintViolationException $e) { case "media":
\Drupal::logger('islandora')->error( $provider = new MediaContextProvider($entity);
'Attempted to create duplicate entry for @uuid in version counter ' . break;
'table. This should never happen.',
['@uuid' => $entity->uuid()] case "file":
); $provider = new FileContextProvider($entity);
break;
default:
$provider = NULL;
break;
}
if ($provider) {
$context_manager = \Drupal::service('context.manager');
$provided = $provider->getRuntimeContexts([]);
$context_manager->evaluateContexts($provided);
foreach ($context_manager->getActiveReactions('index') as $reaction) {
$reaction->execute($entity);
}
} }
} }
/** /**
* Implements hook_ENTITY_TYPE_update(). * Implements hook_entity_update().
*/ */
function islandora_node_update(EntityInterface $entity) { function islandora_entity_update(EntityInterface $entity) {
// Increments the number of changes to the entity. switch ($entity->getEntityType()->id()) {
$versionCounter = \Drupal::service('islandora.versioncounter'); case "node":
$versionCounter->increment($entity->uuid()); $provider = new NodeContextProvider($entity);
break;
case "media":
$provider = new MediaContextProvider($entity);
break;
case "file":
$provider = new FileContextProvider($entity);
break;
default:
$provider = NULL;
break;
}
if ($provider) {
$context_manager = \Drupal::service('context.manager');
$provided = $provider->getRuntimeContexts([]);
$context_manager->evaluateContexts($provided);
foreach ($context_manager->getActiveReactions('index') as $reaction) {
$reaction->execute($entity);
}
}
} }
/** /**
* Implements hook_ENTITY_TYPE_update(). * Implements hook_entity_delete().
*/ */
function islandora_node_delete(EntityInterface $entity) { function islandora_entity_delete(EntityInterface $entity) {
// Deletes the record in the db to track the number of changes to the entity. switch ($entity->getEntityType()->id()) {
$versionCounter = \Drupal::service('islandora.versioncounter'); case "node":
$versionCounter->delete($entity->uuid()); $provider = new NodeContextProvider($entity);
break;
case "media":
$provider = new MediaContextProvider($entity);
break;
case "file":
$provider = new FileContextProvider($entity);
break;
default:
$provider = NULL;
break;
}
if ($provider) {
$context_manager = \Drupal::service('context.manager');
$provided = $provider->getRuntimeContexts([]);
$context_manager->evaluateContexts($provided);
foreach ($context_manager->getActiveReactions('delete') as $reaction) {
$reaction->execute($entity);
}
}
} }
/** /**

10
islandora.services.yml

@ -24,3 +24,13 @@ services:
logger.channel.islandora: logger.channel.islandora:
parent: logger.channel_base parent: logger.channel_base
arguments: ['islandora'] arguments: ['islandora']
islandora.media_route_context_provider:
class: Drupal\islandora\ContextProvider\MediaRouteContextProvider
arguments: ['@current_route_match']
tags:
- { name: 'context_provider' }
islandora.file_route_context_provider:
class: Drupal\islandora\ContextProvider\FileRouteContextProvider
arguments: ['@current_route_match']
tags:
- { name: 'context_provider' }

52
src/ContextProvider/FileContextProvider.php

@ -0,0 +1,52 @@
<?php
namespace Drupal\islandora\ContextProvider;
use Drupal\file\FileInterface;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\ContextProviderInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Sets the provided media as a context.
*/
class FileContextProvider implements ContextProviderInterface {
use StringTranslationTrait;
/**
* File to provide in a context.
*
* @var \Drupal\file\FileInterface
*/
protected $file;
/**
* Constructs a new FileContextProvider.
*
* @var \Drupal\file\FileInterface $file
* The file to provide in a context.
*/
public function __construct(FileInterface $file) {
$this->file = $file;
}
/**
* {@inheritdoc}
*/
public function getRuntimeContexts(array $unqualified_context_ids) {
$context_definition = new ContextDefinition('entity:file', NULL, FALSE);
$context = new Context($context_definition, $this->file);
return ['@islandora.file_route_context_provider:file' => $context];
}
/**
* {@inheritdoc}
*/
public function getAvailableContexts() {
$context = new Context(new ContextDefinition('entity:file', $this->t('File from entity hook')));
return ['@islandora.file_route_context_provider:file' => $context];
}
}

71
src/ContextProvider/FileRouteContextProvider.php

@ -0,0 +1,71 @@
<?php
namespace Drupal\islandora\ContextProvider;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\ContextProviderInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Sets the current file as a context on file routes.
*/
class FileRouteContextProvider implements ContextProviderInterface {
use StringTranslationTrait;
/**
* The route match object.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* Constructs a new FileRouteContextProvider.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match object.
*/
public function __construct(RouteMatchInterface $route_match) {
$this->routeMatch = $route_match;
}
/**
* {@inheritdoc}
*/
public function getRuntimeContexts(array $unqualified_context_ids) {
$context_definition = new ContextDefinition('entity:file', NULL, FALSE);
$value = NULL;
// Hack the file out of the route.
$route_object = $this->routeMatch->getRouteObject();
if ($route_object) {
$route_contexts = $route_object->getOption('parameters');
if ($route_contexts && isset($route_contexts['file'])) {
$file = $this->routeMatch->getParameter('file');
if ($file) {
$value = $file;
}
}
}
$cacheability = new CacheableMetadata();
$cacheability->setCacheContexts(['route']);
$context = new Context($context_definition, $value);
$context->addCacheableDependency($cacheability);
return ['file' => $context];
}
/**
* {@inheritdoc}
*/
public function getAvailableContexts() {
$context = new Context(new ContextDefinition('entity:file', $this->t('File from URL')));
return ['file' => $context];
}
}

52
src/ContextProvider/MediaContextProvider.php

@ -0,0 +1,52 @@
<?php
namespace Drupal\islandora\ContextProvider;
use Drupal\media_entity\MediaInterface;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\ContextProviderInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Sets the provided media as a context.
*/
class MediaContextProvider implements ContextProviderInterface {
use StringTranslationTrait;
/**
* Media to provide in a context.
*
* @var \Drupal\media_entity\MediaInterface
*/
protected $media;
/**
* Constructs a new MediaRouteContext.
*
* @var \Drupal\media_entity\MediaInterface $media
* The media to provide in a context.
*/
public function __construct(MediaInterface $media) {
$this->media = $media;
}
/**
* {@inheritdoc}
*/
public function getRuntimeContexts(array $unqualified_context_ids) {
$context_definition = new ContextDefinition('entity:media', NULL, FALSE);
$context = new Context($context_definition, $this->media);
return ['@islandora.media_route_context_provider:media' => $context];
}
/**
* {@inheritdoc}
*/
public function getAvailableContexts() {
$context = new Context(new ContextDefinition('entity:media', $this->t('Media from entity hook')));
return ['@islandora.media_route_context_provider:media' => $context];
}
}

77
src/ContextProvider/MediaRouteContextProvider.php

@ -0,0 +1,77 @@
<?php
namespace Drupal\islandora\ContextProvider;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\ContextProviderInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\media_entity\Entity\Media;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Sets the current media as a context on media routes.
*/
class MediaRouteContextProvider implements ContextProviderInterface {
use StringTranslationTrait;
/**
* The route match object.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* Constructs a new MediaRouteContextProvider.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match object.
*/
public function __construct(RouteMatchInterface $route_match) {
$this->routeMatch = $route_match;
}
/**
* {@inheritdoc}
*/
public function getRuntimeContexts(array $unqualified_context_ids) {
$result = [];
$context_definition = new ContextDefinition('entity:media', NULL, FALSE);
$value = NULL;
// Hack the media out of the route.
$route_object = $this->routeMatch->getRouteObject();
if ($route_object) {
$route_contexts = $route_object->getOption('parameters');
if ($route_contexts && isset($route_contexts['media'])) {
$media = $this->routeMatch->getParameter('media');
if ($media) {
$value = $media;
}
}
elseif ($this->routeMatch->getRouteName() == 'entity.media.add_form') {
$media_bundle = $this->routeMatch->getParameter('media_bundle');
$value = Media::create(['bundle' => $media_bundle->id()]);
}
}
$cacheability = new CacheableMetadata();
$cacheability->setCacheContexts(['route']);
$context = new Context($context_definition, $value);
$context->addCacheableDependency($cacheability);
return ['media' => $context];
}
/**
* {@inheritdoc}
*/
public function getAvailableContexts() {
$context = new Context(new ContextDefinition('entity:media', $this->t('Media from URL')));
return ['media' => $context];
}
}

52
src/ContextProvider/NodeContextProvider.php

@ -0,0 +1,52 @@
<?php
namespace Drupal\islandora\ContextProvider;
use Drupal\node\NodeInterface;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\ContextProviderInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Sets the provided media as a context.
*/
class NodeContextProvider implements ContextProviderInterface {
use StringTranslationTrait;
/**
* Node to provide in a context.
*
* @var \Drupal\node\NodeInterface
*/
protected $node;
/**
* Constructs a new NodeContextProvider.
*
* @var \Drupal\node\NodeInterface $node
* The node to provide in a context.
*/
public function __construct(NodeInterface $node) {
$this->node = $node;
}
/**
* {@inheritdoc}
*/
public function getRuntimeContexts(array $unqualified_context_ids) {
$context_definition = new ContextDefinition('entity:node', NULL, FALSE);
$context = new Context($context_definition, $this->node);
return ['@node.node_route_context:node' => $context];
}
/**
* {@inheritdoc}
*/
public function getAvailableContexts() {
$context = new Context(new ContextDefinition('entity:node', $this->t('Node from entity hook')));
return ['@node.node_route_context:node' => $context];
}
}

220
src/EventGenerator/EmitEvent.php

@ -0,0 +1,220 @@
<?php
namespace Drupal\islandora\EventGenerator;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Action\ConfigurableActionBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\jwt\Authentication\Provider\JwtAuth;
use Stomp\Exception\StompException;
use Stomp\StatefulStomp;
use Stomp\Transport\Message;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Configurable action base for actions that publish messages to queues.
*/
abstract class EmitEvent extends ConfigurableActionBase implements ContainerFactoryPluginInterface {
/**
* Current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $account;
/**
* User storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $userStorage;
/**
* Event generator service.
*
* @var \Drupal\islandora\EventGenerator\EventGeneratorInterface
*/
protected $eventGenerator;
/**
* Stomp client.
*
* @var \Stomp\StatefulStomp
*/
protected $stomp;
/**
* The JWT Auth Service.
*
* @var \Drupal\jwt\Authentication\Provider\JwtAuth
*/
protected $auth;
/**
* Constructs a EmitEvent action.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Session\AccountInterface $account
* Current user.
* @param \Drupal\Core\Entity\EntityStorageInterface $user_storage
* User storage.
* @param \Drupal\islandora\EventGenerator\EventGeneratorInterface $event_generator
* EventGenerator service to serialize AS2 events.
* @param \Stomp\StatefulStomp $stomp
* Stomp client.
* @param \Drupal\jwt\Authentication\Provider\JwtAuth $auth
* JWT Auth client.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
AccountInterface $account,
EntityStorageInterface $user_storage,
EventGeneratorInterface $event_generator,
StatefulStomp $stomp,
JwtAuth $auth
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->account = $account;
$this->userStorage = $user_storage;
$this->eventGenerator = $event_generator;
$this->stomp = $stomp;
$this->auth = $auth;
}
/**
* {@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')->getStorage('user'),
$container->get('islandora.eventgenerator'),
$container->get('islandora.stomp'),
$container->get('jwt.authentication.jwt')
);
}
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
// Include a token for later authentication in the message.
$token = $this->auth->generateToken();
if (empty($token)) {
// JWT isn't properly configured. Log and notify user.
\Drupal::logger('islandora')->error(
t('Error getting JWT token for message. Check JWT Configuration.')
);
drupal_set_message(
t('Error getting JWT token for message. Check JWT Configuration.'), 'error'
);
return;
}
// Generate the event message.
$user = $this->userStorage->load($this->account->id());
if ($this->configuration['event'] == 'create') {
$message = $this->eventGenerator->generateCreateEvent($entity, $user);
}
elseif ($this->configuration['event'] == 'update') {
$message = $this->eventGenerator->generateUpdateEvent($entity, $user);
}
elseif ($this->configuration['event'] == 'delete') {
$message = $this->eventGenerator->generateDeleteEvent($entity, $user);
}
// Transform message from string into a proper message object.
$message = new Message($message, ['Authorization' => "Bearer $token"]);
// Send the message.
try {
$this->stomp->begin();
$this->stomp->send($this->configuration['queue'], $message);
$this->stomp->commit();
}
catch (StompException $e) {
// Log it.
\Drupal::logger('islandora')->error(
'Error publishing message: @msg',
['@msg' => $e->getMessage()]
);
// Notify user.
drupal_set_message(
t('Error publishing message: @msg',
['@msg' => $e->getMessage()]
),
'error'
);
}
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'queue' => '',
'event' => 'create',
];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['queue'] = [
'#type' => 'textfield',
'#title' => t('Queue'),
'#default_value' => $this->configuration['queue'],
'#required' => TRUE,
'#rows' => '8',
'#description' => t('Name of queue to which event is published'),
];
$form['event'] = [
'#type' => 'select',
'#title' => t('Event type'),
'#default_value' => $this->configuration['event'],
'#description' => t('Type of event to emit'),
'#options' => [
'create' => t('Create'),
'update' => t('Update'),
'delete' => t('Delete'),
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['queue'] = $form_state->getValue('queue');
$this->configuration['event'] = $form_state->getValue('event');
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
$result = AccessResult::allowed();
return $return_as_object ? $result : $result->isAllowed();
}
}

50
src/EventGenerator/EventGeneratorActionBase.php

@ -1,50 +0,0 @@
<?php
namespace Drupal\islandora\EventGenerator;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\rules\Core\RulesActionBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Abstract base class for EventGenerator RulesActions. Sets up DI.
*/
abstract class EventGeneratorActionBase extends RulesActionBase implements ContainerFactoryPluginInterface {
/**
* The event generator that will serialize the events.
*
* @var \Drupal\islandora\EventGenerator\EventGeneratorInterface
*/
protected $eventGenerator;
/**
* Constructs a EventGeneratorActionBase.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\islandora\EventGenerator\EventGeneratorInterface $event_generator
* The EventGenerator service.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EventGeneratorInterface $event_generator) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->eventGenerator = $event_generator;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('islandora.eventgenerator')
);
}
}

17
src/Form/IslandoraSettingsForm.php

@ -15,8 +15,6 @@ class IslandoraSettingsForm extends ConfigFormBase {
const CONFIG_NAME = 'islandora.settings'; const CONFIG_NAME = 'islandora.settings';
const BROKER_URL = 'broker_url'; const BROKER_URL = 'broker_url';
const FEDORA_REST_ENDPOINT = 'fedora_rest_endpoint';
const BROADCAST_QUEUE = 'broadcast_queue';
/** /**
* {@inheritdoc} * {@inheritdoc}
@ -46,19 +44,6 @@ class IslandoraSettingsForm extends ConfigFormBase {
'#default_value' => $config->get(self::BROKER_URL), '#default_value' => $config->get(self::BROKER_URL),
]; ];
$form[self::BROADCAST_QUEUE] = [
'#type' => 'textfield',
'#title' => $this->t('Broadcast Queue'),
'#default_value' => $config->get(self::BROADCAST_QUEUE),
];
$form[self::FEDORA_REST_ENDPOINT] = [
'#type' => 'textfield',
'#title' => $this->t('Fedora REST Endpoint'),
'#description' => $this->t('The URL for your Fedora instance.'),
'#default_value' => $config->get(self::FEDORA_REST_ENDPOINT),
];
return parent::buildForm($form, $form_state); return parent::buildForm($form, $form_state);
} }
@ -99,8 +84,6 @@ class IslandoraSettingsForm extends ConfigFormBase {
$config $config
->set(self::BROKER_URL, $form_state->getValue(self::BROKER_URL)) ->set(self::BROKER_URL, $form_state->getValue(self::BROKER_URL))
->set(self::BROADCAST_QUEUE, $form_state->getValue(self::BROADCAST_QUEUE))
->set(self::FEDORA_REST_ENDPOINT, $form_state->getValue(self::FEDORA_REST_ENDPOINT))
->save(); ->save();
parent::submitForm($form, $form_state); parent::submitForm($form, $form_state);

99
src/IslandoraContextManager.php

@ -0,0 +1,99 @@
<?php
namespace Drupal\islandora;
use Drupal\context\ContextManager;
use Drupal\context\ContextInterface;
use Drupal\Core\Condition\ConditionPluginCollection;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Drupal\Component\Plugin\Exception\ContextException;
/**
* Threads in additional (core) Contexts to provide to Conditions.
*/
class IslandoraContextManager extends ContextManager {
/**
* Evaluate all context conditions.
*
* @param \Drupal\Core\Plugin\Context\Context[] $provided
* Additional provided (core) contexts to apply to Conditions.
*/
public function evaluateContexts(array $provided = []) {
$this->activeContexts = [];
/** @var \Drupal\context\ContextInterface $context */
foreach ($this->getContexts() as $context) {
if ($this->evaluateContextConditions($context, $provided) && !$context->disabled()) {
$this->activeContexts[$context->id()] = $context;
}
}
$this->contextConditionsEvaluated = TRUE;
}
/**
* Evaluate a contexts conditions.
*
* @param \Drupal\context\ContextInterface $context
* The context to evaluate conditions for.
* @param \Drupal\Core\Plugin\Context\Context[] $provided
* Additional provided (core) contexts to apply to Conditions.
*
* @return bool
* TRUE if conditions pass
*/
public function evaluateContextConditions(ContextInterface $context, array $provided = []) {
$conditions = $context->getConditions();
// Apply context to any context aware conditions.
$this->applyContexts($conditions, $provided);
// Set the logic to use when validating the conditions.
$logic = $context->requiresAllConditions()
? 'and'
: 'or';
// Of there are no conditions then the context will be
// applied as a site wide context.
if (!count($conditions)) {
$logic = 'and';
}
return $this->resolveConditions($conditions, $logic);
}
/**
* Apply context to all the context aware conditions in the collection.
*
* @param \Drupal\Core\Condition\ConditionPluginCollection $conditions
* A collection of conditions to apply context to.
* @param \Drupal\Core\Plugin\Context\Context[] $provided
* Additional provided (core) contexts to apply to Conditions.
*
* @return bool
* TRUE if conditions pass
*/
protected function applyContexts(ConditionPluginCollection &$conditions, array $provided = []) {
foreach ($conditions as $condition) {
if ($condition instanceof ContextAwarePluginInterface) {
try {
if (empty($provided)) {
$contexts = $this->contextRepository->getRuntimeContexts(array_values($condition->getContextMapping()));
}
else {
$contexts = $provided;
}
$this->contextHandler->applyContextMapping($condition, $contexts);
}
catch (ContextException $e) {
return FALSE;
}
}
}
return TRUE;
}
}

21
src/IslandoraServiceProvider.php

@ -0,0 +1,21 @@
<?php
namespace Drupal\islandora;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
/**
* Modifies the language manager service.
*/
class IslandoraServiceProvider extends ServiceProviderBase {
/**
* {@inheritdoc}
*/
public function alter(ContainerBuilder $container) {
$definition = $container->getDefinition('context.manager');
$definition->setClass('Drupal\islandora\IslandoraContextManager');
}
}

16
src/Plugin/Action/EmitFileEvent.php

@ -0,0 +1,16 @@
<?php
namespace Drupal\islandora\Plugin\Action;
use Drupal\islandora\EventGenerator\EmitEvent;
/**
* Emits a File event.
*
* @Action(
* id = "emit_file_event",
* label = @Translation("Emit a file event to a queue/topic"),
* type = "file"
* )
*/
class EmitFileEvent extends EmitEvent {}

16
src/Plugin/Action/EmitMediaEvent.php

@ -0,0 +1,16 @@
<?php
namespace Drupal\islandora\Plugin\Action;
use Drupal\islandora\EventGenerator\EmitEvent;
/**
* Emits a Media event.
*
* @Action(
* id = "emit_media_event",
* label = @Translation("Emit a media event to a queue/topic"),
* type = "media"
* )
*/
class EmitMediaEvent extends EmitEvent {}

16
src/Plugin/Action/EmitNodeEvent.php

@ -0,0 +1,16 @@
<?php
namespace Drupal\islandora\Plugin\Action;
use Drupal\islandora\EventGenerator\EmitEvent;
/**
* Emits a Node event.
*
* @Action(
* id = "emit_node_event",
* label = @Translation("Emit a node event to a queue/topic"),
* type = "node"
* )
*/
class EmitNodeEvent extends EmitEvent {}

35
src/Plugin/Condition/IsFile.php

@ -0,0 +1,35 @@
<?php
namespace Drupal\islandora\Plugin\Condition;
use Drupal\Core\Condition\ConditionPluginBase;
use Drupal\file\FileInterface;
/**
* Provides an 'Is File' condition.
*
* @Condition(
* id = "is_file",
* label = @Translation("Is File"),
* context = {
* "file" = @ContextDefinition("entity:file", label = @Translation("File"))
* }
* )
*/
class IsFile extends ConditionPluginBase {
/**
* {@inheritdoc}
*/
public function summary() {
return $this->t('The entity is a File');
}
/**
* {@inheritdoc}
*/
public function evaluate() {
return $this->getContextValue('file') instanceof FileInterface;
}
}

35
src/Plugin/Condition/IsMedia.php

@ -0,0 +1,35 @@
<?php
namespace Drupal\islandora\Plugin\Condition;
use Drupal\Core\Condition\ConditionPluginBase;
use Drupal\media_entity\MediaInterface;
/**
* Provides an 'Is Media' condition.
*
* @Condition(
* id = "is_media",
* label = @Translation("Is Media"),
* context = {
* "media" = @ContextDefinition("entity:media", label = @Translation("Media"))
* }
* )
*/
class IsMedia extends ConditionPluginBase {
/**
* {@inheritdoc}
*/
public function summary() {
return $this->t('The entity is a Media');
}
/**
* {@inheritdoc}
*/
public function evaluate() {
return $this->getContextValue('media') instanceof MediaInterface;
}
}

35
src/Plugin/Condition/IsNode.php

@ -0,0 +1,35 @@
<?php
namespace Drupal\islandora\Plugin\Condition;
use Drupal\Core\Condition\ConditionPluginBase;
use Drupal\node\NodeInterface;
/**
* Provides an 'Is Node' condition.
*
* @Condition(
* id = "is_node",
* label = @Translation("Is Node"),
* context = {
* "node" = @ContextDefinition("entity:node", label = @Translation("Node"))
* }
* )
*/
class IsNode extends ConditionPluginBase {
/**
* {@inheritdoc}
*/
public function summary() {
return $this->t('The entity is a Node');
}
/**
* {@inheritdoc}
*/
public function evaluate() {
return $this->getContextValue('node') instanceof NodeInterface;
}
}

15
src/Plugin/ContextReaction/DeleteReaction.php

@ -0,0 +1,15 @@
<?php
namespace Drupal\islandora\Plugin\ContextReaction;
use Drupal\islandora\PresetReaction\PresetReaction;
/**
* Delete context reaction.
*
* @ContextReaction(
* id = "delete",
* label = @Translation("Delete")
* )
*/
class DeleteReaction extends PresetReaction {}

15
src/Plugin/ContextReaction/IndexReaction.php

@ -0,0 +1,15 @@
<?php
namespace Drupal\islandora\Plugin\ContextReaction;
use Drupal\islandora\PresetReaction\PresetReaction;
/**
* Indexing context reaction.
*
* @ContextReaction(
* id = "index",
* label = @Translation("Index")
* )
*/
class IndexReaction extends PresetReaction {}

160
src/Plugin/RulesAction/Broadcaster.php

@ -1,160 +0,0 @@
<?php
namespace Drupal\islandora\Plugin\RulesAction;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\islandora\Form\IslandoraSettingsForm;
use Drupal\rules\Core\RulesActionBase;
use Drupal\jwt\Authentication\Provider\JwtAuth;
use Stomp\Exception\StompException;
use Stomp\StatefulStomp;
use Stomp\Transport\Message;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides an action to broadcast an event to multiple queues/topics.
*
* @RulesAction(
* id = "islandora_broadcast",
* label = @Translation("Broadcast Message"),
* category = @Translation("Islandora"),
* context = {
* "message" = @ContextDefinition("string",
* label = @Translation("Message")
* ),
* "recipients" = @ContextDefinition("string",
* label = @Translation("Recipients"),
* description = @Translation("Queues/Topics to receive the message"),
* multiple = TRUE
* ),
* }
* )
*/
class Broadcaster extends RulesActionBase implements ContainerFactoryPluginInterface {
/**
* Stomp client.
*
* @var \Stomp\StatefulStomp
*/
protected $stomp;
/**
* Name of broadcast queue.
*
* @var string
*/
protected $broadcastQueue;
/**
* The JWT Auth Service.
*
* @var \Drupal\jwt\Authentication\Provider\JwtAuth
*/
protected $auth;
/**
* Constructs a BroadcastAction.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param string $broadcast_queue
* Name of queue that will handle distributing the broadcast.
* @param \Stomp\StatefulStomp $stomp
* Stomp client.
* @param \Drupal\jwt\Authentication\Provider\JwtAuth $auth
* JWT Auth client.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
$broadcast_queue,
StatefulStomp $stomp,
JwtAuth $auth
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->broadcastQueue = $broadcast_queue;
$this->stomp = $stomp;
$this->auth = $auth;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$config = $container->get('config.factory');
$settings = $config->get(IslandoraSettingsForm::CONFIG_NAME);
$broadcastQueue = $settings->get(IslandoraSettingsForm::BROADCAST_QUEUE);
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$broadcastQueue,
$container->get('islandora.stomp'),
$container->get('jwt.authentication.jwt')
);
}
/**
* Sends a message to a broadcaster to get distributed.
*
* @param string $message
* Message body to send.
* @param array $recipients
* List of queues/topics to broadcast message to.
*/
protected function doExecute($message, array $recipients) {
// Transform recipients array into comma searated list.
$recipients = array_map('trim', $recipients);
$recipients = implode(',', $recipients);
$headers = ['IslandoraBroadcastRecipients' => $recipients];
// Include a token for later authentication in the message.
$token = $this->auth->generateToken();
if (empty($token)) {
// JWT isn't properly configured. Log and notify user.
\Drupal::logger('islandora')->error(
'Error getting JWT token for message: @msg', ['@msg' => $message]
);
drupal_set_message(
t('Error getting JWT token for message. Check JWT Configuration.'), 'error'
);
return;
}
$headers['Authorization'] = "Bearer $token";
// Transform message from string into a proper message object.
$message = new Message($message, $headers);
// Send the message.
try {
$this->stomp->begin();
$this->stomp->send($this->broadcastQueue, $message);
$this->stomp->commit();
}
catch (StompException $e) {
// Log it.
\Drupal::logger('islandora')->error(
'Error publishing message: @msg',
['@msg' => $e->getMessage()]
);
// Notify user.
drupal_set_message(
t('Error publishing message: @msg',
['@msg' => $e->getMessage()]
),
'error'
);
}
}
}

42
src/Plugin/RulesAction/CreateEventGenerator.php

@ -1,42 +0,0 @@
<?php
namespace Drupal\islandora\Plugin\RulesAction;
use Drupal\Core\Entity\EntityInterface;
use Drupal\user\UserInterface;
use Drupal\islandora\EventGenerator\EventGeneratorActionBase;
/**
* Provides an action to generate a serialized Update event.
*
* @RulesAction(
* id = "islandora_create_event_generator",
* label = @Translation("Generate Create Event"),
* category = @Translation("Islandora"),
* context = {
* "entity" = @ContextDefinition("entity",
* label = @Translation("Created entity"),
* description = @Translation("The entity that was created")
* ),
* "user" = @ContextDefinition("entity:user",
* label = @Translation("User"),
* description = @Translation("The user who created the entity")
* ),
* },
* provides = {
* "event_message" = @ContextDefinition("string",
* label = @Translation("Serialized event message")
* )
* }
* )
*/
class CreateEventGenerator extends EventGeneratorActionBase {
/**
* Provides the serialized create event to downstream actions.
*/
protected function doExecute(EntityInterface $entity, UserInterface $user) {
$this->setProvidedValue('event_message', $this->eventGenerator->generateCreateEvent($entity, $user));
}
}

42
src/Plugin/RulesAction/DeleteEventGenerator.php

@ -1,42 +0,0 @@
<?php
namespace Drupal\islandora\Plugin\RulesAction;
use Drupal\Core\Entity\EntityInterface;
use Drupal\user\UserInterface;
use Drupal\islandora\EventGenerator\EventGeneratorActionBase;
/**
* Provides an action to generate a serialized Update event.
*
* @RulesAction(
* id = "islandora_delete_event_generator",
* label = @Translation("Generate Delete Event"),
* category = @Translation("Islandora"),
* context = {
* "entity" = @ContextDefinition("entity",
* label = @Translation("Deleted entity"),
* description = @Translation("The entity that was deleted")
* ),
* "user" = @ContextDefinition("entity:user",
* label = @Translation("User"),
* description = @Translation("The user who deleted the entity")
* ),
* },
* provides = {
* "event_message" = @ContextDefinition("string",
* label = @Translation("Serialized event message")
* )
* }
* )
*/
class DeleteEventGenerator extends EventGeneratorActionBase {
/**
* Provides the serialized delete event to downstream actions.
*/
protected function doExecute(EntityInterface $entity, UserInterface $user) {
$this->setProvidedValue('event_message', $this->eventGenerator->generateDeleteEvent($entity, $user));
}
}

42
src/Plugin/RulesAction/UpdateEventGenerator.php

@ -1,42 +0,0 @@
<?php
namespace Drupal\islandora\Plugin\RulesAction;
use Drupal\Core\Entity\EntityInterface;
use Drupal\user\UserInterface;
use Drupal\islandora\EventGenerator\EventGeneratorActionBase;
/**
* Provides an action to generate a serialized Update event.
*
* @RulesAction(
* id = "islandora_update_event_generator",
* label = @Translation("Generate Update Event"),
* category = @Translation("Islandora"),
* context = {
* "entity" = @ContextDefinition("entity",
* label = @Translation("Updated entity"),
* description = @Translation("The entity that was updated")
* ),
* "user" = @ContextDefinition("entity:user",
* label = @Translation("User"),
* description = @Translation("The user who updated the entity")
* ),
* },
* provides = {
* "event_message" = @ContextDefinition("string",
* label = @Translation("Serialized event message")
* )
* }
* )
*/
class UpdateEventGenerator extends EventGeneratorActionBase {
/**
* Provides the serialized update event to downstream actions.
*/
protected function doExecute(EntityInterface $entity, UserInterface $user) {
$this->setProvidedValue('event_message', $this->eventGenerator->generateUpdateEvent($entity, $user));
}
}

88
src/PresetReaction/PresetReaction.php

@ -0,0 +1,88 @@
<?php
namespace Drupal\islandora\PresetReaction;
use Drupal\context\ContextReactionPluginBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Exposes one or more configured actions as a Context reaction.
*/
class PresetReaction extends ContextReactionPluginBase implements ContainerFactoryPluginInterface {
protected $actionStorage;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $action_storage) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->actionStorage = $action_storage;
}
/**
* {@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')->getStorage('action')
);
}
/**
* {@inheritdoc}
*/
public function summary() {
return $this->t('Perform a pre-configured action.');
}
/**
* {@inheritdoc}
*/
public function execute(EntityInterface $entity = NULL) {
$config = $this->getConfiguration();
$action_ids = $config['actions'];
foreach ($action_ids as $action_id) {
$action = $this->actionStorage->load($action_id);
$action->execute([$entity]);
}
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$actions = $this->actionStorage->loadMultiple();
foreach ($actions as $action) {
$options[ucfirst($action->getType())][$action->id()] = $action->label();
}
$config = $this->getConfiguration();
$form['actions'] = [
'#title' => $this->t('Actions'),
'#description' => $this->t('Pre-configured actions to execute. Multiple actions may be selected by shift or ctrl clicking.'),
'#type' => 'select',
'#multiple' => TRUE,
'#options' => $options,
'#default_value' => isset($config['actions']) ? $config['actions'] : '',
'#size' => 15,
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->setConfiguration(['actions' => $form_state->getValue('actions')]);
}
}

5
src/Tests/Web/IslandoraWebTestBase.php

@ -26,8 +26,9 @@ abstract class IslandoraWebTestBase extends WebTestBase {
'serialization', 'serialization',
'rest', 'rest',
'rdf', 'rdf',
'typed_data', 'action',
'rules', 'context',
'context_ui',
'jsonld', 'jsonld',
'views', 'views',
'key', 'key',

6
src/VersionCounter/VersionCounter.php

@ -33,9 +33,11 @@ class VersionCounter implements VersionCounterInterface {
*/ */
public function create($uuid) { public function create($uuid) {
$this->database->insert('islandora_version_count') $this->database->insert('islandora_version_count')
->fields([ ->fields(
[
'uuid' => $uuid, 'uuid' => $uuid,
]) ]
)
->execute(); ->execute();
} }

508
tests/src/Functional/EmitNodeEventTest.php

@ -0,0 +1,508 @@
<?php
namespace Drupal\Tests\islandora\Functional;
/**
* Tests the EmitNodeEvent action.
*
* @group islandora
*/
class EmitNodeEventTest extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Set up JWT stuff.
$key_value = <<<EOD
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA6ZT5qNjI4WlXpXzXVuo69MQ0K11V1ZmwW7JaztX0Qsi87JCi
saDIhQps2dEBND2YYKG3AehNFd/a0+ttnKPOnqr13uCVewxpgpPD4lYD0XcCD/U1
pPpOmHYrSOoVtmJvZfr5gQQb0izNM/k0wrO5r5UZzsDPX343HQuiBXzFJtIKau3n
TKjjqs5ErdnftmqsnDhI28yUtlwfSjaRVBIevIT5LGmAboWDukHxf9/x1EemvgMG
E9TQL/+JdLs+LiZglJWWeGofkcThGRcTefHe9GqxoBPtwf/rs6CKN7n3MXGfaxjl
r/dKjJ8Lg5NCrINLUFcNNZippDWIUvj/8lLBXwIDAQABAoIBABmwsOTJMw7XrzQc
TvLYQDO7gKFkWpRrmuH689Hb5kmSGnVKUxqGPIelZeNvAVrli2TVZHNpQVEulbrJ
If0gZxE8bF5fBRHLg69A4UJ7g1/+XtOyfHvwq8RI+unCFTFCEk59FAQEl6q+ErOs
rQjdC4csNvJucmBmWVlwdhl0Z5qlOX3EN/ZXCDnTJsKz75mfa8LC+izXaSv+Gesp
h80wc2V/O9H32djCuz/Ct3WLdHCTQuTiZ32fZAILk/AlZHCHjki5PaLHxAySTmo6
FmJ09/ns0EGuaa1IZz98xLn0yAfAX+MGfsWTsKzAxTO1FcMWvj23mAbwD3Q65ayv
ieMWGwECgYEA/QNKuofgfXu95H+IQMn4l/8zXPt4SdGqGxD5/SlOSi292buoJOF/
eLLlDwsHjQ3+XeFXHHgRyGxD7ZyYe5urFxYrabXlNCIidNVhQAgu31i866cs/Sy4
z0UOzVk5ZCQdvx77/Av8Xe5SBVir54KGRa6h+QMnh7DZNHM3Yha+y+8CgYEA7Fb0
hDCA2YJ6Vb6PeqRPyzsKJP4bQNP1JSM8eThk6RZ/ecAuU9uQjjUuB/O/UeEBRt4w
KUCYoyHLTraPs98N8I000SCoejLjqpyf7SOB2LjGIYPjaTTiXlqJoewWPV5KOoeN
pd+PTTTWeRSpFGjnqkSXCpa8e933raxtkLHPsZECgYBhBl4l4e1osYdElNN/ZPR7
9VWRFq4uQMTm1D/JoYlwUNI5KQl1+zOS6aeFeUlQAknFXqC1PiYzobD68c5XuH6H
v+yuAR8AOwbTnvBISdsPs0vfYqCSBhBpC6Z9gPXNPTxbClq/cSk6LCYv/q0NfrRX
DHz4rQj/tAXXY0edyfMo6QKBgGgBqF+YHMwb4IxlbSzyrG7qj39SGFpCLOroA8/w
4m+1R+ojif+7a3U5sAUt3m9BDtfKJfWxiLqZv6fnLXxh1/eZnLm/noUQaiKGBNdO
PfFK915+dRCyhkAxpcoNZIgjO5VgXBS4Oo8mhpAIaJQjynei8blmNpJoT3wtmpYH
ujgRAoGALyTXD/v/kxkE+31rmga1JM2IyjVwzefmqcdzNo3e8KovtZ79FJNfgcEx
FZTd3w207YHqKu/CX/BF15kfIOh03t+0AEUyKUTY5JWS84oQPU6td1DOSA6P36xl
EOLIc/4JOdONrJKWYpWIjDhHLL8BacjLoh2bDY0KdYa69AfYvW4=
-----END RSA PRIVATE KEY-----
EOD;
$key = $this->container->get('entity_type.manager')->getStorage('key')->create([
'id' => 'test',
'label' => 'Test',
'key_type' => 'jwt_rs',
'key_type_settings' => [
'algorithm' => 'RS256',
],
'key_provider' => 'config',
'key_provider_settings' => [
'key_value' => $key_value,
],
]);
$key->save();
$jwt_config = $this->container->get('config.factory')->getEditable('jwt.config');
$jwt_config->set('algorithm', 'RS256');
$jwt_config->set('key_id', 'test');
$jwt_config->save(TRUE);
}
/**
* @covers \Drupal\islandora\ContextProvider\NodeContextProvider::__construct
* @covers \Drupal\islandora\ContextProvider\NodeContextProvider::getRuntimeContexts
* @covers \Drupal\islandora\EventGenerator\EmitEvent::buildConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::submitConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::execute
* @covers \Drupal\islandora\EventGenerator\EventGenerator::generateCreateEvent
* @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts
* @covers \Drupal\islandora\IslandoraContextManager::applyContexts
* @covers \Drupal\islandora\Plugin\Condition\IsNode::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 testNodeCreateEvent() {
// Create a test user.
$account = $this->drupalCreateUser([
'bypass node access',
'administer contexts',
'administer actions',
]);
$this->drupalLogin($account);
// Create an action to emit a node event.
$action_id = $this->createEmitAction('node', 'create');
// Create a context and add the action as an index reaction.
$this->createContext('Test', 'test');
$this->addCondition('test', 'is_node');
$this->addPresetReaction('test', 'index', $action_id);
$this->assertSession()->statusCodeEquals(200);
// Create a new node, which publishes the create event.
$this->postNodeAddForm('test_type', ['title[0][value]' => 'Test Node'], 'Save');
// Validate the message actually gets sent.
$queue = str_replace('_', '-', $action_id);
$this->verifyMessageIsSent($queue, 'Create');
}
/**
* @covers \Drupal\islandora\ContextProvider\MediaContextProvider::__construct
* @covers \Drupal\islandora\ContextProvider\MediaContextProvider::getRuntimeContexts
* @covers \Drupal\islandora\EventGenerator\EmitEvent::buildConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::submitConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::execute
* @covers \Drupal\islandora\EventGenerator\EventGenerator::generateCreateEvent
* @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts
* @covers \Drupal\islandora\IslandoraContextManager::applyContexts
* @covers \Drupal\islandora\Plugin\Condition\IsMedia::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 testMediaCreateEvent() {
// Create a test user.
$account = $this->drupalCreateUser([
'administer contexts',
'administer actions',
'view media',
'create media',
]);
$this->drupalLogin($account);
// Create an action to emit a media event.
$action_id = $this->createEmitAction('media', 'create');
// Create a context and add the action as an index reaction.
$this->createContext('Test', 'test');
$this->addCondition('test', 'is_media');
$this->addPresetReaction('test', 'index', $action_id);
$this->assertSession()->statusCodeEquals(200);
// Create a new media, which publishes the create event.
$this->createThumbnailWithFile();
// Validate the message actually gets sent.
$queue = str_replace('_', '-', $action_id);
$this->verifyMessageIsSent($queue, 'Create');
}
/**
* @covers \Drupal\islandora\ContextProvider\FileContextProvider::__construct
* @covers \Drupal\islandora\ContextProvider\FileContextProvider::getRuntimeContexts
* @covers \Drupal\islandora\EventGenerator\EmitEvent::buildConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::submitConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::execute
* @covers \Drupal\islandora\EventGenerator\EventGenerator::generateCreateEvent
* @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts
* @covers \Drupal\islandora\IslandoraContextManager::applyContexts
* @covers \Drupal\islandora\Plugin\Condition\IsFile::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 testFileCreateEvent() {
// Create a test user.
$account = $this->drupalCreateUser([
'administer contexts',
'administer actions',
'view media',
'create media',
]);
$this->drupalLogin($account);
// Create an action to emit a media event.
$action_id = $this->createEmitAction('file', 'create');
// Create a context and add the action as an index reaction.
$this->createContext('Test', 'test');
$this->addCondition('test', 'is_file');
$this->addPresetReaction('test', 'index', $action_id);
$this->assertSession()->statusCodeEquals(200);
// Create a new file, which publishes the create event.
$this->createThumbnailWithFile();
// Validate the message actually gets sent.
$queue = str_replace('_', '-', $action_id);
$this->verifyMessageIsSent($queue, 'Create');
}
/**
* @covers \Drupal\islandora\ContextProvider\NodeContextProvider::__construct
* @covers \Drupal\islandora\ContextProvider\NodeContextProvider::getRuntimeContexts
* @covers \Drupal\islandora\EventGenerator\EmitEvent::buildConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::submitConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::execute
* @covers \Drupal\islandora\EventGenerator\EventGenerator::generateUpdateEvent
* @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts
* @covers \Drupal\islandora\IslandoraContextManager::applyContexts
* @covers \Drupal\islandora\Plugin\Condition\IsNode::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 testNodeUpdateEvent() {
// Create a test user.
$account = $this->drupalCreateUser([
'bypass node access',
'administer contexts',
'administer actions',
]);
$this->drupalLogin($account);
// Create an action to emit a node event.
$action_id = $this->createEmitAction('node', 'update');
// Create a context and add the action as an index reaction.
$this->createContext('Test', 'test');
$this->addCondition('test', 'is_node');
$this->addPresetReaction('test', 'index', $action_id);
$this->assertSession()->statusCodeEquals(200);
// Create a new node, which publishes the update event.
$this->postNodeAddForm('test_type', ['title[0][value]' => 'Test Node'], 'Save');
$queue = str_replace('_', '-', $action_id);
$this->verifyMessageIsSent($queue, 'Update');
}
/**
* @covers \Drupal\islandora\ContextProvider\MediaContextProvider::__construct
* @covers \Drupal\islandora\ContextProvider\MediaContextProvider::getRuntimeContexts
* @covers \Drupal\islandora\EventGenerator\EmitEvent::buildConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::submitConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::execute
* @covers \Drupal\islandora\EventGenerator\EventGenerator::generateUpdateEvent
* @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts
* @covers \Drupal\islandora\IslandoraContextManager::applyContexts
* @covers \Drupal\islandora\Plugin\Condition\IsMedia::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 testMediaUpdateEvent() {
// Create a test user.
$account = $this->drupalCreateUser([
'administer contexts',
'administer actions',
'view media',
'create media',
]);
$this->drupalLogin($account);
// Create an action to emit a media event.
$action_id = $this->createEmitAction('media', 'update');
// Create a context and add the action as an index reaction.
$this->createContext('Test', 'test');
$this->addCondition('test', 'is_media');
$this->addPresetReaction('test', 'index', $action_id);
$this->assertSession()->statusCodeEquals(200);
// Create a new media, which publishes the update event.
$this->createThumbnailWithFile();
// Validate the message actually gets sent.
$queue = str_replace('_', '-', $action_id);
$this->verifyMessageIsSent($queue, 'Update');
}
/**
* @covers \Drupal\islandora\ContextProvider\FileContextProvider::__construct
* @covers \Drupal\islandora\ContextProvider\FileContextProvider::getRuntimeContexts
* @covers \Drupal\islandora\EventGenerator\EmitEvent::buildConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::submitConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::execute
* @covers \Drupal\islandora\EventGenerator\EventGenerator::generateUpdateEvent
* @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts
* @covers \Drupal\islandora\IslandoraContextManager::applyContexts
* @covers \Drupal\islandora\Plugin\Condition\IsFile::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 testFileUpdateEvent() {
// Create a test user.
$account = $this->drupalCreateUser([
'administer contexts',
'administer actions',
'view media',
'create media',
]);
$this->drupalLogin($account);
// Create an action to emit a media event.
$action_id = $this->createEmitAction('file', 'update');
// Create a context and add the action as an index reaction.
$this->createContext('Test', 'test');
$this->addCondition('test', 'is_file');
$this->addPresetReaction('test', 'index', $action_id);
$this->assertSession()->statusCodeEquals(200);
// Create a new file, which publishes the update event.
$this->createThumbnailWithFile();
// Validate the message actually gets sent.
$queue = str_replace('_', '-', $action_id);
$this->verifyMessageIsSent($queue, 'Update');
}
/**
* @covers \Drupal\islandora\ContextProvider\NodeContextProvider::__construct
* @covers \Drupal\islandora\ContextProvider\NodeContextProvider::getRuntimeContexts
* @covers \Drupal\islandora\EventGenerator\EmitEvent::buildConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::submitConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::execute
* @covers \Drupal\islandora\EventGenerator\EventGenerator::generateDeleteEvent
* @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts
* @covers \Drupal\islandora\IslandoraContextManager::applyContexts
* @covers \Drupal\islandora\Plugin\Condition\IsNode::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 testNodeDeleteEvent() {
// Create a test user.
$account = $this->drupalCreateUser([
'bypass node access',
'administer contexts',
'administer actions',
]);
$this->drupalLogin($account);
// Create an action to emit a node event.
$action_id = $this->createEmitAction('node', 'delete');
// Create a context and add the action as an index reaction.
$this->createContext('Test', 'test');
$this->addCondition('test', 'is_node');
$this->addPresetReaction('test', 'index', $action_id);
$this->assertSession()->statusCodeEquals(200);
// Create a new node, which publishes the delete event (lol).
$this->postNodeAddForm('test_type', ['title[0][value]' => 'Test Node'], 'Save');
$queue = str_replace('_', '-', $action_id);
$this->verifyMessageIsSent($queue, 'Delete');
}
/**
* @covers \Drupal\islandora\ContextProvider\MediaContextProvider::__construct
* @covers \Drupal\islandora\ContextProvider\MediaContextProvider::getRuntimeContexts
* @covers \Drupal\islandora\EventGenerator\EmitEvent::buildConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::submitConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::execute
* @covers \Drupal\islandora\EventGenerator\EventGenerator::generateDeleteEvent
* @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts
* @covers \Drupal\islandora\IslandoraContextManager::applyContexts
* @covers \Drupal\islandora\Plugin\Condition\IsMedia::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 testMediaDeleteEvent() {
// Create a test user.
$account = $this->drupalCreateUser([
'administer contexts',
'administer actions',
'view media',
'create media',
]);
$this->drupalLogin($account);
// Create an action to emit a media event.
$action_id = $this->createEmitAction('media', 'delete');
// Create a context and add the action as an index reaction.
$this->createContext('Test', 'test');
$this->addCondition('test', 'is_media');
$this->addPresetReaction('test', 'index', $action_id);
$this->assertSession()->statusCodeEquals(200);
// Create a new media, which publishes the delete event.
$this->createThumbnailWithFile();
// Validate the message actually gets sent.
$queue = str_replace('_', '-', $action_id);
$this->verifyMessageIsSent($queue, 'Delete');
}
/**
* @covers \Drupal\islandora\ContextProvider\FileContextProvider::__construct
* @covers \Drupal\islandora\ContextProvider\FileContextProvider::getRuntimeContexts
* @covers \Drupal\islandora\EventGenerator\EmitEvent::buildConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::submitConfigurationForm
* @covers \Drupal\islandora\EventGenerator\EmitEvent::execute
* @covers \Drupal\islandora\EventGenerator\EventGenerator::generateDeleteEvent
* @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts
* @covers \Drupal\islandora\IslandoraContextManager::applyContexts
* @covers \Drupal\islandora\Plugin\Condition\IsFile::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 testFileDeleteEvent() {
// Create a test user.
$account = $this->drupalCreateUser([
'administer contexts',
'administer actions',
'view media',
'create media',
]);
$this->drupalLogin($account);
// Create an action to emit a media event.
$action_id = $this->createEmitAction('file', 'delete');
// Create a context and add the action as an index reaction.
$this->createContext('Test', 'test');
$this->addCondition('test', 'is_file');
$this->addPresetReaction('test', 'index', $action_id);
$this->assertSession()->statusCodeEquals(200);
// Create a new file, which publishes the delete event.
$this->createThumbnailWithFile();
// Validate the message actually gets sent.
$queue = str_replace('_', '-', $action_id);
$this->verifyMessageIsSent($queue, 'Delete');
}
/**
* Utility function to create an emit action.
*
* @param string $entity_type
* Entity type id.
* @param string $event_type
* Event type (create, update, or delete).
*/
protected function createEmitAction($entity_type, $event_type) {
$this->drupalGet('admin/config/system/actions');
$this->getSession()->getPage()->findById("edit-action")->selectOption("Emit a $entity_type event to a queue/topic...");
$this->getSession()->getPage()->pressButton(t('Create'));
$this->assertSession()->statusCodeEquals(200);
$action_id = "emit_" . $entity_type . "_" . $event_type;
$this->getSession()->getPage()->fillField('edit-label', "Emit $entity_type $event_type");
$this->getSession()->getPage()->fillField('edit-id', $action_id);
$this->getSession()->getPage()->fillField('edit-queue', "emit-$entity_type-$event_type");
$this->getSession()->getPage()->findById("edit-event")->selectOption($event_type);
$this->getSession()->getPage()->pressButton(t('Save'));
$this->assertSession()->statusCodeEquals(200);
return $action_id;
}
/**
* Asserts the message was delivered and checks its general shape.
*
* @param string $queue
* The queue to check for the message.
* @param string $event_type
* Event type (create, update, or delete).
*/
protected function verifyMessageIsSent($queue, $event_type) {
// Verify message is sent.
$stomp = $this->container->get('islandora.stomp');
try {
$stomp->subscribe($queue);
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 == $event_type, "Expected $event_type, received $type");
}
$stomp->unsubscribe();
}
catch (StompException $e) {
$this->assertTrue(FALSE, "There was an error connecting to the stomp broker");
}
}
}

75
tests/src/Functional/IndexingTest.php

@ -0,0 +1,75 @@
<?php
namespace Drupal\Tests\islandora\Functional;
/**
* Tests indexing and de-indexing in hooks with pre-configured actions.
*
* @group islandora
*/
class IndexingTest extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Create an action that dsm's "Goodbye, Cruel World!".
$goodbye_world = $this->container->get('entity_type.manager')->getStorage('action')->create([
'id' => 'goodbye_world',
'label' => 'Goodbye World',
'type' => 'system',
'plugin' => 'action_message_action',
'configuration' => [
'message' => 'Goodbye, Cruel World!',
],
]);
$goodbye_world->save();
}
/**
* @covers \Drupal\islandora\ContextProvider\NodeContextProvider::__construct
* @covers \Drupal\islandora\ContextProvider\NodeContextProvider::getRuntimeContexts
* @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts
* @covers \Drupal\islandora\IslandoraContextManager::applyContexts
* @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 testIndexing() {
// Create a test user.
$account = $this->drupalCreateUser([
'bypass node access',
'administer contexts',
'administer actions',
]);
$this->drupalLogin($account);
$this->createContext('Test', 'test');
$this->addPresetReaction('test', 'index', 'hello_world');
// Create a new node and confirm Hello World! is printed to the screen.
$this->postNodeAddForm('test_type', ['title[0][value]' => 'Test Node'], 'Save');
$this->assertSession()->pageTextContains("Hello World!");
// Stash the node's url.
$url = $this->getUrl();
// Edit the node and confirm Hello World! is printed to the screen.
$this->postEntityEditForm($url, ['title[0][value]' => 'Test Node Changed'], 'Save');
$this->assertSession()->pageTextContains("Hello World!");
// Add the Goodbye World reaction.
$this->addPresetReaction('test', 'delete', 'goodbye_world');
// Delete the node.
$this->drupalPostForm("$url/delete", [], t('Delete'));
$this->assertSession()->statusCodeEquals(200);
// Confirm Goodbye, Cruel World! is printed to the screen.
$this->assertSession()->pageTextContains("Goodbye, Cruel World!");
}
}

58
tests/src/Functional/IsFileTest.php

@ -0,0 +1,58 @@
<?php
namespace Drupal\Tests\islandora\Functional;
/**
* Tests the IsFile condition.
*
* @group islandora
*/
class IsFileTest extends IslandoraFunctionalTestBase {
/**
* @covers \Drupal\islandora\ContextProvider\FileContextProvider::__construct
* @covers \Drupal\islandora\ContextProvider\FileContextProvider::getRuntimeContexts
* @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\IsFile::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 testIsFile() {
// Create a test user.
$account = $this->drupalCreateUser([
'administer contexts',
'view media',
'create media',
'update media',
]);
$this->drupalLogin($account);
// Set it up.
$this->createContext('Test', 'test');
$this->addCondition('test', 'is_file');
$this->addPresetReaction('test', 'index', 'hello_world');
// Add a new Thumbnail media and confirm Hello World! is printed to the
// screen for the file upload.
$this->createThumbnailWithFile();
$this->assertSession()->pageTextContains("Hello World!");
// Stash the media's url.
$url = $this->getUrl();
// Edit the media, not touching the file this time.
$values = [
'name[0][value]' => 'Test Media Changed',
];
$this->postEntityEditForm($url, $values, 'Save and keep published');
// Confirm Hello World! is not printed to the screen.
$this->assertSession()->pageTextNotContains("Hello World!");
}
}

52
tests/src/Functional/IsMediaTest.php

@ -0,0 +1,52 @@
<?php
namespace Drupal\Tests\islandora\Functional;
/**
* Tests the IsMedia condition.
*
* @group islandora
*/
class IsMediaTest extends IslandoraFunctionalTestBase {
/**
* @covers \Drupal\islandora\ContextProvider\NodeContextProvider::__construct
* @covers \Drupal\islandora\ContextProvider\NodeContextProvider::getRuntimeContexts
* @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\IsMedia::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 testIsMedia() {
// Create a test user.
$account = $this->drupalCreateUser([
'bypass node access',
'administer contexts',
'view media',
'create media',
'update media',
]);
$this->drupalLogin($account);
$this->createContext('Test', 'test');
$this->addCondition('test', 'is_media');
$this->addPresetReaction('test', 'index', 'hello_world');
// Add a new Thumbnail media and confirm Hello World! is printed to the
// screen.
$this->createThumbnailWithFile();
$this->assertSession()->pageTextContains("Hello World!");
// Create a new node.
$this->postNodeAddForm('test_type', ['title[0][value]' => 'Test Node'], 'Save');
// Confirm Hello World! is not printed to the screen.
$this->assertSession()->pageTextNotContains("Hello World!");
}
}

50
tests/src/Functional/IsNodeTest.php

@ -0,0 +1,50 @@
<?php
namespace Drupal\Tests\islandora\Functional;
/**
* Tests the IsNode condition.
*
* @group islandora
*/
class IsNodeTest extends IslandoraFunctionalTestBase {
/**
* @covers \Drupal\islandora\ContextProvider\NodeContextProvider::__construct
* @covers \Drupal\islandora\ContextProvider\NodeContextProvider::getRuntimeContexts
* @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\IsNode::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 testIsNode() {
// Create a test user.
$account = $this->drupalCreateUser([
'bypass node access',
'administer contexts',
'view media',
'create media',
'update media',
]);
$this->drupalLogin($account);
$this->createContext('Test', 'test');
$this->addCondition('test', 'is_node');
$this->addPresetReaction('test', 'index', 'hello_world');
// Create a new node confirm Hello World! is printed to the screen.
$this->postNodeAddForm('test_type', ['title[0][value]' => 'Test Node'], 'Save');
$this->assertSession()->pageTextContains("Hello World!");
// Add a new Thumbnail media and confirm Hello World! is not printed to the
// screen.
$this->createThumbnailWithFile();
$this->assertSession()->pageTextNotContains("Hello World!");
}
}

123
tests/src/Functional/IslandoraFunctionalTestBase.php

@ -0,0 +1,123 @@
<?php
namespace Drupal\Tests\islandora\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\TestFileCreationTrait;
/**
* Base class for Functional tests.
*/
class IslandoraFunctionalTestBase extends BrowserTestBase {
use TestFileCreationTrait;
protected static $modules = ['context_ui', 'islandora'];
protected static $configSchemaCheckerExclusions = [
'jwt.config',
'context.context.test',
'context.context.node',
'context.context.media',
'context.context.file',
'key.key.test',
];
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Delete the context entities provided by the module.
// This will get removed as we split apart contexts into different
// solution packs.
$this->container->get('entity_type.manager')->getStorage('context')->load('node')->delete();
$this->container->get('entity_type.manager')->getStorage('context')->load('media')->delete();
$this->container->get('entity_type.manager')->getStorage('context')->load('file')->delete();
// Create a test content type.
$test_type = $this->container->get('entity_type.manager')->getStorage('node_type')->create([
'type' => 'test_type',
'label' => 'Test Type',
]);
$test_type->save();
// Create an action that dsm's "Hello World!".
$hello_world = $this->container->get('entity_type.manager')->getStorage('action')->create([
'id' => 'hello_world',
'label' => 'Hello World',
'type' => 'system',
'plugin' => 'action_message_action',
'configuration' => [
'message' => 'Hello World!',
],
]);
$hello_world->save();
}
/**
* Creates a test context.
*/
protected function createContext($label, $name) {
$this->drupalPostForm('admin/structure/context/add', ['label' => $label, 'name' => $name], t('Save'));
$this->assertSession()->statusCodeEquals(200);
}
/**
* Adds a condition to the test context.
*/
protected function addCondition($context_id, $condition_id) {
$this->drupalGet("admin/structure/context/$context_id/condition/add/$condition_id");
$this->getSession()->getPage()->pressButton('Save and continue');
$this->assertSession()->statusCodeEquals(200);
}
/**
* Adds a reaction to the test context.
*/
protected function addPresetReaction($context_id, $reaction_type, $action_id) {
$this->drupalGet("admin/structure/context/$context_id/reaction/add/$reaction_type");
$this->getSession()->getPage()->findById("edit-reactions-$reaction_type-actions")->selectOption($action_id);
$this->getSession()->getPage()->pressButton(t('Save and continue'));
$this->assertSession()->statusCodeEquals(200);
}
/**
* Creates a new TN media with a file.
*/
protected function createThumbnailWithFile() {
// Have to do this in two steps since there's no ajax for the alt text.
// It's as annoying as in real life.
$file = current($this->getTestFiles('image'));
$values = [
'name[0][value]' => 'Test Media',
'files[field_image_0]' => drupal_realpath($file->uri),
];
$this->drupalPostForm('media/add/tn', $values, t('Save and publish'));
$values = [
'field_image[0][alt]' => 'Alternate text',
];
$this->getSession()->getPage()->fillField('edit-field-image-0-alt', 'alt text');
$this->getSession()->getPage()->pressButton(t('Save and publish'));
$this->assertResponse(200);
}
/**
* Create a new node by posting its add form.
*/
protected function postNodeAddForm($bundle_id, $values, $button_text) {
$this->drupalPostForm("node/add/$bundle_id", $values, t('@text', ['@text' => $button_text]));
$this->assertResponse(200);
}
/**
* Edits a node by posting its edit form.
*/
protected function postEntityEditForm($entity_url, $values, $button_text) {
$this->drupalPostForm("$entity_url/edit", $values, t('@text', ['@text' => $button_text]));
$this->assertResponse(200);
}
}

138
tests/src/Kernel/BroadcasterTest.php

@ -1,138 +0,0 @@
<?php
namespace Drupal\Tests\islandora\Kernel;
use Stomp\Exception\StompException;
use Stomp\StatefulStomp;
use Drupal\islandora\Plugin\RulesAction\Broadcaster;
use Drupal\jwt\Authentication\Provider\JwtAuth;
/**
* Broadcaster tests.
*
* @group islandora
* @coversDefaultClass \Drupal\islandora\Plugin\RulesAction\Broadcaster
*/
class BroadcasterTest extends IslandoraKernelTestBase {
protected $testQueue = 'islandora-broadcaster-test-queue';
/**
* Tests that the action does not WSOD Drupal when there's a StompException.
*
* @covers \Drupal\islandora\Plugin\RulesAction\Broadcaster::execute
*/
public function testExecuteSquashesStompExceptions() {
// Set up a fake Stomp client to throw a StompException when used.
$prophecy = $this->prophesize(StatefulStomp::CLASS);
$prophecy->begin()->willThrow(new StompException('STOMP EXCEPTION'));
$stomp = $prophecy->reveal();
$action = $this->createBroadcaster($stomp);
try {
// Execute the action.
$action->execute();
$this->assertTrue(TRUE, "The execute() method must squash StompExceptions.");
}
catch (\Exception $e) {
$this->assertTrue(FALSE, "The execute() method must squash StompExceptions.");
}
}
/**
* Tests that the action DOES NOT squash any other Exception.
*
* @covers \Drupal\islandora\Plugin\RulesAction\Broadcaster::execute
* @expectedException \Exception
*/
public function testExecuteDoesNotSquashOtherExceptions() {
// Set up a fake Stomp client to throw a non-StompException when used.
$prophecy = $this->prophesize(StatefulStomp::CLASS);
$prophecy->begin()->willThrow(new \Exception('NOT A STOMP EXCEPTION'));
$stomp = $prophecy->reveal();
$action = $this->createBroadcaster($stomp);
// This should throw an exception.
$action->execute();
}
/**
* Tests that the action publishes the message to be broadcast to a broker.
*
* @covers \Drupal\islandora\Plugin\RulesAction\Broadcaster::execute
*/
public function testBrokerIntegration() {
// Grab a legit stomp client, using values from config.
$this->installConfig('islandora');
$stomp = $this->container->get('islandora.stomp');
// Create and execute the action.
$action = $this->createBroadcaster($stomp);
$action->execute();
// Verify the message actually got sent.
try {
$stomp->subscribe($this->testQueue);
$msg = $stomp->read();
$this->assertTrue(
strcmp($msg->getBody(), 'test') == 0,
"Message body is not 'test'"
);
$headers = $msg->getHeaders();
$this->assertTrue(
strcmp($headers['IslandoraBroadcastRecipients'], 'activemq:queue:foo,activemq:queue:bar') == 0,
"IslandoraBroadcastRecipients header must be a comma separated list of endpoints"
);
$this->assertTrue(
strcmp($headers['Authorization'], 'Bearer some_token') == 0,
"Authorization header must be set"
);
$stomp->unsubscribe();
}
catch (StompException $e) {
$this->assertTrue(FALSE, "There was an error connecting to the stomp broker");
}
}
/**
* Utility function to create a broadcaster action from a Stomp prophecy.
*
* @param \Stomp\StatefulStomp $stomp
* Stomp instance or prophecy.
*
* @return \Drupal\islandora\Plugin\RulesAction\Broadcaster
* Broadcaster, ready to test.
*/
protected function createBroadcaster(StatefulStomp $stomp) {
// Pull the plugin definition out of the plugin system.
$actionManager = $this->container->get('plugin.manager.rules_action');
$definitions = $actionManager->getDefinitions();
$pluginDefinition = $definitions['islandora_broadcast'];
// Mock a JWT generator.
$prophecy = $this->prophesize(JwtAuth::class);
$prophecy->generateToken()->willReturn("some_token");
$jwt = $prophecy->reveal();
$action = new Broadcaster(
[],
'islandora_broadcast',
$pluginDefinition,
$this->testQueue,
$stomp,
$jwt
);
// Set the required contexts for the action to run.
$action->setContextValue('message', "test");
$action->setContextValue('recipients', ['activemq:queue:foo', 'activemq:queue:bar']);
return $action;
}
}

36
tests/src/Kernel/CreateEventGeneratorTest.php

@ -1,36 +0,0 @@
<?php
namespace Drupal\Tests\islandora\Kernel;
/**
* Tests the basic behavior of the CreateEventGenerator rules action.
*
* @group islandora
* @coversDefaultClass \Drupal\islandora\Plugin\RulesAction\CreateEventGenerator
*/
class CreateEventGeneratorTest extends EventGeneratorActionTestBase {
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Get an instance of the action.
$this->action = $this->actionManager->createInstance('islandora_create_event_generator');
}
/**
* Tests the CreateEventGenerator action.
*
* @covers \Drupal\islandora\Plugin\RulesAction\CreateEventGenerator::execute
*/
public function testExecute() {
// Execute the action and get the message it outputs.
$msg = $this->assertExecution();
// Assert it's outputs a 'Create' event.
$this->assertTrue($msg["type"] == "Create", "Event must be of type 'Create'.");
}
}

36
tests/src/Kernel/DeleteEventGeneratorTest.php

@ -1,36 +0,0 @@
<?php
namespace Drupal\Tests\islandora\Kernel;
/**
* Tests the basic behavior of the DeleteEventGenerator rules action.
*
* @group islandora
* @coversDefaultClass \Drupal\islandora\Plugin\RulesAction\DeleteEventGenerator
*/
class DeleteEventGeneratorTest extends EventGeneratorActionTestBase {
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Get an instance of the action.
$this->action = $this->actionManager->createInstance('islandora_delete_event_generator');
}
/**
* Tests the DeleteEventGenerator action.
*
* @covers \Drupal\islandora\Plugin\RulesAction\DeleteEventGenerator::execute
*/
public function testExecute() {
// Execute the action and get the message it outputs.
$msg = $this->assertExecution();
// Assert it's outputs a 'Delete' event.
$this->assertTrue($msg["type"] == "Delete", "Event must be of type 'Delete'.");
}
}

59
tests/src/Kernel/EventGeneratorActionTestBase.php

@ -1,59 +0,0 @@
<?php
namespace Drupal\Tests\islandora\Kernel;
/**
* Base class for testing EventGenerator functionality.
*/
abstract class EventGeneratorActionTestBase extends EventGeneratorTestBase {
/**
* Action plugin manager.
*
* @var \Drupal\rules\Core\RulesActionManagerInterface
*/
protected $actionManager;
/**
* Action to test.
*
* @var Drupal\rules\Core\RulesActionInterface
*/
protected $action;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Get the action manager.
$this->actionManager = $this->container->get('plugin.manager.rules_action');
}
/**
* Utility function to bootstrap an action, run it, and do basic asserts.
*
* @return array
* The event message, as an array.
*/
protected function assertExecution() {
// Set the required contexts for the action to run.
$this->action->setContextValue('entity', $this->entity);
$this->action->setContextValue('user', $this->user);
// Execute the action.
$this->action->execute();
// Assert some basics.
$message_str = $this->action->getProvidedContext('event_message')->getContextValue();
$this->assertNotEmpty($message_str, "Event message must not be empty.");
$message = json_decode($message_str, TRUE);
$this->assertTrue(array_key_exists('type', $message), "Event has type key.");
// Return the event message.
return $message;
}
}

40
tests/src/Kernel/EventGeneratorTest.php

@ -3,6 +3,9 @@
namespace Drupal\Tests\islandora\Kernel; namespace Drupal\Tests\islandora\Kernel;
use Drupal\islandora\EventGenerator\EventGenerator; use Drupal\islandora\EventGenerator\EventGenerator;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\simpletest\UserCreationTrait;
/** /**
* Tests the EventGenerator default implementation. * Tests the EventGenerator default implementation.
@ -10,7 +13,9 @@ use Drupal\islandora\EventGenerator\EventGenerator;
* @group islandora * @group islandora
* @coversDefaultClass \Drupal\islandora\EventGenerator\EventGenerator * @coversDefaultClass \Drupal\islandora\EventGenerator\EventGenerator
*/ */
class EventGeneratorTest extends EventGeneratorTestBase { class EventGeneratorTest extends IslandoraKernelTestBase {
use UserCreationTrait;
/** /**
* The EventGenerator to test. * The EventGenerator to test.
@ -19,12 +24,45 @@ class EventGeneratorTest extends EventGeneratorTestBase {
*/ */
protected $eventGenerator; protected $eventGenerator;
/**
* User entity.
*
* @var \Drupal\user\UserInterface
*/
protected $user;
/**
* Fedora resource entity.
*
* @var \Drupal\node\Entity\NodeInterface
*/
protected $entity;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setUp() { public function setUp() {
parent::setUp(); parent::setUp();
// Create a test user.
$this->user = $this->createUser(['administer nodes']);
$test_type = NodeType::create([
'type' => 'test_type',
'label' => 'Test Type',
]);
$test_type->save();
// Create a test entity.
$this->entity = Node::create([
"type" => "test_type",
"uid" => $this->user->get('uid'),
"title" => "Test Fixture",
"langcode" => "und",
"status" => 1,
]);
$this->entity->save();
// Create the event generator so we can test it. // Create the event generator so we can test it.
$this->eventGenerator = new EventGenerator(); $this->eventGenerator = new EventGenerator();
} }

56
tests/src/Kernel/EventGeneratorTestBase.php

@ -1,56 +0,0 @@
<?php
namespace Drupal\Tests\islandora\Kernel;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\simpletest\UserCreationTrait;
/**
* Base class for testing EventGenerator functionality.
*/
abstract class EventGeneratorTestBase extends IslandoraKernelTestBase {
use UserCreationTrait;
/**
* User entity.
*
* @var \Drupal\user\UserInterface
*/
protected $user;
/**
* Fedora resource entity.
*
* @var \Drupal\node\Entity\NodeInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Create a test user.
$this->user = $this->createUser(['administer nodes']);
$test_type = NodeType::create([
'type' => 'test_type',
'label' => 'Test Type',
]);
$test_type->save();
// Create a test entity.
$this->entity = Node::create([
"type" => "test_type",
"uid" => $this->user->get('uid'),
"title" => "Test Fixture",
"langcode" => "und",
"status" => 1,
]);
$this->entity->save();
}
}

6
tests/src/Kernel/IslandoraKernelTestBase.php

@ -28,8 +28,8 @@ abstract class IslandoraKernelTestBase extends KernelTestBase {
'basic_auth', 'basic_auth',
'hal', 'hal',
'rdf', 'rdf',
'typed_data', 'action',
'rules', 'context',
'jsonld', 'jsonld',
'views', 'views',
'key', 'key',
@ -53,8 +53,8 @@ abstract class IslandoraKernelTestBase extends KernelTestBase {
$this->installSchema('node', 'node_access'); $this->installSchema('node', 'node_access');
$this->installEntitySchema('user'); $this->installEntitySchema('user');
$this->installEntitySchema('node'); $this->installEntitySchema('node');
$this->installEntitySchema('context');
$this->installConfig('filter'); $this->installConfig('filter');
$this->installSchema('islandora', 'islandora_version_count');
} }
} }

36
tests/src/Kernel/UpdateEventGeneratorTest.php

@ -1,36 +0,0 @@
<?php
namespace Drupal\Tests\islandora\Kernel;
/**
* Tests the basic behavior of the UpdateEventGenerator rules action.
*
* @group islandora
* @coversDefaultClass \Drupal\islandora\Plugin\RulesAction\UpdateEventGenerator
*/
class UpdateEventGeneratorTest extends EventGeneratorActionTestBase {
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Get an instance of the action.
$this->action = $this->actionManager->createInstance('islandora_update_event_generator');
}
/**
* Tests the UpdateEventGenerator action.
*
* @covers \Drupal\islandora\Plugin\RulesAction\UpdateEventGenerator::execute
*/
public function testExecute() {
// Execute the action and get the message it outputs.
$msg = $this->assertExecution();
// Assert it's outputs a 'Update' event.
$this->assertTrue($msg["type"] == "Update", "Event must be of type 'Update'.");
}
}

109
tests/src/Kernel/VersionCounterTest.php

@ -1,109 +0,0 @@
<?php
namespace Drupal\Tests\islandora\Kernel;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\simpletest\UserCreationTrait;
/**
* Tests the basic behavior of a vector clock.
*
* @group islandora
* @coversDefaultClass \Drupal\islandora\VersionCounter\VersionCounter
*/
class VersionCounterTest extends IslandoraKernelTestBase {
use UserCreationTrait;
/**
* Fedora resource entity.
*
* @var \Drupal\node\Entity\NodeInterface
*/
protected $entity;
/**
* User entity.
*
* @var \Drupal\user\UserInterface
*/
protected $user;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Create a test user.
$this->user = $this->createUser(['administer nodes']);
$test_type = NodeType::create([
'type' => 'test_type',
'label' => 'Test Type',
]);
$test_type->save();
// Create a test entity.
$this->entity = Node::create([
"type" => "test_type",
"uid" => $this->user->get('uid'),
"title" => "Test Fixture",
"langcode" => "und",
"status" => 1,
]);
$this->entity->save();
}
/**
* @covers \Drupal\islandora\VersionCounter\VersionCounter::create
* @covers \Drupal\islandora\VersionCounter\VersionCounter::get
*/
public function testInitializesRecord() {
$versionCounter = $this->container->get('islandora.versioncounter');
$this->assertTrue($versionCounter->get($this->entity->uuid()) == 0,
"Version counter db record must be initialized to 0."
);
}
/**
* @covers \Drupal\islandora\VersionCounter\VersionCounter::create
* @expectedException \Drupal\Core\Database\IntegrityConstraintViolationException
*/
public function testCannotCreateDuplicateRecord() {
$versionCounter = $this->container->get('islandora.versioncounter');
$versionCounter->create($this->entity->uuid());
}
/**
* @covers \Drupal\islandora\VersionCounter\VersionCounter::increment
* @covers \Drupal\islandora\VersionCounter\VersionCounter::get
*/
public function testRecordIncrementsOnUpdate() {
$this->entity->setTitle("New Title");
$this->entity->save();
$versionCounter = $this->container->get('islandora.versioncounter');
$this->assertTrue($versionCounter->get($this->entity->uuid()) == 1,
"Version counter db record must increment on entity update."
);
}
/**
* @covers \Drupal\islandora\VersionCounter\VersionCounter::delete
* @covers \Drupal\islandora\VersionCounter\VersionCounter::get
*/
public function testRecordsGetCleanedUp() {
$this->entity->delete();
$versionCounter = $this->container->get('islandora.versioncounter');
$this->assertTrue($versionCounter->get($this->entity->uuid()) == -1,
"Version counter db record must be deleted on entity delete."
);
}
}
Loading…
Cancel
Save