hyrax 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/.dassie/config/initializers/hyrax.rb +11 -1
  3. data/.gitignore +3 -0
  4. data/.regen +1 -1
  5. data/.rubocop_fixme.yml +3 -1
  6. data/Dockerfile +2 -1
  7. data/app/actors/hyrax/actors/file_actor.rb +6 -4
  8. data/app/actors/hyrax/actors/transfer_request_actor.rb +3 -7
  9. data/app/assets/javascripts/hyrax/analytics_events.js +8 -2
  10. data/app/assets/javascripts/hyrax/autocomplete/linked_data.es6 +1 -3
  11. data/app/controllers/concerns/hyrax/controller.rb +21 -0
  12. data/app/controllers/concerns/hyrax/works_controller_behavior.rb +83 -59
  13. data/app/controllers/hyrax/admin/admin_sets_controller.rb +105 -19
  14. data/app/controllers/hyrax/admin/permission_template_accesses_controller.rb +12 -19
  15. data/app/controllers/hyrax/batch_edits_controller.rb +12 -3
  16. data/app/controllers/hyrax/batch_uploads_controller.rb +4 -0
  17. data/app/controllers/hyrax/citations_controller.rb +1 -1
  18. data/app/controllers/hyrax/dashboard/collections_controller.rb +19 -10
  19. data/app/forms/hyrax/forms/administrative_set_form.rb +19 -1
  20. data/app/forms/hyrax/forms/batch_edit_form.rb +1 -1
  21. data/app/forms/hyrax/forms/dashboard/nest_collection_form.rb +21 -6
  22. data/app/forms/hyrax/forms/pcdm_collection_form.rb +1 -0
  23. data/app/forms/hyrax/forms/permission_template_form.rb +17 -9
  24. data/app/forms/hyrax/forms/resource_form.rb +9 -5
  25. data/app/helpers/hyrax/collections_helper.rb +14 -0
  26. data/app/helpers/hyrax/membership_helper.rb +1 -1
  27. data/app/helpers/hyrax/trophy_helper.rb +1 -1
  28. data/app/helpers/hyrax/url_helper.rb +1 -1
  29. data/app/indexers/hyrax/administrative_set_indexer.rb +8 -2
  30. data/app/indexers/hyrax/deep_indexing_service.rb +1 -1
  31. data/app/indexers/hyrax/file_set_indexer.rb +1 -0
  32. data/app/indexers/hyrax/pcdm_collection_indexer.rb +3 -1
  33. data/app/indexers/hyrax/thumbnail_indexer.rb +31 -0
  34. data/app/indexers/hyrax/valkyrie_file_set_indexer.rb +6 -6
  35. data/app/indexers/hyrax/valkyrie_indexer.rb +4 -2
  36. data/app/indexers/hyrax/valkyrie_work_indexer.rb +13 -0
  37. data/app/inputs/controlled_vocabulary_input.rb +2 -0
  38. data/app/jobs/change_depositor_event_job.rb +47 -0
  39. data/app/jobs/characterize_job.rb +38 -2
  40. data/app/jobs/concerns/hyrax/members_permission_job_behavior.rb +1 -1
  41. data/app/jobs/content_depositor_change_event_job.rb +2 -1
  42. data/app/jobs/hyrax/propagate_change_depositor_job.rb +32 -0
  43. data/app/jobs/inherit_permissions_job.rb +1 -1
  44. data/app/jobs/valkyrie_create_derivatives_job.rb +25 -0
  45. data/app/jobs/valkyrie_ingest_job.rb +84 -16
  46. data/app/models/admin_set.rb +2 -2
  47. data/app/models/collection_branding_info.rb +8 -6
  48. data/app/models/concerns/hyrax/collection_behavior.rb +2 -2
  49. data/app/models/concerns/hyrax/file_set/characterization.rb +7 -1
  50. data/app/models/concerns/hyrax/solr_document/metadata.rb +1 -0
  51. data/app/models/concerns/hyrax/solr_document_behavior.rb +9 -3
  52. data/app/models/hyrax/administrative_set.rb +36 -1
  53. data/app/models/hyrax/collection_type.rb +2 -2
  54. data/app/models/hyrax/file_metadata.rb +5 -1
  55. data/app/models/hyrax/file_set.rb +42 -1
  56. data/app/models/hyrax/pcdm_collection.rb +56 -0
  57. data/app/models/hyrax/permission_template.rb +11 -5
  58. data/app/models/hyrax/work.rb +91 -0
  59. data/app/models/proxy_deposit_request.rb +1 -1
  60. data/app/presenters/hyrax/admin_set_presenter.rb +2 -2
  61. data/app/presenters/hyrax/pcdm_member_presenter_factory.rb +2 -2
  62. data/app/presenters/hyrax/work_show_presenter.rb +7 -3
  63. data/app/search_builders/hyrax/dashboard/collections_search_builder.rb +2 -2
  64. data/app/search_builders/hyrax/dashboard/managed_search_filters.rb +44 -4
  65. data/app/search_builders/hyrax/dashboard/nested_collections_search_builder.rb +2 -2
  66. data/app/search_builders/hyrax/my/collections_search_builder.rb +11 -4
  67. data/app/services/hyrax/access_control_list.rb +13 -0
  68. data/app/services/hyrax/admin_set_create_service.rb +21 -37
  69. data/app/services/hyrax/change_content_depositor_service.rb +2 -2
  70. data/app/services/hyrax/change_depositor_service.rb +70 -0
  71. data/app/services/hyrax/characterization/valkyrie_characterization_service.rb +1 -1
  72. data/app/services/hyrax/collections/nested_collection_query_service.rb +23 -11
  73. data/app/services/hyrax/custom_queries/navigators/child_file_sets_navigator.rb +45 -0
  74. data/app/services/hyrax/custom_queries/navigators/child_filesets_navigator.rb +7 -2
  75. data/app/services/hyrax/custom_queries/navigators/parent_work_navigator.rb +54 -0
  76. data/app/services/hyrax/default_middleware_stack.rb +3 -0
  77. data/app/services/hyrax/file_set_derivatives_service.rb +21 -2
  78. data/app/services/hyrax/file_set_type_service.rb +2 -5
  79. data/app/services/hyrax/listeners/file_metadata_listener.rb +20 -1
  80. data/app/services/hyrax/listeners/member_cleanup_listener.rb +23 -3
  81. data/app/services/hyrax/listeners/metadata_index_listener.rb +39 -0
  82. data/app/services/hyrax/listeners/proxy_deposit_listener.rb +14 -8
  83. data/app/services/hyrax/location_service.rb +33 -0
  84. data/app/services/hyrax/multiple_membership_checker.rb +44 -1
  85. data/app/services/hyrax/resource_visibility_propagator.rb +1 -1
  86. data/app/services/hyrax/simple_schema_loader.rb +5 -1
  87. data/app/services/hyrax/solr_query_service.rb +12 -7
  88. data/app/services/hyrax/thumbnail_path_service.rb +1 -1
  89. data/app/services/hyrax/work_uploads_handler.rb +0 -10
  90. data/app/validators/hyrax/collection_membership_validator.rb +38 -0
  91. data/app/views/catalog/_index_header_list_hyrax_pcdm_collection.html.erb +4 -0
  92. data/app/views/hyrax/admin/admin_sets/_form_participant_table.html.erb +2 -2
  93. data/app/views/hyrax/admin/admin_sets/_form_participants.html.erb +2 -2
  94. data/app/views/hyrax/admin/admin_sets/_form_visibility.html.erb +2 -2
  95. data/app/views/hyrax/admin/admin_sets/_form_workflow.erb +1 -1
  96. data/app/views/hyrax/admin/collection_types/index.html.erb +1 -1
  97. data/app/views/hyrax/base/_form.html.erb +1 -1
  98. data/app/views/hyrax/base/_form_child_work_relationships.html.erb +1 -1
  99. data/app/views/hyrax/dashboard/collections/_default_group.html.erb +2 -2
  100. data/app/views/hyrax/dashboard/collections/_form.html.erb +21 -15
  101. data/app/views/hyrax/dashboard/collections/_form_discovery.html.erb +6 -3
  102. data/app/views/hyrax/dashboard/collections/_form_share.html.erb +2 -2
  103. data/app/views/hyrax/dashboard/collections/_form_share_table.html.erb +3 -3
  104. data/app/views/hyrax/dashboard/collections/_list_collections.html.erb +2 -2
  105. data/app/views/hyrax/dashboard/works/_default_group.html.erb +1 -1
  106. data/app/views/hyrax/dashboard/works/_list_works.html.erb +1 -1
  107. data/app/views/hyrax/file_sets/_actions.html.erb +2 -2
  108. data/app/views/hyrax/my/_work_action_menu.html.erb +8 -9
  109. data/app/views/hyrax/my/collections/_default_group.html.erb +2 -2
  110. data/app/views/hyrax/my/collections/_list_collections.html.erb +2 -2
  111. data/app/views/hyrax/my/collections/index.html.erb +3 -2
  112. data/app/views/hyrax/my/works/_default_group.html.erb +1 -1
  113. data/app/views/hyrax/my/works/_list_works.html.erb +1 -2
  114. data/app/views/hyrax/my/works/index.html.erb +4 -2
  115. data/chart/hyrax/Chart.yaml +2 -2
  116. data/chart/hyrax/README.md +22 -1
  117. data/config/initializers/listeners.rb +0 -1
  118. data/config/locales/hyrax.de.yml +6 -5
  119. data/config/locales/hyrax.en.yml +30 -28
  120. data/config/locales/hyrax.es.yml +10 -9
  121. data/config/locales/hyrax.fr.yml +2 -1
  122. data/config/locales/hyrax.it.yml +3 -2
  123. data/config/locales/hyrax.pt-BR.yml +2 -1
  124. data/config/locales/hyrax.zh.yml +2 -1
  125. data/config/metadata/basic_metadata.yaml +2 -0
  126. data/config/metadata/core_metadata.yaml +1 -1
  127. data/docker-compose.yml +46 -42
  128. data/documentation/developing-your-hyrax-based-app.md +1 -1
  129. data/documentation/legacyREADME.md +1 -1
  130. data/lib/hyrax/administrative_set_name.rb +18 -0
  131. data/lib/hyrax/collection_name.rb +2 -0
  132. data/lib/hyrax/configuration.rb +10 -0
  133. data/lib/hyrax/controlled_vocabularies/location.rb +9 -2
  134. data/lib/hyrax/controlled_vocabularies/resource_label_caching.rb +42 -0
  135. data/lib/hyrax/controlled_vocabularies.rb +1 -0
  136. data/lib/hyrax/publisher.rb +45 -0
  137. data/lib/hyrax/specs/capybara.rb +1 -1
  138. data/lib/hyrax/specs/shared_specs/hydra_works.rb +11 -4
  139. data/lib/hyrax/specs/shared_specs/indexers.rb +117 -3
  140. data/lib/hyrax/transactions/admin_set_create.rb +2 -1
  141. data/lib/hyrax/transactions/admin_set_destroy.rb +22 -0
  142. data/lib/hyrax/transactions/admin_set_update.rb +21 -0
  143. data/lib/hyrax/transactions/collection_destroy.rb +22 -0
  144. data/lib/hyrax/transactions/collection_update.rb +3 -2
  145. data/lib/hyrax/transactions/container.rb +87 -23
  146. data/lib/hyrax/transactions/create_work.rb +3 -0
  147. data/lib/hyrax/transactions/destroy_work.rb +3 -0
  148. data/lib/hyrax/transactions/steps/apply_collection_permission_template.rb +2 -0
  149. data/lib/hyrax/transactions/steps/apply_permission_template.rb +2 -0
  150. data/lib/hyrax/transactions/steps/apply_visibility.rb +2 -0
  151. data/lib/hyrax/transactions/steps/change_depositor.rb +46 -0
  152. data/lib/hyrax/transactions/steps/check_for_empty_admin_set.rb +36 -0
  153. data/lib/hyrax/transactions/steps/delete_access_control.rb +32 -0
  154. data/lib/hyrax/transactions/steps/delete_resource.rb +19 -3
  155. data/lib/hyrax/transactions/steps/destroy_work.rb +3 -1
  156. data/lib/hyrax/transactions/steps/ensure_permission_template.rb +2 -0
  157. data/lib/hyrax/transactions/steps/save.rb +24 -6
  158. data/lib/hyrax/transactions/steps/save_access_control.rb +2 -2
  159. data/lib/hyrax/transactions/steps/save_work.rb +3 -0
  160. data/lib/hyrax/transactions/steps/set_user_as_creator.rb +41 -0
  161. data/lib/hyrax/transactions/steps/update_work_members.rb +51 -0
  162. data/lib/hyrax/transactions/update_work.rb +4 -3
  163. data/lib/hyrax/transactions/work_create.rb +1 -1
  164. data/lib/hyrax/transactions/work_destroy.rb +2 -1
  165. data/lib/hyrax/transactions/work_update.rb +19 -0
  166. data/lib/hyrax/version.rb +1 -1
  167. data/lib/wings/attribute_transformer.rb +5 -1
  168. data/lib/wings/setup.rb +3 -1
  169. data/lib/wings/valkyrie/query_service.rb +2 -1
  170. data/lib/wings/valkyrie/storage.rb +7 -1
  171. data/template.rb +1 -1
  172. metadata +24 -3
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+ # Log work depositor change to activity streams
3
+ #
4
+ # This class simply logs the transfer, pulling data from the object that was
5
+ # just transferred. It does not perform the transfer.
6
+ class ChangeDepositorEventJob < ContentEventJob
7
+ include Rails.application.routes.url_helpers
8
+ include ActionDispatch::Routing::PolymorphicRoutes
9
+
10
+ # @param [ActiveFedora::Base, Hyrax::Work] work the work that's been transfered
11
+ def perform(work)
12
+ # these get set to repo_object and depositor
13
+ super(work, new_owner(work))
14
+ end
15
+
16
+ def action
17
+ "User #{link_to_profile repo_object.proxy_depositor} has transferred #{link_to_work repo_object.title.first} to user #{link_to_profile depositor}"
18
+ end
19
+
20
+ def link_to_work(text)
21
+ link_to text, polymorphic_path(repo_object)
22
+ end
23
+
24
+ # Log the event to the work's stream
25
+ def log_work_event(work)
26
+ work.log_event(event)
27
+ end
28
+ alias log_file_set_event log_work_event
29
+
30
+ # overriding default to log the event to the depositor instead of their profile
31
+ # and to log the event for both users
32
+ def log_user_event(depositor)
33
+ previous_owner.log_profile_event(event)
34
+ depositor.log_event(event)
35
+ end
36
+
37
+ private
38
+
39
+ def previous_owner
40
+ ::User.find_by_user_key(repo_object.proxy_depositor)
41
+ end
42
+
43
+ # used for @depositor
44
+ def new_owner(work)
45
+ ::User.find_by_user_key(work.depositor)
46
+ end
47
+ end
@@ -36,12 +36,48 @@ class CharacterizeJob < Hyrax::ApplicationJob
36
36
 
37
37
  private
38
38
 
39
- def characterize(file_set, _file_id, filepath)
39
+ def characterize(file_set, _file_id, filepath) # rubocop:disable Metrics/AbcSize
40
+ # store this so we can tell if the original_file is actually changing
41
+ previous_checksum = file_set.characterization_proxy.original_checksum.first
42
+
43
+ clear_metadata(file_set)
44
+
45
+ # If the current FileSet title is the same as the label, it must be a filename as opposed to a user-entered...
46
+ # value. So later we'll ensure it's set to the new file's filename.
47
+ reset_title = file_set.title.first == file_set.label
48
+
40
49
  characterization_service.run(file_set.characterization_proxy, filepath)
41
50
  Rails.logger.debug "Ran characterization on #{file_set.characterization_proxy.id} (#{file_set.characterization_proxy.mime_type})"
42
51
  file_set.characterization_proxy.alpha_channels = channels(filepath) if file_set.image? && Hyrax.config.iiif_image_server?
43
52
  file_set.characterization_proxy.save!
44
- file_set.update_index
53
+
54
+ # Ensure that if the actual file content has changed, the mod timestamp on the FileSet object changes.
55
+ # Otherwise this does not happen when rolling back to a previous version. Perhaps this should be set as part of...
56
+ # `FileActor.revert_to` (or its replacement Transaction?!), where the FileSet is saved. Not sure if the...
57
+ # before/after checksum is readily available there though. I like this checksum verification because it allows...
58
+ # all changes to the current FileSet version to be detected, which in our case triggers re-creation of a...
59
+ # "cold storage" archive of the parent Work. It's worth noting that adding a *new* version always touches this...
60
+ # mod time. This is done in the versioning code.
61
+ file_set.date_modified = Hyrax::TimeService.time_in_utc if file_set.characterization_proxy.original_checksum.first != previous_checksum
62
+
63
+ # set title to label if that's how it was before this characterization
64
+ file_set.title = [file_set.characterization_proxy.original_name] if reset_title
65
+ # always set the label to the original_name
66
+ file_set.label = file_set.characterization_proxy.original_name
67
+
68
+ file_set.save!
69
+ end
70
+
71
+ def clear_metadata(file_set)
72
+ # The characterization of additional file versions adds new height/width/size/checksum values to un-orderable...
73
+ # `ActiveTriples::Relation` fields on `original_file`. Values from those are then randomly pulled into Solr...
74
+ # fields which may have scalar or vector cardinality. So for height/width you get two scalar values pulled from...
75
+ # "randomized parallel arrays". Upshot is to reset all of these before (re)characterization to stop the mayhem.
76
+ file_set.characterization_proxy.height = []
77
+ file_set.characterization_proxy.width = []
78
+ file_set.characterization_proxy.original_checksum = []
79
+ file_set.characterization_proxy.file_size = []
80
+ file_set.characterization_proxy.format_label = []
45
81
  end
46
82
 
47
83
  def channels(filepath)
@@ -17,7 +17,7 @@ module Hyrax
17
17
  when ActiveFedora::Base
18
18
  ::FileSet.search_with_conditions(id: work.member_ids).map(&:id)
19
19
  when Valkyrie::Resource
20
- Hyrax.custom_queries.find_child_fileset_ids(resource: work)
20
+ Hyrax.custom_queries.find_child_file_set_ids(resource: work)
21
21
  end
22
22
  end
23
23
 
@@ -8,10 +8,11 @@ class ContentDepositorChangeEventJob < ContentEventJob
8
8
 
9
9
  attr_accessor :reset
10
10
 
11
- # @param [ActiveFedora::Base] work the work to be transfered
11
+ # @param [ActiveFedora::Base, Hyrax::Work] work the work to be transferred
12
12
  # @param [User] user the user the work is being transfered to.
13
13
  # @param [TrueClass,FalseClass] reset (false) if true, reset the access controls. This revokes edit access from the depositor
14
14
  def perform(work, user, reset = false)
15
+ Deprecation.warn("This class will be removed in the next major release. Use Hyrax::ChangeDepositorService.call instead.")
15
16
  @reset = reset
16
17
  super(work, user)
17
18
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ # updates depositor on file sets and resets permissions if flagged. Used by
4
+ # ChangeDepositorService to background changes to lots of file sets
5
+ class PropagateChangeDepositorJob < ApplicationJob
6
+ # @param work_id [Valkyrie::Id, String] the id of the work
7
+ # that is receiving a change of depositor
8
+ # @param user [User] the user that will "become" the depositor of
9
+ # the given work
10
+ # @param reset [TrueClass, FalseClass] when true, first clear
11
+ # permissions for the given work and contained file
12
+ # sets; regardless of true/false make the given user
13
+ # the depositor of the given work
14
+ def perform(work_id, user, reset)
15
+ work = Hyrax.query_service.find_by(id: work_id)
16
+ Hyrax.custom_queries.find_child_file_sets(resource: work).each do |f|
17
+ if reset
18
+ f.permission_manager.acl.permissions = []
19
+ f.permission_manager.acl.save
20
+ end
21
+ apply_depositor_metadata(f, user)
22
+ Hyrax.persister.save(resource: f)
23
+ end
24
+ end
25
+
26
+ def apply_depositor_metadata(resource, depositor)
27
+ depositor_id = depositor.respond_to?(:user_key) ? depositor.user_key : depositor
28
+ resource.depositor = depositor_id if resource.respond_to? :depositor=
29
+ Hyrax::AccessControlList.new(resource: resource).grant(:edit).to(::User.find_by_user_key(depositor_id)).save
30
+ end
31
+ end
32
+ end
@@ -24,7 +24,7 @@ class InheritPermissionsJob < Hyrax::ApplicationJob
24
24
  # @param work [Resource]
25
25
  # @return [Array<Hyrax::File_Set>]
26
26
  def file_sets_for(work)
27
- Hyrax.custom_queries.find_child_filesets(resource: work)
27
+ Hyrax.custom_queries.find_child_file_sets(resource: work)
28
28
  end
29
29
 
30
30
  # Perform the copy from the work to the contained filesets
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ class ValkyrieCreateDerivativesJob < Hyrax::ApplicationJob
3
+ queue_as Hyrax.config.ingest_queue_name
4
+ def perform(_file_set_id, file_id, _filepath = nil)
5
+ file_metadata = Hyrax.custom_queries.find_file_metadata_by(id: file_id)
6
+ return if file_metadata.video? && !Hyrax.config.enable_ffmpeg
7
+ # Get file into a local path.
8
+ file = Hyrax.storage_adapter.find_by(id: file_metadata.file_identifier)
9
+ # Call derivatives with the file_set.
10
+ derivative_service = Hyrax::DerivativeService.for(file_metadata)
11
+ derivative_service.create_derivatives(file.disk_path)
12
+ # Trigger a reindex to get the thumbnail path.
13
+ Hyrax.publisher.publish('file.metadata.updated', metadata: file_metadata, user: nil)
14
+ end
15
+
16
+ private
17
+
18
+ def query_service
19
+ Hyrax.query_service
20
+ end
21
+
22
+ def storage_adapter
23
+ Hyrax.storage_adapter
24
+ end
25
+ end
@@ -1,56 +1,124 @@
1
1
  # frozen_string_literal: true
2
+
3
+ ##
4
+ # Ingests a {Hyrax::UploadedFile} as file member of a {Hyrax::FileSet}.
5
+ #
6
+ # The {Hyrax::UploadedFile} is passed into {#perform}, and should have a
7
+ # {Hyrax::UploadedFile#file_set_uri} identifying an existing {Hyrax::FileSet}.
2
8
  class ValkyrieIngestJob < Hyrax::ApplicationJob
3
9
  queue_as Hyrax.config.ingest_queue_name
4
10
 
5
11
  ##
6
- # @param [Valkyrie::StorageAdapter::StreamFile] file
7
- def perform(file)
8
- ingest(file: file)
12
+ # @param [Hyrax::UploadedFile] file
13
+ # @param [RDF::URI] pcdm_use is the use/type to apply to the created FileMetadata
14
+ # @see Hyrax::FileMetadata::Use
15
+ def perform(file, pcdm_use: Hyrax::FileMetadata::Use::ORIGINAL_FILE)
16
+ ingest(file: file, pcdm_use: pcdm_use)
9
17
  end
10
18
 
11
19
  ##
12
- # @param [Valkyrie::StorageAdapter::StreamFile] file
20
+ # @api private
13
21
  #
22
+ # @param [Hyrax::UploadedFile] file
23
+ # @param [RDF::URI] pcdm_use
24
+
14
25
  # @return [void]
15
- def ingest(file:)
16
- file_set = Hyrax.query_service.find_by(id: file.file_set_uri)
17
- updated_metadata = upload_file(file: file, file_set: file_set)
26
+ def ingest(file:, pcdm_use:)
27
+ file_set_uri = Valkyrie::ID.new(file.file_set_uri)
28
+ file_set = Hyrax.query_service.find_by(id: file_set_uri)
29
+
30
+ updated_metadata = upload_file(file: file, file_set: file_set, pcdm_use: pcdm_use)
18
31
 
19
- add_file_to_file_set(file_set: file_set, file_metadata: updated_metadata)
32
+ add_file_to_file_set(file_set: file_set,
33
+ file_metadata: updated_metadata,
34
+ user: file.user)
35
+
36
+ ValkyrieCreateDerivativesJob.perform_later(file_set.id.to_s, updated_metadata.id.to_s)
20
37
  end
21
38
 
22
39
  ##
23
- # @todo this should publish something to allow the fileset
24
- # to reindex its membership
40
+ # @api private
41
+ #
25
42
  # @param [Hyrax::FileSet] file_set the file set to add to
26
43
  # @param [Hyrax::FileMetadata] file_metadata the metadata object representing
27
44
  # the file to add
45
+ # @param [::User] user the user performing the add
28
46
  #
29
47
  # @return [Hyrax::FileSet] updated file set
30
- def add_file_to_file_set(file_set:, file_metadata:)
48
+ def add_file_to_file_set(file_set:, file_metadata:, user:)
31
49
  file_set.file_ids << file_metadata.id
50
+ set_file_use_ids(file_set, file_metadata)
51
+
32
52
  Hyrax.persister.save(resource: file_set)
53
+ Hyrax.publisher.publish('object.membership.updated', object: file_set, user: user)
33
54
  end
34
55
 
35
56
  ##
57
+ # @api private
58
+ #
36
59
  # @param [Hyrax::UploadedFile] file
37
60
  # @param [Hyrax::FileSet] file_set
61
+ # @param [RDF::URI] pcdm_use the use/type to apply to the created FileMetadata
38
62
  #
39
63
  # @return [Hyrax::FileMetadata] the metadata representing the uploaded file
40
- def upload_file(file:, file_set:)
64
+ def upload_file(file:, file_set:, pcdm_use:) # rubocop:disable Metrics/MethodLength
41
65
  carrier_wave_sanitized_file = file.uploader.file
66
+ # Pull file, since carrierwave files don't respond to a proper IO #read. See
67
+ # https://github.com/carrierwaveuploader/carrierwave/issues/1959
68
+ file_io = carrier_wave_sanitized_file.to_file
42
69
  uploaded = Hyrax.storage_adapter
43
70
  .upload(resource: file_set,
44
- file: carrier_wave_sanitized_file,
71
+ file: file_io,
45
72
  original_filename: carrier_wave_sanitized_file.original_filename)
46
73
 
47
- file_metadata = Hyrax.custom_queries.find_file_metadata_by(id: uploaded.id)
74
+ file_metadata = find_or_create_metadata(id: uploaded.id, file: carrier_wave_sanitized_file)
75
+
76
+ file_metadata.type << pcdm_use
48
77
  file_metadata.file_set_id = file.file_set_uri
49
78
  file_metadata.file_identifier = uploaded.id
50
79
  file_metadata.size = uploaded.size
51
80
 
52
- Hyrax.publisher.publish("object.file.uploaded", metadata: file_metadata)
81
+ saved_metadata = Hyrax.persister.save(resource: file_metadata)
82
+ Hyrax.publisher.publish("object.file.uploaded", metadata: saved_metadata)
83
+ file_io.close
84
+
85
+ if pcdm_use == Hyrax::FileMetadata::Use::ORIGINAL_FILE
86
+ # Set file set label.
87
+ reset_title = file_set.title.first == file_set.label
88
+ # set title to label if that's how it was before this characterization
89
+ file_set.title = file_metadata.original_filename if reset_title
90
+ # always set the label to the original_name
91
+ file_set.label = file_metadata.original_filename
92
+ end
93
+
94
+ saved_metadata
95
+ end
96
+
97
+ ##
98
+ # @api private
99
+ def find_or_create_metadata(id:, file:)
100
+ Hyrax.custom_queries.find_file_metadata_by(id: id)
101
+ rescue Valkyrie::Persistence::ObjectNotFoundError => e
102
+ Hyrax.logger.warn "Failed to find existing metadata for #{id}:"
103
+ Hyrax.logger.warn e.message
104
+ Hyrax.logger.warn "Creating Hyrax::FileMetadata now"
105
+ Hyrax::FileMetadata.for(file: file)
106
+ end
53
107
 
54
- file_metadata
108
+ ##
109
+ # @api private
110
+ def set_file_use_ids(file_set, file_metadata)
111
+ file_metadata.type.each do |type|
112
+ case type
113
+ when Hyrax::FileMetadata::Use::ORIGINAL_FILE
114
+ file_set.original_file_id = file_metadata.id
115
+ when Hyrax::FileMetadata::Use::THUMBNAIL
116
+ file_set.thumbnail_id = file_metadata.id
117
+ when Hyrax::FileMetadata::Use::EXTRACTED_TEXT
118
+ file_set.extracted_text_id = file_metadata.id
119
+ else
120
+ Rails.logger.warn "Unknown file use #{file_metadata.type} specified for #{file_metadata.file_identifier}"
121
+ end
122
+ end
55
123
  end
56
124
  end
@@ -87,12 +87,12 @@ class AdminSet < ActiveFedora::Base
87
87
  end
88
88
 
89
89
  ##
90
- # @deprecated use PermissionTemplate#reset_access_controls instead
90
+ # @deprecated use PermissionTemplate#reset_access_controls_for instead
91
91
  #
92
92
  # Calculate and update who should have edit access based on who
93
93
  # has "manage" access in the PermissionTemplateAccess
94
94
  def reset_access_controls!
95
- Deprecation.warn("reset_access_controls! is deprecated; use PermissionTemplate#reset_access_controls instead.")
95
+ Deprecation.warn("reset_access_controls! is deprecated; use PermissionTemplate#reset_access_controls_for instead.")
96
96
 
97
97
  permission_template.reset_access_controls_for(collection: self)
98
98
  end
@@ -14,16 +14,19 @@ class CollectionBrandingInfo < ApplicationRecord
14
14
  self.local_path = File.join(role, filename)
15
15
  end
16
16
 
17
- def save(file_location, copy_file = true)
17
+ def save(file_location, upload_file = true)
18
18
  filename = File.split(local_path).last
19
19
  role_and_filename = File.join(role, filename)
20
20
 
21
- storage.upload(resource: Hyrax::PcdmCollection.new(id: collection_id),
22
- file: File.open(file_location),
23
- original_filename: role_and_filename)
21
+ if upload_file
22
+ storage.upload(resource: Hyrax::PcdmCollection.new(id: collection_id),
23
+ file: File.open(file_location),
24
+ original_filename: role_and_filename)
25
+ end
26
+
24
27
  self.local_path = find_local_filename(collection_id, role, filename)
25
28
 
26
- FileUtils.remove_file(file_location) if File.exist?(file_location) && copy_file
29
+ FileUtils.remove_file(file_location) if File.exist?(file_location) && upload_file
27
30
  super()
28
31
  end
29
32
 
@@ -35,7 +38,6 @@ class CollectionBrandingInfo < ApplicationRecord
35
38
  else
36
39
  local_path
37
40
  end
38
-
39
41
  storage.delete(id: id)
40
42
  end
41
43
 
@@ -121,12 +121,12 @@ module Hyrax
121
121
  end
122
122
 
123
123
  ##
124
- # @deprecated use PermissionTemplate#reset_access_controls instead
124
+ # @deprecated use PermissionTemplate#reset_access_controls_for instead
125
125
  #
126
126
  # Calculate and update who should have read/edit access to the collections based on who
127
127
  # has access in PermissionTemplateAccess
128
128
  def reset_access_controls!
129
- Deprecation.warn("reset_access_controls! is deprecated; use PermissionTemplate#reset_access_controls instead.")
129
+ Deprecation.warn("reset_access_controls! is deprecated; use PermissionTemplate#reset_access_controls_for instead.")
130
130
 
131
131
  permission_template
132
132
  .reset_access_controls_for(collection: self, interpret_visibility: true)
@@ -29,7 +29,7 @@ module Hyrax
29
29
  :page_count, :file_title, :last_modified, :original_checksum,
30
30
  :duration, :sample_rate, :alpha_channels
31
31
  ]
32
- self.characterization_proxy = :original_file
32
+ self.characterization_proxy = Hyrax.config.characterization_proxy
33
33
 
34
34
  delegate(*characterization_terms, to: :characterization_proxy)
35
35
 
@@ -64,6 +64,12 @@ module Hyrax
64
64
  end
65
65
 
66
66
  ActiveFedora::WithMetadata::DefaultMetadataClassFactory.file_metadata_schemas << AlphaChannelsSchema
67
+
68
+ # Add file_set_id for Valkyrie support.
69
+ class FileSetIdSchema < ActiveTriples::Schema
70
+ property :file_set_id, predicate: ::RDF::URI.new('http://vocabulary.samvera.org/ns#fileSetId')
71
+ end
72
+ ActiveFedora::WithMetadata::DefaultMetadataClassFactory.file_metadata_schemas << FileSetIdSchema
67
73
  end
68
74
  end
69
75
  end
@@ -52,6 +52,7 @@ module Hyrax
52
52
  attribute :read_groups, Solr::Array, ::Ability.read_group_field
53
53
  attribute :collection_ids, Solr::Array, 'collection_ids_tesim'
54
54
  attribute :admin_set, Solr::Array, "admin_set_tesim"
55
+ attribute :admin_set_id, Solr::Array, "admin_set_id_ssim"
55
56
  attribute :member_ids, Solr::Array, "member_ids_ssim"
56
57
  attribute :member_of_collection_ids, Solr::Array, "member_of_collection_ids_ssim"
57
58
  attribute :member_of_collections, Solr::Array, "member_of_collections_ssim"
@@ -65,7 +65,7 @@ module Hyrax
65
65
  ##
66
66
  # @return [Boolean]
67
67
  def admin_set?
68
- hydra_model == ::AdminSet
68
+ hydra_model == Hyrax.config.admin_set_class
69
69
  end
70
70
 
71
71
  ##
@@ -75,9 +75,9 @@ module Hyrax
75
75
  end
76
76
 
77
77
  # Method to return the model
78
- def hydra_model(classifier: ActiveFedora.model_mapper)
78
+ def hydra_model(classifier: nil)
79
79
  first('has_model_ssim')&.safe_constantize ||
80
- classifier.classifier(self).best_model
80
+ model_classifier(classifier).classifier(self).best_model
81
81
  end
82
82
 
83
83
  def depositor(default = '')
@@ -107,5 +107,11 @@ module Hyrax
107
107
  def collection_type_gid
108
108
  first(Hyrax.config.collection_type_index_field)
109
109
  end
110
+
111
+ private
112
+
113
+ def model_classifier(classifier)
114
+ classifier || ActiveFedora.model_mapper
115
+ end
110
116
  end
111
117
  end
@@ -1,18 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_dependency 'hyrax/administrative_set_name'
4
+
3
5
  module Hyrax
4
6
  ##
5
7
  # Valkyrie model for Admin Set domain objects.
8
+ #
9
+ # ## Relationships
10
+ #
11
+ # ### Administrative Set and Work
12
+ #
13
+ # * Defined: The relationship is defined by the inverse relationship stored in the
14
+ # work's `:admin_set_id` attribute.
15
+ # * Tested: The work tests the relationship.
16
+ # * Administrative Set to Work: (1..m) An admin set can have many works.
17
+ #
18
+ # @example Get works in an admin set:
19
+ # works = Hyrax.query_service.find_inverse_references_by(id: admin_set.id, property: :admin_set_id)
20
+ #
21
+ # * Work to Administrative Set: (1..1) A work must be in one and only one admin set.
22
+ # * See Hyrax::Work for code to get and set the admin set for the work.
23
+ #
24
+ # @see Hyrax::Work
25
+ # @see Valkyrie query adapter's #find_inverse_references_by
26
+ #
6
27
  class AdministrativeSet < Hyrax::Resource
7
28
  include Hyrax::Schema(:core_metadata)
8
29
 
9
30
  attribute :alternative_title, Valkyrie::Types::Set.of(Valkyrie::Types::String)
10
31
  attribute :creator, Valkyrie::Types::Set.of(Valkyrie::Types::String)
11
- attribute :description, Valkyrie::Types::Set.of(Valkyrie::Types::String)
32
+ attribute :description, Valkyrie::Types::String
33
+
34
+ ##
35
+ # @return [Boolean] true
36
+ def collection?
37
+ true
38
+ end
12
39
 
13
40
  def collection_type_gid
14
41
  # allow AdministrativeSet to behave more like a regular PcdmCollection
15
42
  Hyrax::CollectionType.find_or_create_admin_set_type.to_global_id
16
43
  end
44
+
45
+ ##
46
+ # @api private
47
+ #
48
+ # @return [Class] an ActiveModel::Name compatible class
49
+ def self._hyrax_default_name_class
50
+ Hyrax::AdministrativeSetName
51
+ end
17
52
  end
18
53
  end
@@ -109,9 +109,9 @@ module Hyrax
109
109
 
110
110
  ##
111
111
  # @return [Enumerable<Collection, PcdmCollection>]
112
- def collections(use_valkyrie: false)
112
+ def collections(use_valkyrie: Hyrax.config.use_valkyrie?)
113
113
  return [] unless id
114
- return Hyrax.custom_queries.find_collections_by_type(global_id: to_global_id) if use_valkyrie
114
+ return Hyrax.custom_queries.find_collections_by_type(global_id: to_global_id.to_s) if use_valkyrie
115
115
  ActiveFedora::Base.where(Hyrax.config.collection_type_index_field.to_sym => to_global_id.to_s)
116
116
  end
117
117
 
@@ -2,6 +2,10 @@
2
2
 
3
3
  module Hyrax
4
4
  class FileMetadata < Valkyrie::Resource
5
+ # Include mime-types for Hydra Derivatives mime-type checking. We may want
6
+ # to move this logic someday.
7
+ include Hydra::Works::MimeTypes
8
+
5
9
  GENERIC_MIME_TYPE = 'application/octet-stream'
6
10
 
7
11
  ##
@@ -42,7 +46,7 @@ module Hyrax
42
46
  attribute :label, ::Valkyrie::Types::Set
43
47
  attribute :original_filename, ::Valkyrie::Types::String
44
48
  attribute :mime_type, ::Valkyrie::Types::String.default(GENERIC_MIME_TYPE)
45
- attribute :type, ::Valkyrie::Types::Set.default([Use::ORIGINAL_FILE].freeze)
49
+ attribute :type, ::Valkyrie::Types::Set.default([Use::ORIGINAL_FILE])
46
50
 
47
51
  # attributes set by fits
48
52
  attribute :format_label, ::Valkyrie::Types::Set
@@ -4,6 +4,30 @@ module Hyrax
4
4
  ##
5
5
  # Valkyrie model for `FileSet` domain objects in the Hydra Works model.
6
6
  #
7
+ # ## Relationships
8
+ #
9
+ # ### File Set and Work
10
+ #
11
+ # * Defined: The relationship is defined by the inverse relationship stored in the
12
+ # work's `:member_ids` attribute.
13
+ # * Tested: The work tests the relationship.
14
+ # * File Set to Work: (1..1) A file set must be in one and only one work.
15
+ #
16
+ # @example Get work for a file set:
17
+ # work = Hyrax.custom_queries.find_parent_work(resource: file_set)
18
+ #
19
+ # * Work to File Set: (0..m) A work can have many file sets.
20
+ # * See Hyrax::Work for code to get and set file sets for the work.
21
+ #
22
+ # ### File Set and File (TBD)
23
+ #
24
+ # @see Hyrax::Work
25
+ # @see Hyrax::CustomQueries::Navigators::ParentWorkNavigator#find_parent_work
26
+ #
27
+ # @todo The description in Hydra::Works Shared Modeling is out of date and uses
28
+ # terminology to describe the relationships that is no longer used in code.
29
+ # Update the model and link to it. This can be a simple relationship diagram
30
+ # with a link to the original Works Shared Modeling for historical perspective.
7
31
  # @see https://wiki.duraspace.org/display/samvera/Hydra%3A%3AWorks+Shared+Modeling
8
32
  class FileSet < Hyrax::Resource
9
33
  include Hyrax::Schema(:core_metadata)
@@ -15,11 +39,28 @@ module Hyrax
15
39
  end
16
40
  end
17
41
 
42
+ class_attribute :characterization_proxy
43
+ self.characterization_proxy = Hyrax.config.characterization_proxy
44
+
18
45
  attribute :file_ids, Valkyrie::Types::Array.of(Valkyrie::Types::ID) # id for FileMetadata resources
46
+ attribute :thumbnail_id, Valkyrie::Types::ID.optional # id for FileMetadata resource
19
47
  attribute :original_file_id, Valkyrie::Types::ID # id for FileMetadata resource
20
- attribute :thumbnail_id, Valkyrie::Types::ID # id for FileMetadata resource
21
48
  attribute :extracted_text_id, Valkyrie::Types::ID # id for FileMetadata resource
22
49
 
50
+ ##
51
+ # @return [Valkyrie::ID]
52
+ def representative_id
53
+ id
54
+ end
55
+
56
+ ##
57
+ # @return [Valkyrie::ID]
58
+ # If one is set then return it, otherwise use self as the ID to allow for
59
+ # derivative generators to find the on-disk path for the thumbnail.
60
+ def thumbnail_id
61
+ self.[](:thumbnail_id) || id
62
+ end
63
+
23
64
  ##
24
65
  # @return [Boolean] true
25
66
  def pcdm_object?