Browse Source

Flysystem (#92)

* Flysystem adapter and REST API accepts Content-Location with a stream wrapper

* First pass is working now

* Config dump

* typo in composer.json

* Testing install

* Tests.  Coding standards

* Adding token as a depenedency

* Unneccessary config

* Adding drupal/token to composer.json

* Applying the file's uri using the mapping predicate alter.

* Tests vs coding standards whack-a-mole

* Sneaky image resize bug

* Test refactor to quiesce phpcpd.  How you like that @bradspry?

* _slightly_ better error reporting for tests

* Moar test updates

* phpcbf to the rescue

* Config export

* Fixing the Canoncial typo once and for all

* Better trimming

* Collapsing 'Is' style conditions into a singular 'ContentEntityType' condition

* Coding standards

* Was referencing is_node in a test

* Unbreaking core block placement

* Copy pasta fix and removing conditions I obliterated from the schema file.
pull/756/head
dannylamb 6 years ago committed by Jared Whiklo
parent
commit
cb8bb07238
  1. 5
      composer.json
  2. 40
      config/schema/islandora.schema.yml
  3. 2
      islandora.info.yml
  4. 4
      islandora.services.yml
  5. 19
      modules/islandora_core_feature/config/install/core.base_field_override.media.audio.name.yml
  6. 19
      modules/islandora_core_feature/config/install/core.base_field_override.media.file.name.yml
  7. 19
      modules/islandora_core_feature/config/install/core.base_field_override.media.image.name.yml
  8. 19
      modules/islandora_core_feature/config/install/core.base_field_override.media.video.name.yml
  9. 4
      modules/islandora_core_feature/config/install/field.storage.media.field_media_audio_file.yml
  10. 4
      modules/islandora_core_feature/config/install/field.storage.media.field_media_file.yml
  11. 4
      modules/islandora_core_feature/config/install/field.storage.media.field_media_image.yml
  12. 4
      modules/islandora_core_feature/config/install/field.storage.media.field_media_video_file.yml
  13. 2
      modules/islandora_core_feature/config/install/field.storage.media.field_mime_type.yml
  14. 5
      modules/islandora_core_feature/config/install/filehash.settings.yml
  15. 2
      modules/islandora_core_feature/config/install/language.content_settings.media.audio.yml
  16. 2
      modules/islandora_core_feature/config/install/language.content_settings.media.file.yml
  17. 2
      modules/islandora_core_feature/config/install/language.content_settings.media.image.yml
  18. 2
      modules/islandora_core_feature/config/install/language.content_settings.media.video.yml
  19. 2
      modules/islandora_core_feature/config/install/language.content_settings.taxonomy_term.tags.yml
  20. 6
      modules/islandora_core_feature/config/install/system.action.delete_file_from_fedora.yml
  21. 4
      modules/islandora_core_feature/config/install/system.action.index_file_in_fedora.yml
  22. 1
      modules/islandora_core_feature/config/install/views.view.file_checksum.yml
  23. 1
      modules/islandora_core_feature/islandora_core_feature.info.yml
  24. 46
      modules/islandora_demo_feature/config/install/context.context.external_media.yml
  25. 21
      modules/islandora_demo_feature/config/install/context.context.fedora_media.yml
  26. 17
      modules/islandora_demo_feature/config/install/context.context.files_in_fedora.yml
  27. 4
      modules/islandora_demo_feature/config/install/context.context.repository_content.yml
  28. 8
      modules/islandora_demo_feature/config/install/context.context.taxonomy_terms.yml
  29. 19
      modules/islandora_demo_feature/config/install/core.base_field_override.node.islandora_object.menu_link.yml
  30. 17
      modules/islandora_demo_feature/config/install/core.base_field_override.node.islandora_object.title.yml
  31. 3
      modules/islandora_demo_feature/config/install/core.entity_form_display.media.audio.default.yml
  32. 3
      modules/islandora_demo_feature/config/install/core.entity_form_display.media.file.default.yml
  33. 3
      modules/islandora_demo_feature/config/install/core.entity_form_display.media.image.default.yml
  34. 3
      modules/islandora_demo_feature/config/install/core.entity_form_display.media.video.default.yml
  35. 3
      modules/islandora_demo_feature/config/install/core.entity_form_display.node.islandora_object.default.yml
  36. 2
      modules/islandora_demo_feature/config/install/language.content_settings.node.islandora_object.yml
  37. 2
      modules/islandora_demo_feature/config/install/system.action.generate_a_service_file_from_preservation_master.yml
  38. 2
      modules/islandora_demo_feature/config/install/system.action.generate_a_thumbnail_from_an_image_service_file.yml
  39. 14
      src/Controller/MediaSourceController.php
  40. 3
      src/EventGenerator/EmitEvent.php
  41. 102
      src/EventGenerator/EventGenerator.php
  42. 324
      src/Flysystem/Adapter/FedoraAdapter.php
  43. 124
      src/Flysystem/Fedora.php
  44. 31
      src/IslandoraUtils.php
  45. 121
      src/MediaSource/MediaSourceService.php
  46. 98
      src/Plugin/Action/EmitFileEvent.php
  47. 91
      src/Plugin/Action/EmitMediaEvent.php
  48. 105
      src/Plugin/Condition/ContentEntityType.php
  49. 168
      src/Plugin/Condition/FileUsesFilesystem.php
  50. 35
      src/Plugin/Condition/IsFile.php
  51. 35
      src/Plugin/Condition/IsMedia.php
  52. 35
      src/Plugin/Condition/IsNode.php
  53. 43
      src/Plugin/Condition/IsTerm.php
  54. 4
      src/Plugin/Condition/MediaHasTerm.php
  55. 116
      src/Plugin/Condition/MediaUsesFilesystem.php
  56. 5
      src/Plugin/Condition/NodeHasTerm.php
  57. 4
      src/Plugin/Condition/ParentNodeHasTerm.php
  58. 32
      src/Plugin/ContextReaction/MappingUriPredicateReaction.php
  59. 25
      tests/src/Functional/AddMediaToNodeTest.php
  60. 15
      tests/src/Functional/ContentEntityTypeTest.php
  61. 11
      tests/src/Functional/EmitNodeEventTest.php
  62. 63
      tests/src/Functional/IsFileTest.php
  63. 56
      tests/src/Functional/IsMediaTest.php
  64. 51
      tests/src/Functional/IsTermTest.php
  65. 549
      tests/src/Kernel/FedoraAdapterTest.php
  66. 62
      tests/src/Kernel/FedoraPluginTest.php
  67. 1
      tests/src/Kernel/IslandoraKernelTestBase.php

5
composer.json

@ -27,7 +27,10 @@
"drupal/migrate_plus" : "4.0-beta3", "drupal/migrate_plus" : "4.0-beta3",
"drupal/migrate_tools" : "4.0-beta3", "drupal/migrate_tools" : "4.0-beta3",
"drupal/migrate_source_csv" : "^2.1", "drupal/migrate_source_csv" : "^2.1",
"drupal/permissions_by_term" : "^1.51" "drupal/permissions_by_term" : "^1.51",
"drupal/token" : "^1.3",
"drupal/flysystem" : "^1.0",
"islandora/chullo" : "dev-master"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^6", "phpunit/phpunit": "^6",

40
config/schema/islandora.schema.yml

@ -64,22 +64,6 @@ action.configuration.delete_media_and_file:
type: action_configuration_default type: action_configuration_default
label: 'Delete media and file' label: 'Delete media and file'
condition.plugin.is_node:
type: condition.plugin
mapping:
condition.plugin.is_media:
type: condition.plugin
mapping:
condition.plugin.is_file:
type: condition.plugin
mapping:
condition.plugin.is_term:
type: condition.plugin
mapping:
condition.plugin.node_has_term: condition.plugin.node_has_term:
type: condition.plugin type: condition.plugin
mapping: mapping:
@ -100,3 +84,27 @@ condition.plugin.parent_node_has_term:
uri: uri:
type: text type: text
label: 'Taxonomy Term URI' label: 'Taxonomy Term URI'
condition.plugin.file_uses_filesystem:
type: condition.plugin
mapping:
filesystems:
type: sequence
sequence:
type: string
condition.plugin.media_uses_filesystem:
type: condition.plugin
mapping:
filesystems:
type: sequence
sequence:
type: string
condition.plugin.content_entity_type:
type: condition.plugin
mapping:
types:
type: sequence
sequence:
type: string

2
islandora.info.yml

@ -29,3 +29,5 @@ dependencies:
- migrate_tools - migrate_tools
- migrate_source_csv - migrate_source_csv
- content_translation - content_translation
- flysystem
- token

4
islandora.services.yml

@ -47,7 +47,7 @@ services:
- { name: 'context_provider' } - { name: 'context_provider' }
islandora.media_source_service: islandora.media_source_service:
class: Drupal\islandora\MediaSource\MediaSourceService class: Drupal\islandora\MediaSource\MediaSourceService
arguments: ['@entity_type.manager', '@current_user', '@stream_wrapper_manager', '@language_manager', '@token', '@entity.query'] arguments: ['@entity_type.manager', '@current_user', '@language_manager', '@entity.query', '@file_system']
islandora.utils: islandora.utils:
class: Drupal\islandora\IslandoraUtils class: Drupal\islandora\IslandoraUtils
arguments: ['@entity_type.manager', '@entity_field.manager', '@entity.query', '@context.manager', '@stream_wrapper_manager'] arguments: ['@entity_type.manager', '@entity_field.manager', '@entity.query', '@context.manager', '@flysystem_factory']

19
modules/islandora_core_feature/config/install/core.base_field_override.media.audio.name.yml

@ -0,0 +1,19 @@
langcode: en
status: true
dependencies:
config:
- media.type.audio
id: media.audio.name
field_name: name
entity_type: media
bundle: audio
label: Name
description: ''
required: true
translatable: true
default_value:
-
value: ''
default_value_callback: ''
settings: { }
field_type: string

19
modules/islandora_core_feature/config/install/core.base_field_override.media.file.name.yml

@ -0,0 +1,19 @@
langcode: en
status: true
dependencies:
config:
- media.type.file
id: media.file.name
field_name: name
entity_type: media
bundle: file
label: Name
description: ''
required: true
translatable: true
default_value:
-
value: ''
default_value_callback: ''
settings: { }
field_type: string

19
modules/islandora_core_feature/config/install/core.base_field_override.media.image.name.yml

@ -0,0 +1,19 @@
langcode: en
status: true
dependencies:
config:
- media.type.image
id: media.image.name
field_name: name
entity_type: media
bundle: image
label: Name
description: ''
required: true
translatable: true
default_value:
-
value: ''
default_value_callback: ''
settings: { }
field_type: string

19
modules/islandora_core_feature/config/install/core.base_field_override.media.video.name.yml

@ -0,0 +1,19 @@
langcode: en
status: true
dependencies:
config:
- media.type.video
id: media.video.name
field_name: name
entity_type: media
bundle: video
label: Name
description: ''
required: true
translatable: true
default_value:
-
value: ''
default_value_callback: ''
settings: { }
field_type: string

4
modules/islandora_core_feature/config/install/field.storage.media.field_media_audio_file.yml

@ -12,10 +12,10 @@ field_name: field_media_audio_file
entity_type: media entity_type: media
type: file type: file
settings: settings:
target_type: file
display_field: false display_field: false
display_default: false display_default: false
uri_scheme: public uri_scheme: fedora
target_type: file
module: file module: file
locked: false locked: false
cardinality: 1 cardinality: 1

4
modules/islandora_core_feature/config/install/field.storage.media.field_media_file.yml

@ -13,10 +13,10 @@ field_name: field_media_file
entity_type: media entity_type: media
type: file type: file
settings: settings:
uri_scheme: public
target_type: file
display_field: false display_field: false
display_default: false display_default: false
uri_scheme: fedora
target_type: file
module: file module: file
locked: false locked: false
cardinality: 1 cardinality: 1

4
modules/islandora_core_feature/config/install/field.storage.media.field_media_image.yml

@ -14,8 +14,9 @@ field_name: field_media_image
entity_type: media entity_type: media
type: image type: image
settings: settings:
uri_scheme: fedora
default_image: default_image:
uuid: null uuid: ''
alt: '' alt: ''
title: '' title: ''
width: null width: null
@ -23,7 +24,6 @@ settings:
target_type: file target_type: file
display_field: false display_field: false
display_default: false display_default: false
uri_scheme: public
module: image module: image
locked: false locked: false
cardinality: 1 cardinality: 1

4
modules/islandora_core_feature/config/install/field.storage.media.field_media_video_file.yml

@ -12,10 +12,10 @@ field_name: field_media_video_file
entity_type: media entity_type: media
type: file type: file
settings: settings:
target_type: file
display_field: false display_field: false
display_default: false display_default: false
uri_scheme: public uri_scheme: fedora
target_type: file
module: file module: file
locked: false locked: false
cardinality: 1 cardinality: 1

2
modules/islandora_core_feature/config/install/field.storage.media.field_mime_type.yml

@ -17,7 +17,7 @@ settings:
module: core module: core
locked: false locked: false
cardinality: 1 cardinality: 1
translatable: false translatable: true
indexes: { } indexes: { }
persist_with_no_fields: false persist_with_no_fields: false
custom_storage: false custom_storage: false

5
modules/islandora_core_feature/config/install/filehash.settings.yml

@ -0,0 +1,5 @@
algos:
sha1: sha1
md5: '0'
sha256: '0'
dedupe: false

2
modules/islandora_core_feature/config/install/language.content_settings.media.audio.yml

@ -8,6 +8,8 @@ dependencies:
third_party_settings: third_party_settings:
content_translation: content_translation:
enabled: true enabled: true
bundle_settings:
untranslatable_fields_hide: '0'
id: media.audio id: media.audio
target_entity_type_id: media target_entity_type_id: media
target_bundle: audio target_bundle: audio

2
modules/islandora_core_feature/config/install/language.content_settings.media.file.yml

@ -8,6 +8,8 @@ dependencies:
third_party_settings: third_party_settings:
content_translation: content_translation:
enabled: true enabled: true
bundle_settings:
untranslatable_fields_hide: '0'
id: media.file id: media.file
target_entity_type_id: media target_entity_type_id: media
target_bundle: file target_bundle: file

2
modules/islandora_core_feature/config/install/language.content_settings.media.image.yml

@ -8,6 +8,8 @@ dependencies:
third_party_settings: third_party_settings:
content_translation: content_translation:
enabled: true enabled: true
bundle_settings:
untranslatable_fields_hide: '0'
id: media.image id: media.image
target_entity_type_id: media target_entity_type_id: media
target_bundle: image target_bundle: image

2
modules/islandora_core_feature/config/install/language.content_settings.media.video.yml

@ -8,6 +8,8 @@ dependencies:
third_party_settings: third_party_settings:
content_translation: content_translation:
enabled: true enabled: true
bundle_settings:
untranslatable_fields_hide: '0'
id: media.video id: media.video
target_entity_type_id: media target_entity_type_id: media
target_bundle: video target_bundle: video

2
modules/islandora_core_feature/config/install/language.content_settings.taxonomy_term.tags.yml

@ -8,6 +8,8 @@ dependencies:
third_party_settings: third_party_settings:
content_translation: content_translation:
enabled: false enabled: false
bundle_settings:
untranslatable_fields_hide: '0'
id: taxonomy_term.tags id: taxonomy_term.tags
target_entity_type_id: taxonomy_term target_entity_type_id: taxonomy_term
target_bundle: tags target_bundle: tags

6
modules/islandora_core_feature/config/install/system.action.delete_file_from_fedora.yml

@ -7,9 +7,9 @@ dependencies:
module: module:
- islandora - islandora
id: delete_file_from_fedora id: delete_file_from_fedora
label: 'Delete File from Fedora' label: 'Delete Fedora File from Gemini'
type: file type: file
plugin: emit_file_event plugin: emit_file_event
configuration: configuration:
queue: islandora-indexing-fcrepo-delete queue: islandora-indexing-fcrepo-file-delete
event: delete event: Delete

4
modules/islandora_core_feature/config/install/system.action.index_file_in_fedora.yml

@ -7,9 +7,9 @@ dependencies:
module: module:
- islandora - islandora
id: index_file_in_fedora id: index_file_in_fedora
label: 'Index File in Fedora' label: 'Index Fedora File in Gemini'
type: file type: file
plugin: emit_file_event plugin: emit_file_event
configuration: configuration:
queue: islandora-indexing-fcrepo-file queue: islandora-indexing-fcrepo-file
event: update event: Create

1
modules/islandora_core_feature/config/install/views.view.file_checksum.yml

@ -6,6 +6,7 @@ dependencies:
- islandora_core_feature - islandora_core_feature
module: module:
- file - file
- filehash
- rest - rest
- serialization - serialization
id: file_checksum id: file_checksum

1
modules/islandora_core_feature/islandora_core_feature.info.yml

@ -10,6 +10,7 @@ dependencies:
- features - features
- field - field
- file - file
- filehash
- image - image
- islandora - islandora
- jsonld - jsonld

46
modules/islandora_demo_feature/config/install/context.context.external_media.yml

@ -0,0 +1,46 @@
langcode: en
status: true
dependencies:
module:
- islandora
name: external_media
label: 'External Media'
group: Islandora
description: 'Reactions for media whose source are outside of Fedora'
requireAllConditions: true
disabled: false
conditions:
media_uses_filesystem:
id: media_uses_filesystem
filesystems:
fedora: fedora
negate: 1
uuid: 4f3c414a-8c94-464c-a4b9-5d3eb2b35e92
context_mapping:
media: '@islandora.media_route_context_provider:media'
content_entity_type:
id: content_entity_type
types:
media: media
negate: 0
uuid: c10985ef-16ef-4571-89ad-1a0926c83b83
context_mapping:
media: '@islandora.media_route_context_provider:media'
reactions:
index:
id: index
actions:
index_media_in_triplestore: index_media_in_triplestore
index_node_in_fedora: index_node_in_fedora
saved: false
delete:
id: delete
actions:
delete_media_from_triplestore: delete_media_from_triplestore
delete_node_from_fedora: delete_node_from_fedora
saved: false
islandora_map_uri_predicate:
id: islandora_map_uri_predicate
drupal_uri_predicate: 'schema:sameAs'
saved: false
weight: 0

21
modules/islandora_demo_feature/config/install/context.context.media.yml → modules/islandora_demo_feature/config/install/context.context.fedora_media.yml

@ -1,22 +1,21 @@
langcode: en langcode: en
status: true status: true
dependencies: dependencies:
enforced:
module:
- islandora_demo_feature
module: module:
- islandora - islandora
name: media name: fedora_media
label: Media label: 'Fedora Media'
group: Islandora group: Islandora
description: 'All repository media' description: 'Reactions for media whose source is in Fedora'
requireAllConditions: false requireAllConditions: false
disabled: false disabled: false
conditions: conditions:
is_media: media_uses_filesystem:
id: is_media id: media_uses_filesystem
filesystems:
fedora: fedora
negate: 0 negate: 0
uuid: d43b2007-33d2-4503-b4a7-e3597f1e94de uuid: 7754b373-7734-42ac-ba38-21b8574b60d6
context_mapping: context_mapping:
media: '@islandora.media_route_context_provider:media' media: '@islandora.media_route_context_provider:media'
reactions: reactions:
@ -31,4 +30,8 @@ reactions:
actions: actions:
delete_media_from_triplestore: delete_media_from_triplestore delete_media_from_triplestore: delete_media_from_triplestore
saved: false saved: false
islandora_map_uri_predicate:
id: islandora_map_uri_predicate
drupal_uri_predicate: 'schema:sameAs'
saved: false
weight: 0 weight: 0

17
modules/islandora_demo_feature/config/install/context.context.files.yml → modules/islandora_demo_feature/config/install/context.context.files_in_fedora.yml

@ -1,22 +1,21 @@
langcode: en langcode: en
status: true status: true
dependencies: dependencies:
enforced:
module:
- islandora_demo_feature
module: module:
- islandora - islandora
name: files name: files_in_fedora
label: Files label: 'Fedora Files'
group: Islandora group: Islandora
description: 'All repository files' description: 'Files in Fedora'
requireAllConditions: false requireAllConditions: false
disabled: false disabled: false
conditions: conditions:
is_file: file_uses_filesystem:
id: is_file id: file_uses_filesystem
filesystems:
fedora: fedora
negate: 0 negate: 0
uuid: 19b1b4e1-ea50-41f8-ab5b-8afb83eb5588 uuid: ea9d2661-2dc1-4480-bc9b-3fedeceba5f9
context_mapping: context_mapping:
file: '@islandora.file_route_context_provider:file' file: '@islandora.file_route_context_provider:file'
reactions: reactions:

4
modules/islandora_demo_feature/config/install/context.context.repository_content.yml

@ -35,4 +35,8 @@ reactions:
delete_node_from_fedora: delete_node_from_fedora delete_node_from_fedora: delete_node_from_fedora
delete_node_from_triplestore: delete_node_from_triplestore delete_node_from_triplestore: delete_node_from_triplestore
saved: false saved: false
islandora_map_uri_predicate:
id: islandora_map_uri_predicate
drupal_uri_predicate: 'schema:sameAs'
saved: false
weight: 0 weight: 0

8
modules/islandora_demo_feature/config/install/context.context.taxonomy_terms.yml

@ -13,10 +13,12 @@ description: 'All taxonomy terms'
requireAllConditions: false requireAllConditions: false
disabled: false disabled: false
conditions: conditions:
is_term: content_entity_type:
id: is_term id: content_entity_type
types:
taxonomy_term: taxonomy_term
negate: 0 negate: 0
uuid: 71ad4847-3f48-4b16-b355-364672bdfc49 uuid: cd01ce46-58a9-4d0e-8643-66f981b2c137
context_mapping: context_mapping:
taxonomy_term: '@islandora.taxonomy_term_route_context_provider:taxonomy_term' taxonomy_term: '@islandora.taxonomy_term_route_context_provider:taxonomy_term'
reactions: reactions:

19
modules/islandora_demo_feature/config/install/core.base_field_override.node.islandora_object.menu_link.yml

@ -0,0 +1,19 @@
langcode: en
status: true
dependencies:
config:
- node.type.islandora_object
id: node.islandora_object.menu_link
field_name: menu_link
entity_type: node
bundle: islandora_object
label: 'Menu link'
description: 'Computed menu link for the node (only available during node saving).'
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
handler: default
handler_settings: { }
field_type: entity_reference

17
modules/islandora_demo_feature/config/install/core.base_field_override.node.islandora_object.title.yml

@ -0,0 +1,17 @@
langcode: en
status: true
dependencies:
config:
- node.type.islandora_object
id: node.islandora_object.title
field_name: title
entity_type: node
bundle: islandora_object
label: Title
description: ''
required: true
translatable: true
default_value: { }
default_value_callback: ''
settings: { }
field_type: string

3
modules/islandora_demo_feature/config/install/core.entity_form_display.media.audio.default.yml

@ -75,6 +75,9 @@ content:
weight: 7 weight: 7
region: content region: content
third_party_settings: { } third_party_settings: { }
translation:
weight: 10
region: content
uid: uid:
type: entity_reference_autocomplete type: entity_reference_autocomplete
weight: 4 weight: 4

3
modules/islandora_demo_feature/config/install/core.entity_form_display.media.file.default.yml

@ -75,6 +75,9 @@ content:
weight: 7 weight: 7
region: content region: content
third_party_settings: { } third_party_settings: { }
translation:
weight: 10
region: content
uid: uid:
type: entity_reference_autocomplete type: entity_reference_autocomplete
weight: 4 weight: 4

3
modules/islandora_demo_feature/config/install/core.entity_form_display.media.image.default.yml

@ -79,6 +79,9 @@ content:
weight: 7 weight: 7
region: content region: content
third_party_settings: { } third_party_settings: { }
translation:
weight: 10
region: content
uid: uid:
type: entity_reference_autocomplete type: entity_reference_autocomplete
weight: 4 weight: 4

3
modules/islandora_demo_feature/config/install/core.entity_form_display.media.video.default.yml

@ -75,6 +75,9 @@ content:
weight: 7 weight: 7
region: content region: content
third_party_settings: { } third_party_settings: { }
translation:
weight: 10
region: content
uid: uid:
type: entity_reference_autocomplete type: entity_reference_autocomplete
weight: 4 weight: 4

3
modules/islandora_demo_feature/config/install/core.entity_form_display.node.islandora_object.default.yml

@ -90,6 +90,9 @@ content:
size: 60 size: 60
placeholder: '' placeholder: ''
third_party_settings: { } third_party_settings: { }
translation:
weight: 10
region: content
uid: uid:
type: entity_reference_autocomplete type: entity_reference_autocomplete
weight: 4 weight: 4

2
modules/islandora_demo_feature/config/install/language.content_settings.node.islandora_object.yml

@ -8,6 +8,8 @@ dependencies:
third_party_settings: third_party_settings:
content_translation: content_translation:
enabled: true enabled: true
bundle_settings:
untranslatable_fields_hide: '0'
id: node.islandora_object id: node.islandora_object
target_entity_type_id: node target_entity_type_id: node
target_bundle: islandora_object target_bundle: islandora_object

2
modules/islandora_demo_feature/config/install/system.action.generate_a_service_file_from_preservation_master.yml

@ -17,3 +17,5 @@ configuration:
derivative_term_uri: 'http://pcdm.org/use#ServiceFile' derivative_term_uri: 'http://pcdm.org/use#ServiceFile'
mimetype: image/jpeg mimetype: image/jpeg
args: '' args: ''
scheme: public
path: '[date:custom:Y]-[date:custom:m]/[node:nid]-[term:name].jpg'

2
modules/islandora_demo_feature/config/install/system.action.generate_a_thumbnail_from_an_image_service_file.yml

@ -17,3 +17,5 @@ configuration:
derivative_term_uri: 'http://pcdm.org/use#ThumbnailImage' derivative_term_uri: 'http://pcdm.org/use#ThumbnailImage'
mimetype: image/jpeg mimetype: image/jpeg
args: '-thumbnail 100x100' args: '-thumbnail 100x100'
scheme: public
path: '[date:custom:Y]-[date:custom:m]/[node:nid]-[term:name].jpg'

14
src/Controller/MediaSourceController.php

@ -143,17 +143,7 @@ class MediaSourceController extends ControllerBase {
throw new BadRequestHttpException("Missing Content-Type header"); throw new BadRequestHttpException("Missing Content-Type header");
} }
$content_disposition = $request->headers->get('Content-Disposition', ""); $content_location = $request->headers->get('Content-Location', "");
if (empty($content_disposition)) {
throw new BadRequestHttpException("Missing Content-Disposition header");
}
$matches = [];
if (!preg_match('/attachment; filename="(.*)"/', $content_disposition, $matches)) {
throw new BadRequestHttpException("Malformed Content-Disposition header");
}
$filename = $matches[1];
// Since we create both a Media and its File, // Since we create both a Media and its File,
// start a transaction. // start a transaction.
@ -166,7 +156,7 @@ class MediaSourceController extends ControllerBase {
$taxonomy_term, $taxonomy_term,
$request->getContent(TRUE), $request->getContent(TRUE),
$content_type, $content_type,
$filename $content_location
); );
// We return the media if it was newly created. // We return the media if it was newly created.

3
src/EventGenerator/EmitEvent.php

@ -4,6 +4,7 @@ namespace Drupal\islandora\EventGenerator;
use Drupal\Core\Access\AccessResult; use Drupal\Core\Access\AccessResult;
use Drupal\Core\Action\ConfigurableActionBase; use Drupal\Core\Action\ConfigurableActionBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManager; use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
@ -160,7 +161,7 @@ abstract class EmitEvent extends ConfigurableActionBase implements ContainerFact
/** /**
* Override this function to control what gets encoded as a json note. * Override this function to control what gets encoded as a json note.
*/ */
protected function generateData($entity) { protected function generateData(EntityInterface $entity) {
return $this->configuration; return $this->configuration;
} }

102
src/EventGenerator/EventGenerator.php

@ -21,6 +21,15 @@ class EventGenerator implements EventGeneratorInterface {
$user_url = $user->toUrl()->setAbsolute()->toString(); $user_url = $user->toUrl()->setAbsolute()->toString();
if ($entity instanceof FileInterface) {
$entity_url = $entity->url();
$mimetype = $entity->getMimeType();
}
else {
$entity_url = $entity->toUrl()->setAbsolute()->toString();
$mimetype = 'text/html';
}
$event = [ $event = [
"@context" => "https://www.w3.org/ns/activitystreams", "@context" => "https://www.w3.org/ns/activitystreams",
"actor" => [ "actor" => [
@ -38,7 +47,15 @@ class EventGenerator implements EventGeneratorInterface {
], ],
"object" => [ "object" => [
"id" => "urn:uuid:{$entity->uuid()}", "id" => "urn:uuid:{$entity->uuid()}",
"url" => $entity instanceof FileInterface ? $this->generateFileLinks($entity) : $this->generateRestLinks($entity), "url" => [
[
"name" => "Canonical",
"type" => "Link",
"href" => $entity_url,
"mediaType" => $mimetype,
"rel" => "canonical",
],
],
], ],
]; ];
@ -53,6 +70,24 @@ class EventGenerator implements EventGeneratorInterface {
$event["summary"] = ucfirst($data["event"]) . " a " . ucfirst($entity_type); $event["summary"] = ucfirst($data["event"]) . " a " . ucfirst($entity_type);
} }
// Add REST links for non-file entities.
if ($entity_type != 'file') {
$event['object']['url'][] = [
"name" => "JSON",
"type" => "Link",
"href" => "$entity_url?_format=json",
"mediaType" => "application/json",
"rel" => "alternate",
];
$event['object']['url'][] = [
"name" => "JSONLD",
"type" => "Link",
"href" => "$entity_url?_format=jsonld",
"mediaType" => "application/ld+json",
"rel" => "alternate",
];
}
unset($data["event"]); unset($data["event"]);
unset($data["queue"]); unset($data["queue"]);
@ -67,69 +102,4 @@ class EventGenerator implements EventGeneratorInterface {
return json_encode($event); return json_encode($event);
} }
/**
* Generates file urls.
*
* @param \Drupal\file\FileInterface $file
* The file.
*
* @return array
* AS2 Links.
*/
protected function generateFileLinks(FileInterface $file) {
$file_url = $file->url();
$checksum_url = Url::fromRoute('view.file_checksum.rest_export_1', ['file' => $file->id()])
->setAbsolute()
->toString();
return [
[
"name" => "File",
"type" => "Link",
"href" => "$file_url",
"mediaType" => $file->getMimeType(),
],
[
"name" => "Checksum",
"type" => "Link",
"href" => "$checksum_url?_format=json",
"mediaType" => "application/json",
],
];
}
/**
* Generates REST urls.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity.
*
* @return array
* AS2 Links.
*/
protected function generateRestLinks(EntityInterface $entity) {
$url = $entity->toUrl()->setAbsolute()->toString();
return [
[
"name" => "Canoncial",
"type" => "Link",
"href" => "$url",
"mediaType" => "text/html",
"rel" => "canonical",
],
[
"name" => "JSONLD",
"type" => "Link",
"href" => "$url?_format=jsonld",
"mediaType" => "application/ld+json",
],
[
"name" => "JSON",
"type" => "Link",
"href" => "$url?_format=json",
"mediaType" => "application/json",
],
];
}
} }

324
src/Flysystem/Adapter/FedoraAdapter.php

@ -0,0 +1,324 @@
<?php
namespace Drupal\islandora\Flysystem\Adapter;
use Islandora\Chullo\IFedoraApi;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Adapter\Polyfill\NotSupportingVisibilityTrait;
use League\Flysystem\Adapter\Polyfill\StreamedCopyTrait;
use League\Flysystem\Config;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\StreamWrapper;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
/**
* Fedora adapter for Flysystem.
*/
class FedoraAdapter implements AdapterInterface {
use StreamedCopyTrait;
use NotSupportingVisibilityTrait;
protected $fedora;
protected $mimeTypeGuesser;
/**
* Constructs a Fedora adapter for Flysystem.
*
* @param \Islandora\Chullo\IFedoraApi $fedora
* Fedora client.
* @param \Symfony\Component\HttpFoundation\File\Mimetype\MimeTypeGuesserInterface $mime_type_guesser
* Mimetype guesser.
*/
public function __construct(IFedoraApi $fedora, MimeTypeGuesserInterface $mime_type_guesser) {
$this->fedora = $fedora;
$this->mimeTypeGuesser = $mime_type_guesser;
}
/**
* {@inheritdoc}
*/
public function has($path) {
$response = $this->fedora->getResourceHeaders($path);
return $response->getStatusCode() == 200;
}
/**
* {@inheritdoc}
*/
public function read($path) {
$meta = $this->readStream($path);
if (!$meta) {
return FALSE;
}
if (isset($meta['stream'])) {
$meta['contents'] = stream_get_contents($meta['stream']);
fclose($meta['stream']);
unset($meta['stream']);
}
return $meta;
}
/**
* {@inheritdoc}
*/
public function readStream($path) {
$response = $this->fedora->getResource($path);
if ($response->getStatusCode() != 200) {
return FALSE;
}
$meta = $this->getMetadataFromHeaders($response);
$meta['path'] = $path;
if ($meta['type'] == 'file') {
$meta['stream'] = StreamWrapper::getResource($response->getBody());
}
return $meta;
}
/**
* {@inheritdoc}
*/
public function getMetadata($path) {
$response = $this->fedora->getResourceHeaders($path);
if ($response->getStatusCode() != 200) {
return FALSE;
}
$meta = $this->getMetadataFromHeaders($response);
$meta['path'] = $path;
return $meta;
}
/**
* {@inheritdoc}
*/
public function getSize($path) {
return $this->getMetadata($path);
}
/**
* {@inheritdoc}
*/
public function getMimetype($path) {
return $this->getMetadata($path);
}
/**
* {@inheritdoc}
*/
public function getTimestamp($path) {
return $this->getMetadata($path);
}
/**
* Gets metadata from response headers.
*
* @param \GuzzleHttp\Psr7\Response $response
* Response.
*/
protected function getMetadataFromHeaders(Response $response) {
$last_modified = \DateTime::createFromFormat(
\DateTime::RFC1123,
$response->getHeader('Last-Modified')[0]
);
// NonRDFSource's are considered files. Everything else is a
// directory.
$type = 'dir';
$links = Psr7\parse_header($response->getHeader('Link'));
foreach ($links as $link) {
if ($link['rel'] == 'type' && $link[0] == '<http://www.w3.org/ns/ldp#NonRDFSource>') {
$type = 'file';
break;
}
}
$meta = [
'type' => $type,
'timestamp' => $last_modified->getTimestamp(),
];
if ($type == 'file') {
$meta['size'] = $response->getHeader('Content-Length')[0];
$meta['mimetype'] = $response->getHeader('Content-Type')[0];
}
return $meta;
}
/**
* {@inheritdoc}
*/
public function listContents($directory = '', $recursive = FALSE) {
// Strip leading and trailing whitespace and /'s.
$normalized = trim($directory, ' \t\n\r\0\x0B/');
// Exit early if it's a file.
$meta = $this->getMetadata($normalized);
if ($meta['type'] == 'file') {
return [];
}
// Get the resource from Fedora.
$response = $this->fedora->getResource($normalized, ['Accept' => 'application/ld+json']);
$jsonld = (string) $response->getBody();
$graph = json_decode($jsonld, TRUE);
$uri = $this->fedora->getBaseUri() . $normalized;
// Hack it out of the graph.
// There may be more than one resource returned.
$resource = [];
foreach ($graph as $elem) {
if (isset($elem['@id']) && $elem['@id'] == $uri) {
$resource = $elem;
break;
}
}
// Exit early if resource doesn't contain other resources.
if (!isset($resource['http://www.w3.org/ns/ldp#contains'])) {
return [];
}
// Collapse uris to a single array.
$contained = array_map(
function ($elem) {
return $elem['@id'];
},
$resource['http://www.w3.org/ns/ldp#contains']
);
// Exit early if not recursive.
if (!$recursive) {
// Transform results to their flysystem metadata.
return array_map(
[$this, 'transformToMetadata'],
$contained
);
}
// Recursively get containment for ancestors.
$ancestors = [];
foreach ($contained as $child_uri) {
$child_directory = explode($this->fedora->getBaseUri(), $child_uri)[1];
$ancestors = array_merge($this->listContents($child_directory, $recursive), $ancestors);
}
// // Transform results to their flysystem metadata.
return array_map(
[$this, 'transformToMetadata'],
array_merge($ancestors, $contained)
);
}
/**
* Normalizes data for listContents().
*
* @param string $uri
* Uri.
*/
protected function transformToMetadata($uri) {
if (is_array($uri)) {
return $uri;
}
$exploded = explode($this->fedora->getBaseUri(), $uri);
return $this->getMetadata($exploded[1]);
}
/**
* {@inheritdoc}
*/
public function write($path, $contents, Config $config) {
$headers = [
'Content-Type' => $this->mimeTypeGuesser->guess($path),
];
$response = $this->fedora->saveResource(
$path,
$contents,
$headers
);
$code = $response->getStatusCode();
if (!in_array($code, [201, 204])) {
return FALSE;
}
return $this->getMetadata($path);
}
/**
* {@inheritdoc}
*/
public function writeStream($path, $contents, Config $config) {
return $this->write($path, $contents, $config);
}
/**
* {@inheritdoc}
*/
public function update($path, $contents, Config $config) {
return $this->write($path, $contents, $config);
}
/**
* {@inheritdoc}
*/
public function updateStream($path, $contents, Config $config) {
return $this->write($path, $contents, $config);
}
/**
* {@inheritdoc}
*/
public function delete($path) {
$response = $this->fedora->deleteResource($path);
$code = $response->getStatusCode();
return in_array($code, [204, 404]);
}
/**
* {@inheritdoc}
*/
public function deleteDir($dirname) {
return $this->delete($dirname);
}
/**
* {@inheritdoc}
*/
public function rename($path, $newpath) {
if ($this->copy($path, $newpath)) {
return $this->delete($path);
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function createDir($dirname, Config $config) {
$response = $this->fedora->saveResource(
$dirname
);
$code = $response->getStatusCode();
if (!in_array($code, [201, 204])) {
return FALSE;
}
return $this->getMetadata($dirname);
}
}

124
src/Flysystem/Fedora.php

@ -0,0 +1,124 @@
<?php
namespace Drupal\islandora\Flysystem;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\flysystem\Plugin\FlysystemPluginInterface;
use Drupal\flysystem\Plugin\FlysystemUrlTrait;
use Drupal\islandora\Flysystem\Adapter\FedoraAdapter;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Client;
use Islandora\Chullo\IFedoraApi;
use Islandora\Chullo\FedoraApi;
use Psr\Http\Message\RequestInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
/**
* Drupal plugin for the Fedora Flysystem adapter.
*
* @Adapter(id = "fedora")
*/
class Fedora implements FlysystemPluginInterface, ContainerFactoryPluginInterface {
use FlysystemUrlTrait;
protected $fedora;
protected $mimeTypeGuesser;
/**
* Constructs a Fedora plugin for Flysystem.
*
* @param \Islandora\Chullo\IFedoraApi $fedora
* Fedora client.
* @param \Symfony\Component\HttpFoundation\File\Mimetype\MimeTypeGuesserInterface $mime_type_guesser
* Mimetype guesser.
*/
public function __construct(
IFedoraApi $fedora,
MimeTypeGuesserInterface $mime_type_guesser
) {
$this->fedora = $fedora;
$this->mimeTypeGuesser = $mime_type_guesser;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
// Construct Authorization header using jwt token.
$jwt = $container->get('jwt.authentication.jwt');
$auth = 'Bearer ' . $jwt->generateToken();
// Construct guzzle client to middleware that adds the header.
$stack = HandlerStack::create();
$stack->push(static::addHeader('Authorization', $auth));
$client = new Client([
'handler' => $stack,
'base_uri' => $configuration['root'],
]);
$fedora = new FedoraApi($client);
// Return it.
return new static(
$fedora,
$container->get('file.mime_type.guesser')
);
}
/**
* Guzzle middleware to add a header to outgoing requests.
*
* @param string $header
* Header name.
* @param string $value
* Header value.
*/
public static function addHeader($header, $value) {
return function (callable $handler) use ($header, $value) {
return function (
RequestInterface $request,
array $options
) use (
$handler,
$header,
$value
) {
$request = $request->withHeader($header, $value);
return $handler($request, $options);
};
};
}
/**
* {@inheritdoc}
*/
public function getAdapter() {
return new FedoraAdapter($this->fedora, $this->mimeTypeGuesser);
}
/**
* {@inheritdoc}
*/
public function ensure($force = FALSE) {
// Check fedora root for sanity.
$response = $this->fedora->getResourceHeaders('');
if ($response->getStatusCode() != 200) {
return [[
'severity' => RfcLogLevel::ERROR,
'message' => '%url returned %status',
'context' => [
'%url' => $this->fedora->getBaseUri(),
'%status' => $response->getStatusCode(),
],
],
];
}
return [];
}
}

31
src/IslandoraUtils.php

@ -7,8 +7,9 @@ use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityFieldManager; use Drupal\Core\Entity\EntityFieldManager;
use Drupal\Core\Entity\EntityTypeManager; use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Entity\Query\QueryFactory; use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\StreamWrapper\StreamWrapperManager; use Drupal\Core\Site\Settings;
use Drupal\file\FileInterface; use Drupal\file\FileInterface;
use Drupal\flysystem\FlysystemFactory;
use Drupal\islandora\ContextProvider\NodeContextProvider; use Drupal\islandora\ContextProvider\NodeContextProvider;
use Drupal\islandora\ContextProvider\MediaContextProvider; use Drupal\islandora\ContextProvider\MediaContextProvider;
use Drupal\islandora\ContextProvider\FileContextProvider; use Drupal\islandora\ContextProvider\FileContextProvider;
@ -55,11 +56,11 @@ class IslandoraUtils {
protected $contextManager; protected $contextManager;
/** /**
* Stream wrapper manager. * Flysystem factory.
* *
* @var \Drupal\Core\StreamWrapper\StreamWrapperManager * @var \Drupal\flysystem\FlysystemFactory
*/ */
protected $streamWrapperManager; protected $flysystemFactory;
/** /**
* Constructor. * Constructor.
@ -72,21 +73,21 @@ class IslandoraUtils {
* Entity query. * Entity query.
* @param \Drupal\context\ContextManager $context_manager * @param \Drupal\context\ContextManager $context_manager
* Context manager. * Context manager.
* @param \Drupal\Core\StreamWrapper\StreamWrapperManager $stream_wrapper_manager * @param \Drupal\flysystem\FlysystemFactory $flysystem_factory
* Stream wrapper manager. * Flysystem factory.
*/ */
public function __construct( public function __construct(
EntityTypeManager $entity_type_manager, EntityTypeManager $entity_type_manager,
EntityFieldManager $entity_field_manager, EntityFieldManager $entity_field_manager,
QueryFactory $entity_query, QueryFactory $entity_query,
ContextManager $context_manager, ContextManager $context_manager,
StreamWrapperManager $stream_wrapper_manager FlysystemFactory $flysystem_factory
) { ) {
$this->entityTypeManager = $entity_type_manager; $this->entityTypeManager = $entity_type_manager;
$this->entityFieldManager = $entity_field_manager; $this->entityFieldManager = $entity_field_manager;
$this->entityQuery = $entity_query; $this->entityQuery = $entity_query;
$this->contextManager = $context_manager; $this->contextManager = $context_manager;
$this->streamWrapperManager = $stream_wrapper_manager; $this->flysystemFactory = $flysystem_factory;
} }
/** /**
@ -367,4 +368,18 @@ class IslandoraUtils {
return FALSE; return FALSE;
} }
/**
* Returns a list of all available filesystem schemes.
*
* @return String[]
* List of all available filesystem schemes.
*/
public function getFilesystemSchemes() {
$schemes = ['public'];
if (!empty(Settings::get('file_private_path'))) {
$schemes[] = 'private';
}
return array_merge($schemes, $this->flysystemFactory->getSchemes());
}
} }

121
src/MediaSource/MediaSourceService.php

@ -2,13 +2,11 @@
namespace Drupal\islandora\MediaSource; namespace Drupal\islandora\MediaSource;
use Drupal\Component\Render\PlainTextOutput;
use Drupal\Core\Entity\EntityTypeManager; use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Entity\Query\QueryFactory; use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\File\FileSystem;
use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StreamWrapper\StreamWrapperManager;
use Drupal\Core\Utility\Token;
use Drupal\file\FileInterface; use Drupal\file\FileInterface;
use Drupal\media\MediaInterface; use Drupal\media\MediaInterface;
use Drupal\media\MediaTypeInterface; use Drupal\media\MediaTypeInterface;
@ -37,13 +35,6 @@ class MediaSourceService {
*/ */
protected $account; protected $account;
/**
* Stream wrapper manager.
*
* @var \Drupal\Core\StreamWrapper\StreamWrapperManager
*/
protected $streamWrapperManager;
/** /**
* Language manager. * Language manager.
* *
@ -52,18 +43,18 @@ class MediaSourceService {
protected $languageManager; protected $languageManager;
/** /**
* Token service. * Entity query.
* *
* @var \Drupal\Core\Utility\Token * @var \Drupal\Core\Entity\Query\QueryFactory
*/ */
protected $token; protected $entityQuery;
/** /**
* Entity query. * File system service.
* *
* @var \Drupal\Core\Entity\Query\QueryFactory * @var \Drupal\Core\File\FileSystem
*/ */
protected $entityQuery; protected $fileSystem;
/** /**
* Constructor. * Constructor.
@ -72,29 +63,25 @@ class MediaSourceService {
* The entity type manager. * The entity type manager.
* @param \Drupal\Core\Session\AccountInterface $account * @param \Drupal\Core\Session\AccountInterface $account
* The current user. * The current user.
* @param \Drupal\Core\StreamWrapper\StreamWrapperManager $stream_wrapper_manager
* Stream wrapper manager.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* Language manager. * Language manager.
* @param \Drupal\Core\Utility\Token $token
* Token service.
* @param \Drupal\Core\Entity\Query\QueryFactory $entity_query * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
* Entity query. * Entity query.
* @param \Drupal\Core\File\FileSystem $file_system
* File system service.
*/ */
public function __construct( public function __construct(
EntityTypeManager $entity_type_manager, EntityTypeManager $entity_type_manager,
AccountInterface $account, AccountInterface $account,
StreamWrapperManager $stream_wrapper_manager,
LanguageManagerInterface $language_manager, LanguageManagerInterface $language_manager,
Token $token, QueryFactory $entity_query,
QueryFactory $entity_query FileSystem $file_system
) { ) {
$this->entityTypeManager = $entity_type_manager; $this->entityTypeManager = $entity_type_manager;
$this->account = $account; $this->account = $account;
$this->streamWrapperManager = $stream_wrapper_manager;
$this->languageManager = $language_manager; $this->languageManager = $language_manager;
$this->token = $token;
$this->entityQuery = $entity_query; $this->entityQuery = $entity_query;
$this->fileSystem = $file_system;
} }
/** /**
@ -120,6 +107,32 @@ class MediaSourceService {
return $type_configuration['source_field']; return $type_configuration['source_field'];
} }
/**
* Gets the value of a source field for a Media.
*
* @param \Drupal\media\MediaInterface $media
* Media whose source field you are searching for.
*
* @return \Drupal\file\FileInterface
* File if it exists
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
public function getSourceFile(MediaInterface $media) {
// Get the source field for the media type.
$source_field = $this->getSourceFieldName($media->bundle());
if (empty($source_field)) {
throw new NotFoundHttpException("Source field not set for {$media->bundle()} media");
}
// Get the file from the media.
$files = $media->get($source_field)->referencedEntities();
$file = reset($files);
return $file;
}
/** /**
* Updates a media's source field with the supplied resource. * Updates a media's source field with the supplied resource.
* *
@ -137,25 +150,19 @@ class MediaSourceService {
$resource, $resource,
$mimetype $mimetype
) { ) {
// Get the source field for the media type.
$source_field = $this->getSourceFieldName($media->bundle()); $source_field = $this->getSourceFieldName($media->bundle());
$file = $this->getSourceFile($media);
if (empty($source_field)) {
throw new NotFoundHttpException("Source field not set for {$media->bundle()} media");
}
// Get the file from the media.
$files = $media->get($source_field)->referencedEntities();
$file = reset($files);
// Update it. // Update it.
$this->updateFile($file, $resource, $mimetype); $this->updateFile($file, $resource, $mimetype);
$file->save();
// Set fields provided by type plugin and mapped in bundle configuration // Set fields provided by type plugin and mapped in bundle configuration
// for the media. // for the media.
foreach ($media->bundle->entity->getFieldMap() as $source => $destination) { foreach ($media->bundle->entity->getFieldMap() as $source => $destination) {
if ($media->hasField($destination) && $value = $media->getSource()->getMetadata($media, $source)) { if ($media->hasField($destination) && $value = $media->getSource()->getMetadata($media, $source)) {
$media->set($destination, $value); $media->set($destination, $value);
}
// Ensure width and height are updated on File reference when it's an // Ensure width and height are updated on File reference when it's an
// image. Otherwise you run into scaling problems when updating images // image. Otherwise you run into scaling problems when updating images
// with different sizes. // with different sizes.
@ -163,7 +170,6 @@ class MediaSourceService {
$media->get($source_field)->first()->set($source, $value); $media->get($source_field)->first()->set($source, $value);
} }
} }
}
$media->save(); $media->save();
} }
@ -180,11 +186,13 @@ class MediaSourceService {
*/ */
protected function updateFile(FileInterface $file, $resource, $mimetype = NULL) { protected function updateFile(FileInterface $file, $resource, $mimetype = NULL) {
$uri = $file->getFileUri(); $uri = $file->getFileUri();
$file_stream_wrapper = $this->streamWrapperManager->getViaUri($uri);
$path = ""; $destination = fopen($uri, 'wb');
$file_stream_wrapper->stream_open($uri, 'w', STREAM_REPORT_ERRORS, $path); if (!$destination) {
$file_stream = $file_stream_wrapper->stream_cast(STREAM_CAST_AS_STREAM); throw new HttpException(500, "File $uri could not be opened to write.");
$content_length = stream_copy_to_stream($resource, $file_stream); }
$content_length = stream_copy_to_stream($resource, $destination);
if ($content_length === FALSE) { if ($content_length === FALSE) {
throw new HttpException(500, "Request body could not be copied to $uri"); throw new HttpException(500, "Request body could not be copied to $uri");
@ -192,8 +200,8 @@ class MediaSourceService {
if ($content_length === 0) { if ($content_length === 0) {
// Clean up the newly created, empty file. // Clean up the newly created, empty file.
$file_stream_wrapper->unlink($uri); unlink($uri);
throw new BadRequestHttpException("The request contents are empty."); throw new HttpException(400, "No bytes were copied to $uri");
} }
if (!empty($mimetype)) { if (!empty($mimetype)) {
@ -217,8 +225,8 @@ class MediaSourceService {
* New file contents as a resource. * New file contents as a resource.
* @param string $mimetype * @param string $mimetype
* New mimetype of contents. * New mimetype of contents.
* @param string $filename * @param string $content_location
* New filename for contents. * Drupal/PHP stream wrapper for where to upload the binary.
* *
* @throws HttpException * @throws HttpException
*/ */
@ -228,9 +236,8 @@ class MediaSourceService {
TermInterface $taxonomy_term, TermInterface $taxonomy_term,
$resource, $resource,
$mimetype, $mimetype,
$filename $content_location
) { ) {
$existing = $this->entityQuery->get('media') $existing = $this->entityQuery->get('media')
->condition('field_media_of', $node->id()) ->condition('field_media_of', $node->id())
->condition('field_tags', $taxonomy_term->id()) ->condition('field_tags', $taxonomy_term->id())
@ -256,22 +263,11 @@ class MediaSourceService {
throw new NotFoundHttpException("Source field not set for $bundle media"); throw new NotFoundHttpException("Source field not set for $bundle media");
} }
// Load its config to get file extensions and upload path.
$source_field_config = $this->entityTypeManager->getStorage('field_config')->load("media.$bundle.$source_field");
// Construct the destination uri.
$directory = $source_field_config->getSetting('file_directory');
$directory = trim($directory, '/');
$directory = PlainTextOutput::renderFromHtml($this->token->replace($directory, ['node' => $node]));
$scheme = file_default_scheme();
$destination_directory = "$scheme://$directory";
$destination = "$destination_directory/$filename";
// Construct the File. // Construct the File.
$file = $this->entityTypeManager->getStorage('file')->create([ $file = $this->entityTypeManager->getStorage('file')->create([
'uid' => $this->account->id(), 'uid' => $this->account->id(),
'uri' => $destination, 'uri' => $content_location,
'filename' => $filename, 'filename' => $this->fileSystem->basename($content_location),
'filemime' => $mimetype, 'filemime' => $mimetype,
'status' => FILE_STATUS_PERMANENT, 'status' => FILE_STATUS_PERMANENT,
]); ]);
@ -285,7 +281,8 @@ class MediaSourceService {
throw new BadRequestHttpException("Invalid file extension. Valid types are $valid_extensions"); throw new BadRequestHttpException("Invalid file extension. Valid types are $valid_extensions");
} }
if (!file_prepare_directory($destination_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { $directory = $this->fileSystem->dirname($content_location);
if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
throw new HttpException(500, "The destination directory does not exist, could not be created, or is not writable"); throw new HttpException(500, "The destination directory does not exist, could not be created, or is not writable");
} }
@ -297,7 +294,7 @@ class MediaSourceService {
$media_struct = [ $media_struct = [
'bundle' => $bundle, 'bundle' => $bundle,
'uid' => $this->account->id(), 'uid' => $this->account->id(),
'name' => $filename, 'name' => $file->getFilename(),
'langcode' => $this->languageManager->getDefaultLanguage()->getId(), 'langcode' => $this->languageManager->getDefaultLanguage()->getId(),
"$source_field" => [ "$source_field" => [
'target_id' => $file->id(), 'target_id' => $file->id(),
@ -312,7 +309,7 @@ class MediaSourceService {
// Set alt text. // Set alt text.
if ($source_field_config->getSetting('alt_field') && $source_field_config->getSetting('alt_field_required')) { if ($source_field_config->getSetting('alt_field') && $source_field_config->getSetting('alt_field_required')) {
$media_struct[$source_field]['alt'] = $filename; $media_struct[$source_field]['alt'] = $file->getFilename;
} }
$media = $this->entityTypeManager->getStorage('media')->create($media_struct); $media = $this->entityTypeManager->getStorage('media')->create($media_struct);

98
src/Plugin/Action/EmitFileEvent.php

@ -2,7 +2,16 @@
namespace Drupal\islandora\Plugin\Action; namespace Drupal\islandora\Plugin\Action;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\File\FileSystem;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Site\Settings;
use Drupal\jwt\Authentication\Provider\JwtAuth;
use Drupal\islandora\EventGenerator\EmitEvent; use Drupal\islandora\EventGenerator\EmitEvent;
use Drupal\islandora\EventGenerator\EventGeneratorInterface;
use Stomp\StatefulStomp;
use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Emits a File event. * Emits a File event.
@ -13,4 +22,91 @@ use Drupal\islandora\EventGenerator\EmitEvent;
* type = "file" * type = "file"
* ) * )
*/ */
class EmitFileEvent extends EmitEvent {} class EmitFileEvent extends EmitEvent {
/**
* File system service.
*
* @var \Drupal\Core\File\FileSystem
*/
protected $fileSystem;
/**
* 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\EntityTypeManager $entity_type_manager
* Entity type manager.
* @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.
* @param \Drupal\Core\File\FileSystem $file_system
* File system service.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
AccountInterface $account,
EntityTypeManager $entity_type_manager,
EventGeneratorInterface $event_generator,
StatefulStomp $stomp,
JwtAuth $auth,
FileSystem $file_system
) {
parent::__construct(
$configuration,
$plugin_id,
$plugin_definition,
$account,
$entity_type_manager,
$event_generator,
$stomp,
$auth
);
$this->fileSystem = $file_system;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('current_user'),
$container->get('entity_type.manager'),
$container->get('islandora.eventgenerator'),
$container->get('islandora.stomp'),
$container->get('jwt.authentication.jwt'),
$container->get('file_system')
);
}
/**
* {@inheritdoc}
*/
protected function generateData(EntityInterface $entity) {
$uri = $entity->getFileUri();
$scheme = $this->fileSystem->uriScheme($uri);
$flysystem_config = Settings::get('flysystem');
$data = parent::generateData($entity);
if (isset($flysystem_config[$scheme]) && $flysystem_config[$scheme]['driver'] == 'fedora') {
$data['fedora_uri'] = str_replace("$scheme://", $flysystem_config[$scheme]['config']['root'], $uri);
}
return $data;
}
}

91
src/Plugin/Action/EmitMediaEvent.php

@ -2,7 +2,15 @@
namespace Drupal\islandora\Plugin\Action; namespace Drupal\islandora\Plugin\Action;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Session\AccountInterface;
use Drupal\jwt\Authentication\Provider\JwtAuth;
use Drupal\islandora\EventGenerator\EmitEvent; use Drupal\islandora\EventGenerator\EmitEvent;
use Drupal\islandora\EventGenerator\EventGeneratorInterface;
use Drupal\islandora\MediaSource\MediaSourceService;
use Stomp\StatefulStomp;
use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Emits a Media event. * Emits a Media event.
@ -13,4 +21,85 @@ use Drupal\islandora\EventGenerator\EmitEvent;
* type = "media" * type = "media"
* ) * )
*/ */
class EmitMediaEvent extends EmitEvent {} class EmitMediaEvent extends EmitEvent {
/**
* Media source service.
*
* @var \Drupal\islandora\MediaSource\MediaSourceService
*/
protected $mediaSource;
/**
* 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\EntityTypeManager $entity_type_manager
* Entity type manager.
* @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.
* @param \Drupal\islandora\MediaSource\MediaSourceService $media_source
* Media source service.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
AccountInterface $account,
EntityTypeManager $entity_type_manager,
EventGeneratorInterface $event_generator,
StatefulStomp $stomp,
JwtAuth $auth,
MediaSourceService $media_source
) {
parent::__construct(
$configuration,
$plugin_id,
$plugin_definition,
$account,
$entity_type_manager,
$event_generator,
$stomp,
$auth
);
$this->mediaSource = $media_source;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('current_user'),
$container->get('entity_type.manager'),
$container->get('islandora.eventgenerator'),
$container->get('islandora.stomp'),
$container->get('jwt.authentication.jwt'),
$container->get('islandora.media_source_service')
);
}
/**
* {@inheritdoc}
*/
protected function generateData(EntityInterface $entity) {
$data = parent::generateData($entity);
$data['source_field'] = $this->mediaSource->getSourceFieldName($entity->bundle());
return $data;
}
}

105
src/Plugin/Condition/ContentEntityType.php

@ -0,0 +1,105 @@
<?php
namespace Drupal\islandora\Plugin\Condition;
use Drupal\Core\Condition\ConditionPluginBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides a 'Content Entity Type' condition.
*
* @Condition(
* id = "content_entity_type",
* label = @Translation("Content Entity Type"),
* context = {
* "node" = @ContextDefinition("entity:node", required = FALSE, label = @Translation("Node")),
* "media" = @ContextDefinition("entity:media", required = FALSE, label = @Translation("Media")),
* "file" = @ContextDefinition("entity:file", required = FALSE, label = @Translation("File")),
* "taxonomy_term" = @ContextDefinition("entity:taxonomy_term", required = FALSE, label = @Translation("Term"))
* }
* )
*/
class ContentEntityType extends ConditionPluginBase {
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['types'] = [
'#title' => $this->t('Content Entity Types'),
'#type' => 'checkboxes',
'#options' => [
'node' => $this->t('Node'),
'media' => $this->t('Media'),
'file' => $this->t('File'),
'taxonomy_term' => $this->t('Taxonomy Term'),
],
'#default_value' => $this->configuration['types'],
];
return parent::buildConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['types'] = array_filter($form_state->getValue('types'));
parent::submitConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function evaluate() {
if (empty($this->configuration['types']) && !$this->isNegated()) {
return TRUE;
}
foreach ($this->configuration['types'] as $type) {
if ($this->getContext($type)->hasContextValue()) {
$entity = $this->getContextValue($type);
if ($entity && $entity->getEntityTypeId() == $type) {
return TRUE;
}
}
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function summary() {
if (count($this->configuration['types']) > 1) {
$types = $this->configuration['types'];
$last = array_pop($types);
$types = implode(', ', $types);
return $this->t(
'The content entity is a @types or @last',
[
'@types' => $types,
'@last' => $last,
]
);
}
$type = reset($this->configuration['types']);
return $this->t(
'The content entity is a @type',
[
'@type' => $type,
]
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return array_merge(
['types' => []],
parent::defaultConfiguration()
);
}
}

168
src/Plugin/Condition/FileUsesFilesystem.php

@ -0,0 +1,168 @@
<?php
namespace Drupal\islandora\Plugin\Condition;
use Drupal\Core\Condition\ConditionPluginBase;
use Drupal\Core\File\FileSystem;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\file\FileInterface;
use Drupal\islandora\IslandoraUtils;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a condition to filter media based on where its source file resides.
*
* @Condition(
* id = "file_uses_filesystem",
* label = @Translation("File uses filesystem"),
* context = {
* "file" = @ContextDefinition("entity:file", required = TRUE , label = @Translation("file"))
* }
* )
*/
class FileUsesFilesystem extends ConditionPluginBase implements ContainerFactoryPluginInterface {
/**
* Islandora utility functions.
*
* @var \Drupal\islandora\IslandoraUtils
*/
protected $utils;
/**
* File system service.
*
* @var \Drupal\Core\File\FileSystem
*/
protected $fileSystem;
/**
* Constructor.
*
* @param array $configuration
* The plugin configuration, i.e. an array with configuration values keyed
* by configuration option name. The special key 'context' may be used to
* initialize the defined contexts by setting it to an array of context
* values keyed by context names.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\islandora\IslandoraUtils $utils
* Islandora utility functions.
* @param \Drupal\Core\File\FileSystem $file_system
* File system service.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
IslandoraUtils $utils,
FileSystem $file_system
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->utils = $utils;
$this->fileSystem = $file_system;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('islandora.utils'),
$container->get('file_system')
);
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$schemes = $this->utils->getFilesystemSchemes();
$options = array_combine($schemes, $schemes);
$form['filesystems'] = [
'#title' => $this->t('Filesystems'),
'#type' => 'checkboxes',
'#options' => $options,
'#default_value' => $this->configuration['filesystems'],
];
return parent::buildConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['filesystems'] = array_filter($form_state->getValue('filesystems'));
parent::submitConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function summary() {
if (count($this->configuration['filesystems']) > 1) {
$filesystems = $this->configuration['filesystems'];
$last = array_pop($filesystems);
$filesystems = implode(', ', $filesystems);
return $this->t(
'The file uses @filesystems or @last',
[
'@filesystems' => $filesystems,
'@last' => $last,
]
);
}
$filesystem = reset($this->configuration['filesystems']);
return $this->t(
'The file uses @filesystem',
[
'@filesystem' => $filesystem,
]
);
}
/**
* {@inheritdoc}
*/
public function evaluate() {
if (empty($this->configuration['filesystems']) && !$this->isNegated()) {
return TRUE;
}
$file = $this->getContextValue('file');
return $this->evaluateFile($file);
}
/**
* The actual evaluate function.
*
* @param \Drupal\file\FileInterface $file
* File.
*
* @return bool
* TRUE on success.
*/
protected function evaluateFile(FileInterface $file) {
$uri = $file->getFileUri();
$scheme = $this->fileSystem->uriScheme($uri);
return !empty($this->configuration['filesystems'][$scheme]);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return array_merge(
['filesystems' => []],
parent::defaultConfiguration()
);
}
}

35
src/Plugin/Condition/IsFile.php

@ -1,35 +0,0 @@
<?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

@ -1,35 +0,0 @@
<?php
namespace Drupal\islandora\Plugin\Condition;
use Drupal\Core\Condition\ConditionPluginBase;
use Drupal\media\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

@ -1,35 +0,0 @@
<?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;
}
}

43
src/Plugin/Condition/IsTerm.php

@ -1,43 +0,0 @@
<?php
namespace Drupal\islandora\Plugin\Condition;
use Drupal\Core\Condition\ConditionPluginBase;
use Drupal\taxonomy\TermInterface;
/**
* Provides an 'Is Term' condition.
*
* @Condition(
* id = "is_term",
* label = @Translation("Is Term"),
* context = {
* "taxonomy_term" = @ContextDefinition("entity:taxonomy_term", label = @Translation("Term"))
* }
* )
*/
class IsTerm extends ConditionPluginBase {
/**
* {@inheritdoc}
*/
public function summary() {
return $this->t('The entity is a Taxonomy Term');
}
/**
* {@inheritdoc}
*/
public function getContextMapping() {
$this->configuration['context_mapping'] = ['taxonomy_term' => '@islandora.taxonomy_term_route_context_provider:taxonomy_term'];
return parent::getContextMapping();
}
/**
* {@inheritdoc}
*/
public function evaluate() {
return $this->getContextValue('taxonomy_term') instanceof TermInterface;
}
}

4
src/Plugin/Condition/MediaHasTerm.php

@ -19,6 +19,10 @@ class MediaHasTerm extends NodeHasTerm {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function evaluate() { public function evaluate() {
if (empty($this->configuration['uri']) && !$this->isNegated()) {
return TRUE;
}
$media = $this->getContextValue('media'); $media = $this->getContextValue('media');
if (!$media) { if (!$media) {
return FALSE; return FALSE;

116
src/Plugin/Condition/MediaUsesFilesystem.php

@ -0,0 +1,116 @@
<?php
namespace Drupal\islandora\Plugin\Condition;
use Drupal\Core\File\FileSystem;
use Drupal\islandora\MediaSource\MediaSourceService;
use Drupal\islandora\IslandoraUtils;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a condition to filter media based on where its source file resides.
*
* @Condition(
* id = "media_uses_filesystem",
* label = @Translation("Media uses filesystem"),
* context = {
* "media" = @ContextDefinition("entity:media", required = TRUE , label = @Translation("media"))
* }
* )
*/
class MediaUsesFilesystem extends FileUsesFilesystem {
/**
* Media source service.
*
* @var \Drupal\islandora\MediaSource\MediaSourceService
*/
protected $mediaSource;
/**
* Constructor.
*
* @param array $configuration
* The plugin configuration, i.e. an array with configuration values keyed
* by configuration option name. The special key 'context' may be used to
* initialize the defined contexts by setting it to an array of context
* values keyed by context names.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\islandora\IslandoraUtils $utils
* Islandora utility functions.
* @param \Drupal\Core\File\FileSystem $file_system
* File system service.
* @param \Drupal\islandora\MediaSource\MediaSourceService $media_source
* Media source service.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
IslandoraUtils $utils,
FileSystem $file_system,
MediaSourceService $media_source
) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $utils, $file_system);
$this->mediaSource = $media_source;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('islandora.utils'),
$container->get('file_system'),
$container->get('islandora.media_source_service')
);
}
/**
* {@inheritdoc}
*/
public function summary() {
if (count($this->configuration['filesystems']) > 1) {
$filesystems = $this->configuration['filesystems'];
$last = array_pop($filesystems);
$filesystems = implode(', ', $filesystems);
return $this->t(
'The media uses @filesystems or @last',
[
'@filesystems' => $filesystems,
'@last' => $last,
]
);
}
$filesystem = reset($this->configuration['filesystems']);
return $this->t(
'The media uses @filesystem',
[
'@filesystem' => $filesystem,
]
);
}
/**
* {@inheritdoc}
*/
public function evaluate() {
if (empty($this->configuration['filesystems']) && !$this->isNegated()) {
return TRUE;
}
$media = $this->getContextValue('media');
$file = $this->mediaSource->getSourceFile($media);
if (!$file) {
return FALSE;
}
return $this->evaluateFile($file);
}
}

5
src/Plugin/Condition/NodeHasTerm.php

@ -94,7 +94,6 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI
'#type' => 'entity_autocomplete', '#type' => 'entity_autocomplete',
'#title' => $this->t('Term'), '#title' => $this->t('Term'),
'#tags' => TRUE, '#tags' => TRUE,
'#required' => TRUE,
'#default_value' => $default, '#default_value' => $default,
'#target_type' => 'taxonomy_term', '#target_type' => 'taxonomy_term',
]; ];
@ -124,6 +123,10 @@ class NodeHasTerm extends ConditionPluginBase implements ContainerFactoryPluginI
* {@inheritdoc} * {@inheritdoc}
*/ */
public function evaluate() { public function evaluate() {
if (empty($this->configuration['uri']) && !$this->isNegated()) {
return TRUE;
}
$node = $this->getContextValue('node'); $node = $this->getContextValue('node');
if (!$node) { if (!$node) {
return FALSE; return FALSE;

4
src/Plugin/Condition/ParentNodeHasTerm.php

@ -19,6 +19,10 @@ class ParentNodeHasTerm extends NodeHasTerm {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function evaluate() { public function evaluate() {
if (empty($this->configuration['uri']) && !$this->isNegated()) {
return TRUE;
}
$media = $this->getContextValue('media'); $media = $this->getContextValue('media');
if (!$media) { if (!$media) {
return FALSE; return FALSE;

32
src/Plugin/ContextReaction/MappingUriPredicateReaction.php

@ -4,8 +4,12 @@ namespace Drupal\islandora\Plugin\ContextReaction;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\islandora\ContextReaction\NormalizerAlterReaction; use Drupal\islandora\ContextReaction\NormalizerAlterReaction;
use Drupal\islandora\MediaSource\MediaSourceService;
use Drupal\jsonld\Normalizer\NormalizerBase; use Drupal\jsonld\Normalizer\NormalizerBase;
use Drupal\media\MediaInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Map URI to predicate context reaction. * Map URI to predicate context reaction.
@ -15,10 +19,32 @@ use Drupal\jsonld\Normalizer\NormalizerBase;
* label = @Translation("Map URI to predicate") * label = @Translation("Map URI to predicate")
* ) * )
*/ */
class MappingUriPredicateReaction extends NormalizerAlterReaction { class MappingUriPredicateReaction extends NormalizerAlterReaction implements ContainerFactoryPluginInterface {
const URI_PREDICATE = 'drupal_uri_predicate'; const URI_PREDICATE = 'drupal_uri_predicate';
protected $mediaSource;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, MediaSourceService $media_source) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->mediaSource = $media_source;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('islandora.media_source_service')
);
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -43,6 +69,10 @@ class MappingUriPredicateReaction extends NormalizerAlterReaction {
if (isset($normalized['@graph']) && is_array($normalized['@graph'])) { if (isset($normalized['@graph']) && is_array($normalized['@graph'])) {
foreach ($normalized['@graph'] as &$graph) { foreach ($normalized['@graph'] as &$graph) {
if (isset($graph['@id']) && $graph['@id'] == $url) { if (isset($graph['@id']) && $graph['@id'] == $url) {
if ($entity instanceof MediaInterface) {
$file = $this->mediaSource->getSourceFile($entity);
$url = $file->url('canonical', ['absolute' => TRUE]);
}
if (isset($graph[$drupal_predicate])) { if (isset($graph[$drupal_predicate])) {
if (!is_array($graph[$drupal_predicate])) { if (!is_array($graph[$drupal_predicate])) {
if ($graph[$drupal_predicate] == $url) { if ($graph[$drupal_predicate] == $url) {

25
tests/src/Functional/AddMediaToNodeTest.php

@ -84,7 +84,7 @@ class AddMediaToNodeTest extends IslandoraFunctionalTestBase {
'http_errors' => FALSE, 'http_errors' => FALSE,
'headers' => [ 'headers' => [
'Content-Type' => 'text/plain', 'Content-Type' => 'text/plain',
'Content-Disposition' => 'attachment; filename="test_file.txt"', 'Content-Location' => 'public://test_file.txt',
], ],
'body' => $file_contents, 'body' => $file_contents,
]; ];
@ -124,14 +124,14 @@ class AddMediaToNodeTest extends IslandoraFunctionalTestBase {
'auth' => [$account->getUsername(), $account->pass_raw], 'auth' => [$account->getUsername(), $account->pass_raw],
'http_errors' => FALSE, 'http_errors' => FALSE,
'headers' => [ 'headers' => [
'Content-Disposition' => 'attachment; filename="test_file.txt"', 'Content-Location' => 'public://test_file.txt',
], ],
'body' => $file_contents, 'body' => $file_contents,
]; ];
$response = $client->request('PUT', $add_to_node_url, $options); $response = $client->request('PUT', $add_to_node_url, $options);
$this->assertTrue($response->getStatusCode() == 400, "Expected 400, received {$response->getStatusCode()}"); $this->assertTrue($response->getStatusCode() == 400, "Expected 400, received {$response->getStatusCode()}");
// Request without Content-Disposition header should fail with 400. // Request without Content-Location header should fail with 400.
$options = [ $options = [
'auth' => [$account->getUsername(), $account->pass_raw], 'auth' => [$account->getUsername(), $account->pass_raw],
'http_errors' => FALSE, 'http_errors' => FALSE,
@ -143,26 +143,13 @@ class AddMediaToNodeTest extends IslandoraFunctionalTestBase {
$response = $client->request('PUT', $add_to_node_url, $options); $response = $client->request('PUT', $add_to_node_url, $options);
$this->assertTrue($response->getStatusCode() == 400, "Expected 400, received {$response->getStatusCode()}"); $this->assertTrue($response->getStatusCode() == 400, "Expected 400, received {$response->getStatusCode()}");
// Request with malformed Content-Disposition header should fail with 400.
$options = [
'auth' => [$account->getUsername(), $account->pass_raw],
'http_errors' => FALSE,
'headers' => [
'Content-Type' => 'text/plain',
'Content-Disposition' => 'garbage; filename="test_file.txt"',
],
'body' => $file_contents,
];
$response = $client->request('PUT', $add_to_node_url, $options);
$this->assertTrue($response->getStatusCode() == 400, "Expected 400, received {$response->getStatusCode()}");
// Request without body should fail with 400. // Request without body should fail with 400.
$options = [ $options = [
'auth' => [$account->getUsername(), $account->pass_raw], 'auth' => [$account->getUsername(), $account->pass_raw],
'http_errors' => FALSE, 'http_errors' => FALSE,
'headers' => [ 'headers' => [
'Content-Type' => 'text/plain', 'Content-Type' => 'text/plain',
'Content-Disposition' => 'attachment; filename="test_file.txt"', 'Content-Location' => 'public://test_file.txt',
], ],
]; ];
$response = $client->request('PUT', $add_to_node_url, $options); $response = $client->request('PUT', $add_to_node_url, $options);
@ -174,7 +161,7 @@ class AddMediaToNodeTest extends IslandoraFunctionalTestBase {
'http_errors' => FALSE, 'http_errors' => FALSE,
'headers' => [ 'headers' => [
'Content-Type' => 'text/plain', 'Content-Type' => 'text/plain',
'Content-Disposition' => 'attachment; filename="test_file.txt"', 'Content-Location' => 'public://test_file.txt',
], ],
'body' => $file_contents, 'body' => $file_contents,
]; ];
@ -217,7 +204,7 @@ class AddMediaToNodeTest extends IslandoraFunctionalTestBase {
'http_errors' => FALSE, 'http_errors' => FALSE,
'headers' => [ 'headers' => [
'Content-Type' => 'text/plain', 'Content-Type' => 'text/plain',
'Content-Disposition' => 'attachment; filename="test_file.txt"', 'Content-Location' => 'public://test_file.txt',
], ],
'body' => $file_contents, 'body' => $file_contents,
]; ];

15
tests/src/Functional/IsNodeTest.php → tests/src/Functional/ContentEntityTypeTest.php

@ -3,11 +3,11 @@
namespace Drupal\Tests\islandora\Functional; namespace Drupal\Tests\islandora\Functional;
/** /**
* Tests the IsNode condition. * Tests the ContentEntityType condition.
* *
* @group islandora * @group islandora
*/ */
class IsNodeTest extends IslandoraFunctionalTestBase { class ContentEntityTypeTest extends IslandoraFunctionalTestBase {
/** /**
* @covers \Drupal\islandora\ContextProvider\NodeContextProvider::__construct * @covers \Drupal\islandora\ContextProvider\NodeContextProvider::__construct
@ -16,13 +16,15 @@ class IsNodeTest extends IslandoraFunctionalTestBase {
* @covers \Drupal\islandora\ContextProvider\MediaContextProvider::getRuntimeContexts * @covers \Drupal\islandora\ContextProvider\MediaContextProvider::getRuntimeContexts
* @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts * @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts
* @covers \Drupal\islandora\IslandoraContextManager::applyContexts * @covers \Drupal\islandora\IslandoraContextManager::applyContexts
* @covers \Drupal\islandora\Plugin\Condition\IsNode::evaluate * @covers \Drupal\islandora\Plugin\Condition\ContentEntityType::buildConfigurationForm
* @covers \Drupal\islandora\Plugin\Condition\ContentEntityType::submitConfigurationForm
* @covers \Drupal\islandora\Plugin\Condition\ContentEntityType::evaluate
* @covers \Drupal\islandora\PresetReaction\PresetReaction::buildConfigurationForm * @covers \Drupal\islandora\PresetReaction\PresetReaction::buildConfigurationForm
* @covers \Drupal\islandora\PresetReaction\PresetReaction::submitConfigurationForm * @covers \Drupal\islandora\PresetReaction\PresetReaction::submitConfigurationForm
* @covers \Drupal\islandora\PresetReaction\PresetReaction::execute * @covers \Drupal\islandora\PresetReaction\PresetReaction::execute
* @covers \Drupal\islandora\IslandoraServiceProvider::alter * @covers \Drupal\islandora\IslandoraServiceProvider::alter
*/ */
public function testIsNode() { public function testContentEntityType() {
// Create a test user. // Create a test user.
$account = $this->drupalCreateUser([ $account = $this->drupalCreateUser([
'bypass node access', 'bypass node access',
@ -34,7 +36,10 @@ class IsNodeTest extends IslandoraFunctionalTestBase {
$this->drupalLogin($account); $this->drupalLogin($account);
$this->createContext('Test', 'test'); $this->createContext('Test', 'test');
$this->addCondition('test', 'is_node'); $this->addCondition('test', 'content_entity_type');
$this->getSession()->getPage()->checkField("edit-conditions-content-entity-type-types-node");
$this->getSession()->getPage()->findById("edit-conditions-content-entity-type-context-mapping-node")->selectOption("@node.node_route_context:node");
$this->getSession()->getPage()->pressButton(t('Save and continue'));
$this->addPresetReaction('test', 'index', 'hello_world'); $this->addPresetReaction('test', 'index', 'hello_world');
// Create a new node confirm Hello World! is printed to the screen. // Create a new node confirm Hello World! is printed to the screen.

11
tests/src/Functional/EmitNodeEventTest.php

@ -18,7 +18,9 @@ class EmitNodeEventTest extends IslandoraFunctionalTestBase {
* @covers \Drupal\islandora\EventGenerator\EventGenerator::generateEvent * @covers \Drupal\islandora\EventGenerator\EventGenerator::generateEvent
* @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts * @covers \Drupal\islandora\IslandoraContextManager::evaluateContexts
* @covers \Drupal\islandora\IslandoraContextManager::applyContexts * @covers \Drupal\islandora\IslandoraContextManager::applyContexts
* @covers \Drupal\islandora\Plugin\Condition\IsNode::evaluate * @covers \Drupal\islandora\Plugin\Condition\ContentEntityType::buildConfigurationForm
* @covers \Drupal\islandora\Plugin\Condition\ContentEntityType::submitConfigurationForm
* @covers \Drupal\islandora\Plugin\Condition\ContentEntityType::evaluate
* @covers \Drupal\islandora\PresetReaction\PresetReaction::buildConfigurationForm * @covers \Drupal\islandora\PresetReaction\PresetReaction::buildConfigurationForm
* @covers \Drupal\islandora\PresetReaction\PresetReaction::submitConfigurationForm * @covers \Drupal\islandora\PresetReaction\PresetReaction::submitConfigurationForm
* @covers \Drupal\islandora\PresetReaction\PresetReaction::execute * @covers \Drupal\islandora\PresetReaction\PresetReaction::execute
@ -38,7 +40,12 @@ class EmitNodeEventTest extends IslandoraFunctionalTestBase {
// Create a context and add the action as an index reaction. // Create a context and add the action as an index reaction.
$this->createContext('Test', 'test'); $this->createContext('Test', 'test');
$this->addCondition('test', 'is_node');
$this->addCondition('test', 'content_entity_type');
$this->getSession()->getPage()->checkField("edit-conditions-content-entity-type-types-node");
$this->getSession()->getPage()->findById("edit-conditions-content-entity-type-context-mapping-node")->selectOption("@node.node_route_context:node");
$this->getSession()->getPage()->pressButton(t('Save and continue'));
$this->addPresetReaction('test', 'index', $action_id); $this->addPresetReaction('test', 'index', $action_id);
$this->assertSession()->statusCodeEquals(200); $this->assertSession()->statusCodeEquals(200);

63
tests/src/Functional/IsFileTest.php

@ -1,63 +0,0 @@
<?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 media and confirm Hello World! is printed to the
// screen for the file upload.
$file = current($this->getTestFiles('file'));
$values = [
'name[0][value]' => 'Test Media',
'files[field_media_file_0]' => __DIR__ . '/../../fixtures/test_file.txt',
];
$this->drupalPostForm('media/add/' . $this->testMediaType->id(), $values, t('Save'));
$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');
// Confirm Hello World! is not printed to the screen.
$this->assertSession()->pageTextNotContains("Hello World!");
}
}

56
tests/src/Functional/IsMediaTest.php

@ -1,56 +0,0 @@
<?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 media and confirm Hello World! is printed to the
// screen.
$values = [
'name[0][value]' => 'Test Media',
'files[field_media_file_0]' => __DIR__ . '/../../fixtures/test_file.txt',
];
$this->drupalPostForm('media/add/' . $this->testMediaType->id(), $values, t('Save'));
$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!");
}
}

51
tests/src/Functional/IsTermTest.php

@ -1,51 +0,0 @@
<?php
namespace Drupal\Tests\islandora\Functional;
/**
* Tests the IsTerm condition.
*
* @group islandora
*/
class IsTermTest 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\IsTerm::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 testIsTerm() {
// Create a test user.
$account = $this->drupalCreateUser([
'bypass node access',
'administer contexts',
'administer taxonomy',
]);
$this->drupalLogin($account);
$this->createContext('Test', 'test');
$this->addCondition('test', 'is_term');
$this->addPresetReaction('test', 'index', 'hello_world');
// Create a new term and confirm Hello World! is printed to the screen.
$this->drupalPostForm(
'admin/structure/taxonomy/manage/' . $this->testVocabulary->id() . '/add',
['name[0][value]' => 'Test Term'],
t('Save')
);
$this->assertSession()->pageTextContains("Hello World!");
// Create a new node and confirm Hello World! is not printed to the screen.
$this->postNodeAddForm('test_type', ['title[0][value]' => 'Test Node'], 'Save');
$this->assertSession()->pageTextNotContains("Hello World!");
}
}

549
tests/src/Kernel/FedoraAdapterTest.php

@ -0,0 +1,549 @@
<?php
namespace Drupal\Tests\islandora\Kernel;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Response;
use Drupal\islandora\Flysystem\Adapter\FedoraAdapter;
use Islandora\Chullo\IFedoraApi;
use League\Flysystem\Config;
use Prophecy\Argument;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
/**
* Tests the Fedora adapter for Flysystem.
*
* @group islandora
* @coversDefaultClass \Drupal\islandora\Flysystem\Adapter\FedoraAdapter
*/
class FedoraAdapterTest extends IslandoraKernelTestBase {
/**
* Mocks up an adapter for Fedora calls that return 404.
*/
protected function createAdapterForFail() {
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(404);
$response = $prophecy->reveal();
$prophecy = $this->prophesize(IFedoraApi::class);
$prophecy->getResourceHeaders('')->willReturn($response);
$prophecy->getResource('')->willReturn($response);
$api = $prophecy->reveal();
$mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)->reveal();
return new FedoraAdapter($api, $mime_guesser);
}
/**
* Mocks up an adapter for Fedora LDP-NR response.
*/
protected function createAdapterForFile() {
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(200);
$prophecy->getHeader('Last-Modified')->willReturn(["Wed, 25 Jul 2018 17:42:04 GMT"]);
$prophecy->getHeader('Link')->willReturn(['<http://www.w3.org/ns/ldp#Resource>;rel="type"', '<http://www.w3.org/ns/ldp#NonRDFSource>;rel="type"']);
$prophecy->getHeader('Content-Type')->willReturn(['text/plain']);
$prophecy->getHeader('Content-Length')->willReturn([strlen("DERP")]);
$prophecy->getBody()->willReturn(PSR7\stream_for("DERP"));
$response = $prophecy->reveal();
$prophecy = $this->prophesize(IFedoraApi::class);
$prophecy->getResourceHeaders('')->willReturn($response);
$prophecy->getResource('')->willReturn($response);
$api = $prophecy->reveal();
$mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)->reveal();
return new FedoraAdapter($api, $mime_guesser);
}
/**
* Mocks up an adapter for Fedora LDP-RS response.
*/
protected function createAdapterForDirectory() {
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(200);
$prophecy->getHeader('Last-Modified')->willReturn(["Wed, 25 Jul 2018 17:42:04 GMT"]);
$prophecy->getHeader('Link')->willReturn(['<http://www.w3.org/ns/ldp#Resource>;rel="type"', '<http://www.w3.org/ns/ldp#RDFSource>;rel="type"']);
$response = $prophecy->reveal();
$prophecy = $this->prophesize(IFedoraApi::class);
$prophecy->getResourceHeaders('')->willReturn($response);
$api = $prophecy->reveal();
$mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)->reveal();
return new FedoraAdapter($api, $mime_guesser);
}
/**
* Mocks up an adapter for Fedora write requests.
*/
protected function createAdapterForWrite() {
$fedora_prophecy = $this->prophesize(IFedoraApi::class);
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(201);
$fedora_prophecy->saveResource('', '', Argument::any())->willReturn($prophecy->reveal());
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(200);
$prophecy->getHeader('Last-Modified')->willReturn(["Wed, 25 Jul 2018 17:42:04 GMT"]);
$prophecy->getHeader('Link')->willReturn(['<http://www.w3.org/ns/ldp#Resource>;rel="type"', '<http://www.w3.org/ns/ldp#NonRDFSource>;rel="type"']);
$prophecy->getHeader('Content-Type')->willReturn(['text/plain']);
$prophecy->getHeader('Content-Length')->willReturn([strlen("DERP")]);
$prophecy->getBody()->willReturn(PSR7\stream_for("DERP"));
$fedora_prophecy->getResourceHeaders('')->willReturn($prophecy->reveal());
$api = $fedora_prophecy->reveal();
$mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)->reveal();
return new FedoraAdapter($api, $mime_guesser);
}
/**
* Mocks up an adapter for Fedora write requests that fail.
*/
protected function createAdapterForWriteFail() {
$fedora_prophecy = $this->prophesize(IFedoraApi::class);
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(500);
$fedora_prophecy->saveResource('', '', Argument::any())->willReturn($prophecy->reveal());
$api = $fedora_prophecy->reveal();
$mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)->reveal();
return new FedoraAdapter($api, $mime_guesser);
}
/**
* Mocks up an adapter for creating directories requests.
*/
protected function createAdapterForCreateDir() {
$fedora_prophecy = $this->prophesize(IFedoraApi::class);
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(201);
$fedora_prophecy->saveResource('')->willReturn($prophecy->reveal());
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(200);
$prophecy->getHeader('Last-Modified')->willReturn(["Wed, 25 Jul 2018 17:42:04 GMT"]);
$prophecy->getHeader('Link')->willReturn(['<http://www.w3.org/ns/ldp#Resource>;rel="type"', '<http://www.w3.org/ns/ldp#RDFSource>;rel="type"']);
$fedora_prophecy->getResourceHeaders('')->willReturn($prophecy->reveal());
$api = $fedora_prophecy->reveal();
$mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)->reveal();
return new FedoraAdapter($api, $mime_guesser);
}
/**
* Mocks up an adapter for Delete requests.
*/
protected function createAdapterForDelete() {
$fedora_prophecy = $this->prophesize(IFedoraApi::class);
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(204);
$fedora_prophecy->deleteResource('')->willReturn($prophecy->reveal());
$fedora_prophecy->getResourceHeaders('')->willReturn($prophecy->reveal());
$api = $fedora_prophecy->reveal();
$mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)->reveal();
return new FedoraAdapter($api, $mime_guesser);
}
/**
* Mocks up an adapter for Delete requests that fail.
*/
protected function createAdapterForDeleteFail() {
$fedora_prophecy = $this->prophesize(IFedoraApi::class);
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(500);
$fedora_prophecy->deleteResource('')->willReturn($prophecy->reveal());
$api = $fedora_prophecy->reveal();
$mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)->reveal();
return new FedoraAdapter($api, $mime_guesser);
}
/**
* Asserts the stucture/contents of a metadata response for a file.
*/
protected function assertFileMetadata(array $metadata) {
$this->assertTrue($metadata['type'] == 'file', "Expecting 'type' of 'file', received '" . $metadata['type'] . "'");
$this->assertTrue($metadata['timestamp'] == '1532540524', "Expecting 'timestamp' of '1532540524', received '" . $metadata['timestamp'] . "'");
$this->assertTrue($metadata['size'] == strlen("DERP"), "Expecting 'size' of '" . strlen("DERP") . "', received '" . $metadata['size'] . "'");
$this->assertTrue($metadata['mimetype'] == 'text/plain', "Expecting 'mimetype' of 'image/png', received '" . $metadata['mimetype'] . "'");
}
/**
* Asserts the stucture/contents of a metadata response for a directory.
*/
protected function assertDirMetadata(array $metadata) {
$this->assertTrue($metadata['type'] == 'dir', "Expecting 'type' of 'dir', received '" . $metadata['type'] . "'");
$this->assertTrue($metadata['timestamp'] == '1532540524', "Expecting 'timestamp' of '1532540524', received '" . $metadata['timestamp'] . "'");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::getMetadata
*/
public function testGetMetadataFail() {
$adapter = $this->createAdapterForFail();
$this->assertTrue($adapter->getMetadata('') == FALSE, "getMetadata() must return FALSE on non-200");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::getMetadata
*/
public function testGetMetadataForFile() {
$adapter = $this->createAdapterForFile();
$metadata = $adapter->getMetadata('');
$this->assertFileMetadata($metadata);
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::getMetadata
*/
public function testGetMetadataForDirectory() {
$adapter = $this->createAdapterForDirectory();
$metadata = $adapter->getMetadata('');
$this->assertDirMetadata($metadata);
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::readStream
*/
public function testReadStream() {
$adapter = $this->createAdapterForFile();
$metadata = $adapter->readStream('');
$this->assertFileMetadata($metadata);
$this->assertTrue(is_resource($metadata['stream']), "Expecting a 'stream' that is a resource");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::readStream
*/
public function testReadStreamFail() {
$adapter = $this->createAdapterForFail();
$this->assertTrue($adapter->readStream('') == FALSE, "readStream() should return FALSE on non-200 from Fedora");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::read
*/
public function testRead() {
$adapter = $this->createAdapterForFile();
$metadata = $adapter->read('');
$this->assertFileMetadata($metadata);
$this->assertTrue($metadata['contents'] == "DERP", "Expecting 'content' of 'DERP', received '${metadata['contents']}'");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::read
*/
public function testReadFail() {
$adapter = $this->createAdapterForFail();
$this->assertTrue($adapter->read('') == FALSE, "readStream() should return FALSE on non-200 from Fedora");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::has
*/
public function testHasFail() {
$adapter = $this->createAdapterForFail();
$this->assertTrue($adapter->has('') == FALSE, "has() must return FALSE on non-200");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::has
*/
public function testHasSuccess() {
$adapter = $this->createAdapterForFile();
$this->assertTrue($adapter->has('') == TRUE, "has() must return TRUE on 200");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::getSize
*/
public function testGetSizeFail() {
$adapter = $this->createAdapterForFail();
$this->assertTrue($adapter->getSize('') == FALSE, "getSize() must return FALSE on non-200");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::getSize
*/
public function testGetSizeSuccess() {
$adapter = $this->createAdapterForFile();
$metadata = $adapter->getSize('');
$this->assertTrue($metadata['size'] == strlen("DERP"), "Expecting 'size' of '" . strlen("DERP") . "', received '" . $metadata['size'] . "'");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::getMimetype
*/
public function testGetMimetypeFail() {
$adapter = $this->createAdapterForFail();
$this->assertTrue($adapter->getMimetype('') == FALSE, "getMimetype() must return FALSE on non-200");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::getMimetype
*/
public function testGetMimetypeSuccess() {
$adapter = $this->createAdapterForFile();
$metadata = $adapter->getMimetype('');
$this->assertTrue($metadata['mimetype'] == 'text/plain', "Expecting 'mimetype' of 'text/plain', received '" . $metadata['mimetype'] . "'");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::getTimestamp
*/
public function testGetTimestampFail() {
$adapter = $this->createAdapterForFail();
$this->assertTrue($adapter->getTimestamp('') == FALSE, "getTimestamp() must return FALSE on non-200");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::getTimestamp
*/
public function testGetTimestampSuccess() {
$adapter = $this->createAdapterForFile();
$metadata = $adapter->getTimestamp('');
$this->assertTrue($metadata['timestamp'] == '1532540524', "Expecting 'timestamp' of '1532540524', received '" . $metadata['timestamp'] . "'");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::write
*/
public function testWriteFail() {
$adapter = $this->createAdapterForWriteFail();
$this->assertTrue($adapter->write('', '', $this->prophesize(Config::class)->reveal()) == FALSE, "write() must return FALSE on non-201 or non-204");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::write
*/
public function testWrite() {
$adapter = $this->createAdapterForWrite();
$metadata = $adapter->write('', '', $this->prophesize(Config::class)->reveal());
$this->assertFileMetadata($metadata);
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::writeStream
*/
public function testWriteStreamFail() {
$adapter = $this->createAdapterForWriteFail();
$this->assertTrue($adapter->writeStream('', '', $this->prophesize(Config::class)->reveal()) == FALSE, "writeStream() must return FALSE on non-201 or non-204");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::writeStream
*/
public function testWriteStream() {
$adapter = $this->createAdapterForWrite();
$metadata = $adapter->writeStream('', '', $this->prophesize(Config::class)->reveal());
$this->assertFileMetadata($metadata);
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::update
*/
public function testUpdateFail() {
$adapter = $this->createAdapterForWriteFail();
$this->assertTrue($adapter->update('', '', $this->prophesize(Config::class)->reveal()) == FALSE, "write() must return FALSE on non-201 or non-204");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::update
*/
public function testUpdate() {
$adapter = $this->createAdapterForWrite();
$metadata = $adapter->update('', '', $this->prophesize(Config::class)->reveal());
$this->assertFileMetadata($metadata);
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::updateStream
*/
public function testUpdateStreamFail() {
$adapter = $this->createAdapterForWriteFail();
$this->assertTrue($adapter->updateStream('', '', $this->prophesize(Config::class)->reveal()) == FALSE, "writeStream() must return FALSE on non-201 or non-204");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::updateStream
*/
public function testUpdateStream() {
$adapter = $this->createAdapterForWrite();
$metadata = $adapter->updateStream('', '', $this->prophesize(Config::class)->reveal());
$this->assertFileMetadata($metadata);
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::delete
*/
public function testDeleteFail() {
$adapter = $this->createAdapterForDeleteFail();
$this->assertTrue($adapter->delete('') == FALSE, "delete() must return FALSE on non-204 or non-404");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::delete
*/
public function testDelete() {
$adapter = $this->createAdapterForDelete();
$this->assertTrue($adapter->delete('') == TRUE, "delete() must return TRUE on 204 or 404");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::deleteDir
*/
public function testDeleteDirFail() {
$adapter = $this->createAdapterForDeleteFail();
$this->assertTrue($adapter->deleteDir('') == FALSE, "deleteDir() must return FALSE on non-204 or non-404");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::deleteDir
*/
public function testDeleteDir() {
$adapter = $this->createAdapterForDelete();
$this->assertTrue($adapter->deleteDir('') == TRUE, "deleteDir() must return TRUE on 204 or 404");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::rename
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::copy
*/
public function testRenameFail() {
$adapter = $this->createAdapterForFail();
$this->assertTrue($adapter->rename('', '') == FALSE, "rename() must return FALSE on Fedora error");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::rename
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::copy
*/
public function testRename() {
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(200);
$prophecy->getHeader('Last-Modified')->willReturn(["Wed, 25 Jul 2018 17:42:04 GMT"]);
$prophecy->getHeader('Link')->willReturn(['<http://www.w3.org/ns/ldp#Resource>;rel="type"', '<http://www.w3.org/ns/ldp#NonRDFSource>;rel="type"']);
$prophecy->getHeader('Content-Type')->willReturn(['text/plain']);
$prophecy->getHeader('Content-Length')->willReturn([strlen("DERP")]);
$prophecy->getBody()->willReturn(PSR7\stream_for("DERP"));
$response = $prophecy->reveal();
$fedora_prophecy = $this->prophesize(IFedoraApi::class);
$fedora_prophecy->getResource(Argument::any())->willReturn($response);
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(201);
$response = $prophecy->reveal();
$fedora_prophecy->saveResource(Argument::any(), Argument::any(), Argument::any())->willReturn($response);
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(200);
$prophecy->getHeader('Last-Modified')->willReturn(["Wed, 25 Jul 2018 17:42:04 GMT"]);
$prophecy->getHeader('Link')->willReturn(['<http://www.w3.org/ns/ldp#Resource>;rel="type"', '<http://www.w3.org/ns/ldp#NonRDFSource>;rel="type"']);
$prophecy->getHeader('Content-Type')->willReturn(['text/plain']);
$prophecy->getHeader('Content-Length')->willReturn([strlen("DERP")]);
$response = $prophecy->reveal();
$fedora_prophecy->getResourceHeaders(Argument::any())->willReturn($response);
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(204);
$response = $prophecy->reveal();
$fedora_prophecy->deleteResource(Argument::any())->willReturn($response);
$api = $fedora_prophecy->reveal();
$mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)->reveal();
$adapter = new FedoraAdapter($api, $mime_guesser);
$this->assertTrue($adapter->rename('', '') == TRUE, "rename() must return TRUE on success");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::createDir
*/
public function testCreateDirFail() {
$prophecy = $this->prophesize(Response::class);
$prophecy->getStatusCode()->willReturn(500);
$fedora_prophecy = $this->prophesize(IFedoraApi::class);
$fedora_prophecy->saveResource('')->willReturn($prophecy->reveal());
$api = $fedora_prophecy->reveal();
$mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)->reveal();
$adapter = new FedoraAdapter($api, $mime_guesser);
$this->assertTrue($adapter->createDir('', $this->prophesize(Config::class)->reveal()) == FALSE, "createDir() must return FALSE on fail");
}
/**
* @covers \Drupal\islandora\Flysystem\Adapter\FedoraAdapter::createDir
*/
public function testCreateDir() {
$adapter = $this->createAdapterForCreateDir();
$metadata = $adapter->createDir('', $this->prophesize(Config::class)->reveal());
$this->assertDirMetadata($metadata);
}
}

62
tests/src/Kernel/FedoraPluginTest.php

@ -0,0 +1,62 @@
<?php
namespace Drupal\Tests\islandora\Kernel;
use Drupal\islandora\Flysystem\Fedora;
use League\Flysystem\AdapterInterface;
use Islandora\Chullo\IFedoraApi;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
/**
* Tests the Fedora plugin for Flysystem.
*
* @group islandora
* @coversDefaultClass \Drupal\islandora\Flysystem\Fedora
*/
class FedoraPluginTest extends IslandoraKernelTestBase {
/**
* Mocks up a plugin.
*/
protected function createPlugin($return_code) {
$prophecy = $this->prophesize(ResponseInterface::class);
$prophecy->getStatusCode()->willReturn($return_code);
$response = $prophecy->reveal();
$prophecy = $this->prophesize(IFedoraApi::class);
$prophecy->getResourceHeaders('')->willReturn($response);
$prophecy->getBaseUri()->willReturn("");
$api = $prophecy->reveal();
$mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)->reveal();
return new Fedora($api, $mime_guesser);
}
/**
* Tests the getAdapter() method.
*
* @covers \Drupal\islandora\Flysystem\Fedora::getAdapter
*/
public function testGetAdapter() {
$plugin = $this->createPlugin(200);
$adapter = $plugin->getAdapter();
$this->assertTrue($adapter instanceof AdapterInterface, "getAdapter() must return an AdapterInterface");
}
/**
* Tests the ensure() method.
*
* @covers \Drupal\islandora\Flysystem\Fedora::ensure
*/
public function testEnsure() {
$plugin = $this->createPlugin(200);
$this->assertTrue(empty($plugin->ensure()), "ensure() must return an empty array on success");
$plugin = $this->createPlugin(404);
$this->assertTrue(!empty($plugin->ensure()), "ensure() must return a non-empty array on fail");
}
}

1
tests/src/Kernel/IslandoraKernelTestBase.php

@ -37,6 +37,7 @@ abstract class IslandoraKernelTestBase extends KernelTestBase {
'image', 'image',
'media', 'media',
'islandora', 'islandora',
'flysystem',
]; ];
/** /**

Loading…
Cancel
Save