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
@@ -12,11 +12,11 @@ module Hyrax
12
12
  attr_accessor :parents, :pathnames, :ancestors, :depth, :id
13
13
 
14
14
  def initialize(id:, scope:)
15
- query_builder = Hyrax::CollectionSearchBuilder.new(scope).where(id: id)
15
+ query_builder = Hyrax::CollectionSearchBuilder.new(scope).where(id: id.to_s)
16
16
  query = Hyrax::Collections::NestedCollectionQueryService.clean_lucene_error(builder: query_builder)
17
17
  response = scope.repository.search(query)
18
18
  collection_doc = response.documents.first
19
- @id = id
19
+ @id = id.to_s
20
20
  @parents = collection_doc[Samvera::NestingIndexer.configuration.solr_field_name_for_storing_parent_ids]
21
21
  @pathnames = collection_doc[Samvera::NestingIndexer.configuration.solr_field_name_for_storing_pathnames]
22
22
  @ancestors = collection_doc[Samvera::NestingIndexer.configuration.solr_field_name_for_storing_ancestors]
@@ -34,7 +34,7 @@ module Hyrax
34
34
  # @param limit_to_id [nil, String] Limit the query to just check if the given id is in the response. Useful for validation.
35
35
  # @return [Array<SolrDocument>]
36
36
  def self.available_child_collections(parent:, scope:, limit_to_id: nil)
37
- return [] unless parent.try(:nestable?)
37
+ return [] unless nestable?(collection: parent)
38
38
  return [] unless scope.can?(:deposit, parent)
39
39
  query_solr(collection: parent, access: :read, scope: scope, limit_to_id: limit_to_id, nest_direction: :as_child).documents
40
40
  end
@@ -52,7 +52,7 @@ module Hyrax
52
52
  #
53
53
  # @return [Array<SolrDocument>]
54
54
  def self.available_parent_collections(child:, scope:, limit_to_id: nil)
55
- return [] unless child.try(:nestable?)
55
+ return [] unless nestable?(collection: child)
56
56
  return [] unless scope.can?(:read, child)
57
57
  query_solr(collection: child, access: :deposit, scope: scope, limit_to_id: limit_to_id, nest_direction: :as_parent).documents
58
58
  end
@@ -69,7 +69,7 @@ module Hyrax
69
69
  #
70
70
  # @return [Blacklight::Solr::Response]
71
71
  def self.parent_collections(child:, scope:, page: 1)
72
- return [] unless child.try(:nestable?)
72
+ return [] unless nestable?(collection: child)
73
73
  query_builder = Hyrax::NestedCollectionsParentSearchBuilder.new(scope: scope, child: child, page: page)
74
74
  query = clean_lucene_error(builder: query_builder)
75
75
  scope.repository.search(query)
@@ -86,7 +86,7 @@ module Hyrax
86
86
  # id is in the response. Useful for validation.
87
87
  # @param nest_direction [Symbol] :as_child or :as_parent
88
88
  def self.query_solr(collection:, access:, scope:, limit_to_id:, nest_direction:)
89
- nesting_attributes = NestingAttributes.new(id: collection.id, scope: scope)
89
+ nesting_attributes = NestingAttributes.new(id: collection.id.to_s, scope: scope)
90
90
  query_builder = Hyrax::Dashboard::NestedCollectionsSearchBuilder.new(
91
91
  access: access,
92
92
  collection: collection,
@@ -95,7 +95,7 @@ module Hyrax
95
95
  nest_direction: nest_direction
96
96
  )
97
97
 
98
- query_builder.where(id: limit_to_id) if limit_to_id
98
+ query_builder.where(id: limit_to_id.to_s) if limit_to_id
99
99
  query = clean_lucene_error(builder: query_builder)
100
100
  scope.repository.search(query)
101
101
  end
@@ -139,8 +139,8 @@ module Hyrax
139
139
  def self.parent_and_child_can_nest?(parent:, child:, scope:)
140
140
  return false if parent == child # Short-circuit
141
141
  return false unless parent.collection_type_gid == child.collection_type_gid
142
- return false if available_parent_collections(child: child, scope: scope, limit_to_id: parent.id).none?
143
- return false if available_child_collections(parent: parent, scope: scope, limit_to_id: child.id).none?
142
+ return false if available_parent_collections(child: child, scope: scope, limit_to_id: parent.id.to_s).none?
143
+ return false if available_child_collections(parent: parent, scope: scope, limit_to_id: child.id.to_s).none?
144
144
  true
145
145
  end
146
146
 
@@ -190,7 +190,7 @@ module Hyrax
190
190
  descendant_depth = response[Samvera::NestingIndexer.configuration.solr_field_name_for_deepest_nested_depth]
191
191
 
192
192
  # => 2) Then we get the stored depth of the child collection itself to eliminate the collections above this one from our count, and add 1 to add back in this collection itself
193
- child_depth = NestingAttributes.new(id: child.id, scope: scope).depth
193
+ child_depth = NestingAttributes.new(id: child.id.to_s, scope: scope).depth
194
194
  nesting_depth = descendant_depth - child_depth + 1
195
195
 
196
196
  # this should always be positive, but just being safe
@@ -207,9 +207,21 @@ module Hyrax
207
207
  # this collection (includes this collection)
208
208
  def self.parent_nesting_depth(parent:, scope:)
209
209
  return 1 if parent.nil?
210
- NestingAttributes.new(id: parent.id, scope: scope).depth
210
+ NestingAttributes.new(id: parent.id.to_s, scope: scope).depth
211
211
  end
212
212
  private_class_method :parent_nesting_depth
213
+
214
+ # @api private
215
+ #
216
+ # @param collection [Hyrax::PcdmCollection,::Collection]
217
+ # @return [Boolean] true if the collection is nestable; otherwise, false
218
+ def self.nestable?(collection:)
219
+ return false if collection.blank?
220
+ return collection.nestable? if collection.respond_to? :nestable?
221
+ collection_type = Hyrax::CollectionType.find_by_gid!(collection.collection_type_gid)
222
+ collection_type.nestable?
223
+ end
224
+ private_class_method :nestable?
213
225
  end
214
226
  end
215
227
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hyrax
4
+ module CustomQueries
5
+ module Navigators
6
+ ##
7
+ # Navigate from a resource to the child filesets in the resource.
8
+ #
9
+ # @see https://github.com/samvera/valkyrie/wiki/Queries#custom-queries
10
+ # @since 3.4.0
11
+ class ChildFileSetsNavigator
12
+ # Define the queries that can be fulfilled by this navigator.
13
+ def self.queries
14
+ [:find_child_file_sets, :find_child_file_set_ids]
15
+ end
16
+
17
+ attr_reader :query_service
18
+
19
+ def initialize(query_service:)
20
+ @query_service = query_service
21
+ end
22
+
23
+ ##
24
+ # Find child filesets of a given resource, and map to Valkyrie Resources
25
+ #
26
+ # @param [Valkyrie::Resource] resource
27
+ #
28
+ # @return [Array<Valkyrie::Resource>]
29
+ def find_child_file_sets(resource:)
30
+ query_service.find_members(resource: resource).select(&:file_set?)
31
+ end
32
+
33
+ ##
34
+ # Find the ids of child filesets of a given resource, and map to Valkyrie Resources IDs
35
+ #
36
+ # @param [Valkyrie::Resource] resource
37
+ #
38
+ # @return [Array<Valkyrie::ID>]
39
+ def find_child_file_set_ids(resource:)
40
+ find_child_file_sets(resource: resource).map(&:id)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -6,6 +6,7 @@ module Hyrax
6
6
  ##
7
7
  # Navigate from a resource to the child filesets in the resource.
8
8
  #
9
+ # @deprecated use Hyrax::CustomQueries::Navigators::ChildFileSetsNavigator instead
9
10
  # @see https://github.com/samvera/valkyrie/wiki/Queries#custom-queries
10
11
  # @since 3.0.0
11
12
  class ChildFilesetsNavigator
@@ -26,8 +27,10 @@ module Hyrax
26
27
  # @param [Valkyrie::Resource] resource
27
28
  #
28
29
  # @return [Array<Valkyrie::Resource>]
30
+ # @deprecated
29
31
  def find_child_filesets(resource:)
30
- query_service.find_members(resource: resource).select(&:file_set?)
32
+ Deprecation.warn("Custom query find_child_filesets is deprecated; use find_child_file_sets instead.")
33
+ Hyrax.custom_queries.find_child_file_sets(resource: resource)
31
34
  end
32
35
 
33
36
  ##
@@ -36,8 +39,10 @@ module Hyrax
36
39
  # @param [Valkyrie::Resource] resource
37
40
  #
38
41
  # @return [Array<Valkyrie::ID>]
42
+ # @deprecated
39
43
  def find_child_fileset_ids(resource:)
40
- find_child_filesets(resource: resource).map(&:id)
44
+ Deprecation.warn("Custom query find_child_fileset_ids is deprecated; use find_child_file_set_ids instead.")
45
+ Hyrax.custom_queries.find_child_file_set_ids(resource: resource)
41
46
  end
42
47
  end
43
48
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hyrax
4
+ module CustomQueries
5
+ module Navigators
6
+ ##
7
+ # Navigate from a resource to it's parent work.
8
+ #
9
+ # @see https://github.com/samvera/valkyrie/wiki/Queries#custom-queries
10
+ # @since 3.4.0
11
+ class ParentWorkNavigator
12
+ # Define the queries that can be fulfilled by this navigator.
13
+ def self.queries
14
+ [:find_parent_work, :find_parent_work_id]
15
+ end
16
+
17
+ attr_reader :query_service
18
+
19
+ def initialize(query_service:)
20
+ @query_service = query_service
21
+ end
22
+
23
+ ##
24
+ # Find parent work of a given resource, and map to Valkyrie Resources
25
+ # @note There should be only one parent resource. A warning is logged if
26
+ # more than one resource is found and the first of the resources is returned.
27
+ # @param [Valkyrie::Resource] resource
28
+ #
29
+ # @return [Array<Valkyrie::Resource>]
30
+ def find_parent_work(resource:)
31
+ results = Hyrax.query_service.find_inverse_references_by(resource: resource,
32
+ property: :member_ids).select(&:work?)
33
+ if results.count > 1
34
+ Hyrax.logger.warn("#{resource.work? ? 'Work' : 'File set'} " \
35
+ "#{resource.id} is in #{results.count} works when it " \
36
+ "should be in no more than one.")
37
+ end
38
+ results.first
39
+ end
40
+
41
+ ##
42
+ # Find the id of the parent work of a given resource, and map to Valkyrie Resources IDs
43
+ # @note There should be only one parent resource. A warning is logged if
44
+ # more than one resource is found and the first of the resources is returned.
45
+ # @param [Valkyrie::Resource] resource
46
+ #
47
+ # @return [Array<Valkyrie::ID>]
48
+ def find_parent_work_id(resource:)
49
+ find_parent_work(resource: resource)&.id
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -42,6 +42,9 @@ module Hyrax
42
42
  # Decode the private/public/institution on the form into permisisons on
43
43
  # the model
44
44
  middleware.use Hyrax::Actors::InterpretVisibilityActor
45
+ #
46
+ # Handles transfering ownership of works from one user to another
47
+ middleware.use Hyrax::Actors::TransferRequestActor
45
48
 
46
49
  # Copies default permissions from the PermissionTemplate to the work
47
50
  middleware.use Hyrax::Actors::ApplyPermissionTemplateActor
@@ -3,13 +3,22 @@ module Hyrax
3
3
  # Responsible for creating and cleaning up the derivatives of a file_set
4
4
  class FileSetDerivativesService
5
5
  attr_reader :file_set
6
- delegate :uri, :mime_type, to: :file_set
6
+ delegate :mime_type, to: :file_set
7
7
 
8
8
  # @param file_set [Hyrax::FileSet] At least for this class, it must have #uri and #mime_type
9
9
  def initialize(file_set)
10
10
  @file_set = file_set
11
11
  end
12
12
 
13
+ def uri
14
+ # If given a FileMetadata object, use its parent ID.
15
+ if file_set.respond_to?(:file_set_id)
16
+ file_set.file_set_id.to_s
17
+ else
18
+ file_set.uri
19
+ end
20
+ end
21
+
13
22
  def cleanup_derivatives
14
23
  derivative_path_factory.derivatives_for_reference(file_set).each do |path|
15
24
  FileUtils.rm_f(path)
@@ -33,12 +42,22 @@ module Hyrax
33
42
  # The destination_name parameter has to match up with the file parameter
34
43
  # passed to the DownloadsController
35
44
  def derivative_url(destination_name)
36
- path = derivative_path_factory.derivative_path_for_reference(file_set, destination_name)
45
+ path = derivative_path_factory.derivative_path_for_reference(derivative_url_target, destination_name)
37
46
  URI("file://#{path}").to_s
38
47
  end
39
48
 
40
49
  private
41
50
 
51
+ # If given a FileMetadata object pass the file_set_id for derivative URL
52
+ # creation.
53
+ def derivative_url_target
54
+ if file_set.try(:file_set_id)
55
+ file_set.file_set_id.to_s
56
+ else
57
+ file_set
58
+ end
59
+ end
60
+
42
61
  def supported_mime_types
43
62
  file_set.class.pdf_mime_types +
44
63
  file_set.class.office_document_mime_types +
@@ -17,13 +17,10 @@ module Hyrax
17
17
  attr_reader :file_set
18
18
 
19
19
  ##
20
- # @todo make `file_set_characterization_proxy` (or something better?)
21
- # application-level configuration.
22
- #
23
20
  # @param [Hyrax::FileSet] file_set
24
21
  # @param [Symbol] characterization_proxy defaults to the setting provided by
25
22
  # the application's ActiveFedora `FileSet` class.
26
- def initialize(file_set:, characterization_proxy: ::FileSet.characterization_proxy, query_service: Hyrax.custom_queries)
23
+ def initialize(file_set:, characterization_proxy: Hyrax.config.characterization_proxy, query_service: Hyrax.custom_queries)
27
24
  @file_set = file_set
28
25
  @proxy_use = Hyrax::FileMetadata::Use.uri_for(use: characterization_proxy)
29
26
  @queries = query_service
@@ -48,7 +45,7 @@ module Hyrax
48
45
  private
49
46
 
50
47
  def audio_types
51
- return ::FileSet.audio_mime_types if defined?(::FileSet)
48
+ return ::FileSet.audio_mime_types if defined?(::FileSet) && ::FileSet.respond_to?(:audio_mime_types)
52
49
  DEFAULT_AUDIO_TYPES
53
50
  end
54
51
  end
@@ -3,13 +3,32 @@
3
3
  module Hyrax
4
4
  module Listeners
5
5
  ##
6
- # Listens for events related to Hyrax::FileMetadata
6
+ # Listens for events related to {Hyrax::FileMetadata}
7
7
  class FileMetadataListener
8
+ ##
9
+ # Called when 'file.metadata.updated' event is published; reindexes a
10
+ # {Hyrax::FileSet} when a file claiming to be its `pcdm_use:OriginalFile`
11
+ #
12
+ # @param [Dry::Events::Event] event
13
+ # @return [void]
14
+ def on_file_metadata_updated(event)
15
+ return unless event[:metadata].original_file?
16
+
17
+ file_set = Hyrax.query_service.find_by(id: event[:metadata].file_set_id)
18
+ Hyrax.index_adapter.save(resource: file_set)
19
+ rescue Valkyrie::Persistence::ObjectNotFoundError => err
20
+ Hyrax.logger.warn "tried to index file with id #{event[:metadata].id} " \
21
+ "in response to an event of type #{event.id} but " \
22
+ "encountered an error #{err.message}. should this " \
23
+ "object be in a FileSet #{event[:metadata]}"
24
+ end
25
+
8
26
  ##
9
27
  # Called when 'object.file.uploaded' event is published
10
28
  # @param [Dry::Events::Event] event
11
29
  # @return [void]
12
30
  def on_object_file_uploaded(event)
31
+ # Run characterization
13
32
  Hyrax.config
14
33
  .characterization_service
15
34
  .run(metadata: event[:metadata], file: event[:metadata].file)
@@ -3,7 +3,7 @@
3
3
  module Hyrax
4
4
  module Listeners
5
5
  ##
6
- # Listens for object deleted events and cleans up associated members
6
+ # Listens for resource deleted events and cleans up associated members
7
7
  class MemberCleanupListener
8
8
  # Called when 'object.deleted' event is published
9
9
  # @param [Dry::Events::Event] event
@@ -12,11 +12,11 @@ module Hyrax
12
12
  return unless event.payload.key?(:object) # legacy callback
13
13
  return if event[:object].is_a?(ActiveFedora::Base) # handled by legacy code
14
14
 
15
- Hyrax.custom_queries.find_child_filesets(resource: event[:object]).each do |file_set|
15
+ Hyrax.custom_queries.find_child_file_sets(resource: event[:object]).each do |file_set|
16
16
  begin
17
17
  Hyrax.persister.delete(resource: file_set)
18
18
  Hyrax.publisher
19
- .publish('object.deleted', object: file_set, id: file_set.id, user: user)
19
+ .publish('object.deleted', object: file_set, id: file_set.id, user: event[:user])
20
20
  rescue StandardError # we don't uncaught errors looping filesets
21
21
  Hyrax.logger.warn "Failed to delete #{file_set.class}:#{file_set.id} " \
22
22
  "during cleanup for resource: #{event[:object]}. " \
@@ -24,6 +24,26 @@ module Hyrax
24
24
  end
25
25
  end
26
26
  end
27
+
28
+ # Called when 'collection.deleted' event is published
29
+ # @param [Dry::Events::Event] event
30
+ # @return [void]
31
+ def on_collection_deleted(event)
32
+ return unless event.payload.key?(:collection) # legacy callback
33
+ return if event[:collection].is_a?(ActiveFedora::Base) # handled by legacy code
34
+
35
+ Hyrax.custom_queries.find_members_of(collection: event[:collection]).each do |resource|
36
+ begin
37
+ resource.member_of_collection_ids -= [event[:collection].id]
38
+ Hyrax.persister.save(resource: resource)
39
+ Hyrax.publisher
40
+ .publish('collection.membership.updated', collection: event[:collection], user: event[:user])
41
+ rescue StandardError
42
+ Hyrax.logger.warn "Failed to remove collection reference from #{work.class}:#{work.id} " \
43
+ "during cleanup for collection: #{event[:collection]}. "
44
+ end
45
+ end
46
+ end
27
47
  end
28
48
  end
29
49
  end
@@ -22,6 +22,34 @@ module Hyrax
22
22
  Hyrax.index_adapter.save(resource: event[:collection])
23
23
  end
24
24
 
25
+ ##
26
+ # Re-index the resource.
27
+ #
28
+ # Called when 'file.metadata.updated' event is published
29
+ # @param [Dry::Events::Event] event
30
+ # @return [void]
31
+ def on_file_metadata_updated(event)
32
+ return unless resource? event[:metadata]
33
+ Hyrax.index_adapter.save(resource: event[:metadata])
34
+ end
35
+
36
+ ##
37
+ # Re-index the resource.
38
+ #
39
+ # Called when 'object.membership.updated' event is published
40
+ # @param [Dry::Events::Event] event
41
+ # @return [void]
42
+ def on_object_membership_updated(event)
43
+ resource = event.to_h.fetch(:object) { Hyrax.query_service.find_by(id: event[:object_id]) }
44
+ return unless resource?(resource)
45
+
46
+ Hyrax.index_adapter.save(resource: resource)
47
+ rescue Valkyrie::Persistence::ObjectNotFoundError => err
48
+ Hyrax.logger.error("Tried to index for an #{event.id} event with " \
49
+ "payload #{event.payload}, but failed due to error:\n"\
50
+ "\t#{err.message}")
51
+ end
52
+
25
53
  ##
26
54
  # Re-index the resource.
27
55
  #
@@ -44,6 +72,17 @@ module Hyrax
44
72
  Hyrax.index_adapter.delete(resource: event[:object])
45
73
  end
46
74
 
75
+ ##
76
+ # Remove the resource from the index.
77
+ #
78
+ # Called when 'collection.deleted' event is published
79
+ # @param [Dry::Events::Event] event
80
+ # @return [void]
81
+ def on_collection_deleted(event)
82
+ return unless resource?(event.payload[:collection])
83
+ Hyrax.index_adapter.delete(resource: event[:collection])
84
+ end
85
+
47
86
  private
48
87
 
49
88
  def resource?(resource)
@@ -3,19 +3,25 @@
3
3
  module Hyrax
4
4
  module Listeners
5
5
  ##
6
- # Listens for deposit events, and checks for proxy situations. When a user
6
+ # @deprecated transfer requests are now carried out synchronously during
7
+ # object save
8
+ #
9
+ ## Listens for deposit events, and checks for proxy situations. When a user
7
10
  # deposits an item `on_behalf_of` another, ensures transfer is handled.
8
11
  class ProxyDepositListener
9
12
  ##
10
13
  # Called when 'object.deposited' event is published
11
- # @param [Dry::Events::Event] event
14
+ # @param [Dry::Events::Event] _event
12
15
  # @return [void]
13
- def on_object_deposited(event)
14
- return if event[:object].try(:on_behalf_of).blank? ||
15
- (event[:object].on_behalf_of == event[:object].depositor)
16
-
17
- ContentDepositorChangeEventJob
18
- .perform_later(event[:object], ::User.find_by_user_key(event[:object].on_behalf_of))
16
+ def on_object_deposited(_event)
17
+ Deprecation.warn(
18
+ "The ProxyDepositListener was deprecated, effective immediately, in \
19
+ response to a difficult-to-diagnose race condition bug. This listener \
20
+ is now a no-op. To retain functionality ensure that \
21
+ DefaultMiddlewareStack is configured to use \
22
+ Hyrax::Actors::TransferRequestActor. To quiet this deprecation remove any \
23
+ local initializer configuration that subscribes this listener."
24
+ )
19
25
  end
20
26
  end
21
27
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ class LocationService < ::Qa::Authorities::Geonames
4
+ CACHE_KEY_PREFIX = 'hyrax_geonames_label-v1-'
5
+ CACHE_EXPIRATION = 1.week
6
+
7
+ def full_label(uri)
8
+ return if uri.blank?
9
+ id = extract_id uri
10
+ Rails.cache.fetch(cache_key(id), expires_in: CACHE_EXPIRATION) do
11
+ label.call(find(id))
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def extract_id(obj)
18
+ uri = case obj
19
+ when String
20
+ URI(obj)
21
+ when URI
22
+ obj
23
+ else
24
+ raise ArgumentError, "#{obj} is not a valid type"
25
+ end
26
+ uri.path.split('/').last
27
+ end
28
+
29
+ def cache_key(id)
30
+ "#{CACHE_KEY_PREFIX}#{id}"
31
+ end
32
+ end
33
+ end
@@ -11,6 +11,26 @@ module Hyrax
11
11
  @item = item
12
12
  end
13
13
 
14
+ # @api public
15
+ #
16
+ # Validate member_of_collection_ids does not have any membership conflicts
17
+ # based on the collection types of the members.
18
+ #
19
+ # Collections that have a collection type declaring
20
+ # `allow_multiple_membership` as `false` require that its members do not
21
+ # also belong to other collections of the same type.
22
+ #
23
+ # @return [True, String] true if no conflicts; otherwise, an error message string
24
+ def validate
25
+ return true if item.member_of_collection_ids.empty? || item.member_of_collection_ids.count <= 1
26
+ return true unless single_membership_collection_types_exist?
27
+
28
+ collections_to_check = filter_to_single_membership_collections(item.member_of_collection_ids)
29
+ problematic_collections = check_collections(collections_to_check)
30
+ errs = build_error_message(problematic_collections)
31
+ errs.presence || true
32
+ end
33
+
14
34
  # @api public
15
35
  #
16
36
  # Scan a list of collection_ids for multiple single-membership collections.
@@ -42,15 +62,18 @@ module Hyrax
42
62
 
43
63
  private
44
64
 
65
+ # @return [Boolean] true if there a any collection types that restrict membership
45
66
  def single_membership_collection_types_exist?
46
67
  single_membership_collection_types_gids.present?
47
68
  end
48
69
 
70
+ # @return [Array<String] global ids of collection types that restrict membership
49
71
  def single_membership_collection_types_gids
50
72
  @single_membership_collection_types_gids ||=
51
73
  Hyrax::CollectionType.gids_that_do_not_allow_multiple_membership&.map(&:to_s)
52
74
  end
53
75
 
76
+ # @return [Array<Hyrax::PcdmCollection>] list of collection instances for single membership collections
54
77
  def filter_to_single_membership_collections(collection_ids)
55
78
  return [] if collection_ids.blank?
56
79
  field_pairs = {
@@ -58,11 +81,14 @@ module Hyrax
58
81
  }
59
82
  Hyrax::SolrQueryService.new
60
83
  .with_generic_type(generic_type: "Collection")
61
- .with_ids(ids: Array[collection_ids])
84
+ .with_ids(ids: Array(collection_ids).map(&:to_s))
62
85
  .with_field_pairs(field_pairs: field_pairs, join_with: ' OR ')
63
86
  .get_objects(use_valkyrie: true).to_a
64
87
  end
65
88
 
89
+ # @param proposed [Array<Hyrax::PcdmCollection>] collections with restricted membership that are being added
90
+ # @param include_current_members [Boolean] true if current collections for item should also be checked
91
+ # @return [Array<Hyrax::PcdmCollection>] collections with restricted membership
66
92
  def collections_to_check(proposed, include_current_members)
67
93
  # ActorStack does a wholesale collection membership replacement, such that
68
94
  # proposed collections include existing and new collections. Parameter
@@ -72,6 +98,19 @@ module Hyrax
72
98
  proposed | filter_to_single_membership_collections(item.member_of_collection_ids)
73
99
  end
74
100
 
101
+ # @param collections_to_check [Array<Hyrax::PcdmCollection>] collections with restricted membership
102
+ # @return [Array<Array>] collections groups by collection type
103
+ # @example example return result
104
+ # [
105
+ # [
106
+ # <Collection(id: 1, collection_type_gid: 1, ...)>,
107
+ # <Collection(id: 4, collection_type_gid: 1, ...)>
108
+ # ],
109
+ # [
110
+ # <Collection(id: 13, collection_type_gid: 8, ...)>,
111
+ # <Collection(id: 26, collection_type_gid: 8, ...)>
112
+ # ]
113
+ # ]
75
114
  def check_collections(collections_to_check)
76
115
  # uniq insures we include a collection only once when it is in the list multiple
77
116
  # group_by groups collections of the same collection type together
@@ -82,6 +121,7 @@ module Hyrax
82
121
  .select { |_gid, list| list.count > 1 }
83
122
  end
84
123
 
124
+ # @return [nil, String] nil if no errors; otherwise, errors appended into a single human readable message
85
125
  def build_error_message(problematic_collections)
86
126
  return if problematic_collections.blank?
87
127
  error_message_clauses = problematic_collections.map do |gid, list|
@@ -93,10 +133,13 @@ module Hyrax
93
133
  "#{error_message_clauses.join('; ')}"
94
134
  end
95
135
 
136
+ # @return [String] title of the collection type
96
137
  def collection_type_title_from_gid(gid)
97
138
  Hyrax::CollectionType.find_by_gid(gid).title
98
139
  end
99
140
 
141
+ # @return [String] comma separated (with and before final) list of titles
142
+ # @example "Title 1, Title 2, and Title 3"
100
143
  def collection_titles_from_list(collection_list)
101
144
  collection_list.map do |collection|
102
145
  collection.title.first
@@ -39,7 +39,7 @@ module Hyrax
39
39
  #
40
40
  # @raise [RuntimeError] if visibility propagation fails
41
41
  def propagate
42
- queries.find_child_filesets(resource: source).each do |file_set|
42
+ queries.find_child_file_sets(resource: source).each do |file_set|
43
43
  file_set.visibility = source.visibility
44
44
  embargo_manager.copy_embargo_to(target: file_set)
45
45
  lease_manager.copy_lease_to(target: file_set)
@@ -76,7 +76,11 @@ module Hyrax
76
76
  ##
77
77
  # @return [Dry::Types::Type]
78
78
  def type
79
- collection_type = config['multiple'] ? Valkyrie::Types::Array : Identity
79
+ collection_type = if config['multiple']
80
+ Valkyrie::Types::Array.constructor { |v| Array(v).select(&:present?) }
81
+ else
82
+ Identity
83
+ end
80
84
  collection_type.of(type_for(config['type']))
81
85
  end
82
86