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.
- checksums.yaml +4 -4
- data/.dassie/config/initializers/hyrax.rb +11 -1
- data/.gitignore +3 -0
- data/.regen +1 -1
- data/.rubocop_fixme.yml +3 -1
- data/Dockerfile +2 -1
- data/app/actors/hyrax/actors/file_actor.rb +6 -4
- data/app/actors/hyrax/actors/transfer_request_actor.rb +3 -7
- data/app/assets/javascripts/hyrax/analytics_events.js +8 -2
- data/app/assets/javascripts/hyrax/autocomplete/linked_data.es6 +1 -3
- data/app/controllers/concerns/hyrax/controller.rb +21 -0
- data/app/controllers/concerns/hyrax/works_controller_behavior.rb +83 -59
- data/app/controllers/hyrax/admin/admin_sets_controller.rb +105 -19
- data/app/controllers/hyrax/admin/permission_template_accesses_controller.rb +12 -19
- data/app/controllers/hyrax/batch_edits_controller.rb +12 -3
- data/app/controllers/hyrax/batch_uploads_controller.rb +4 -0
- data/app/controllers/hyrax/citations_controller.rb +1 -1
- data/app/controllers/hyrax/dashboard/collections_controller.rb +19 -10
- data/app/forms/hyrax/forms/administrative_set_form.rb +19 -1
- data/app/forms/hyrax/forms/batch_edit_form.rb +1 -1
- data/app/forms/hyrax/forms/dashboard/nest_collection_form.rb +21 -6
- data/app/forms/hyrax/forms/pcdm_collection_form.rb +1 -0
- data/app/forms/hyrax/forms/permission_template_form.rb +17 -9
- data/app/forms/hyrax/forms/resource_form.rb +9 -5
- data/app/helpers/hyrax/collections_helper.rb +14 -0
- data/app/helpers/hyrax/membership_helper.rb +1 -1
- data/app/helpers/hyrax/trophy_helper.rb +1 -1
- data/app/helpers/hyrax/url_helper.rb +1 -1
- data/app/indexers/hyrax/administrative_set_indexer.rb +8 -2
- data/app/indexers/hyrax/deep_indexing_service.rb +1 -1
- data/app/indexers/hyrax/file_set_indexer.rb +1 -0
- data/app/indexers/hyrax/pcdm_collection_indexer.rb +3 -1
- data/app/indexers/hyrax/thumbnail_indexer.rb +31 -0
- data/app/indexers/hyrax/valkyrie_file_set_indexer.rb +6 -6
- data/app/indexers/hyrax/valkyrie_indexer.rb +4 -2
- data/app/indexers/hyrax/valkyrie_work_indexer.rb +13 -0
- data/app/inputs/controlled_vocabulary_input.rb +2 -0
- data/app/jobs/change_depositor_event_job.rb +47 -0
- data/app/jobs/characterize_job.rb +38 -2
- data/app/jobs/concerns/hyrax/members_permission_job_behavior.rb +1 -1
- data/app/jobs/content_depositor_change_event_job.rb +2 -1
- data/app/jobs/hyrax/propagate_change_depositor_job.rb +32 -0
- data/app/jobs/inherit_permissions_job.rb +1 -1
- data/app/jobs/valkyrie_create_derivatives_job.rb +25 -0
- data/app/jobs/valkyrie_ingest_job.rb +84 -16
- data/app/models/admin_set.rb +2 -2
- data/app/models/collection_branding_info.rb +8 -6
- data/app/models/concerns/hyrax/collection_behavior.rb +2 -2
- data/app/models/concerns/hyrax/file_set/characterization.rb +7 -1
- data/app/models/concerns/hyrax/solr_document/metadata.rb +1 -0
- data/app/models/concerns/hyrax/solr_document_behavior.rb +9 -3
- data/app/models/hyrax/administrative_set.rb +36 -1
- data/app/models/hyrax/collection_type.rb +2 -2
- data/app/models/hyrax/file_metadata.rb +5 -1
- data/app/models/hyrax/file_set.rb +42 -1
- data/app/models/hyrax/pcdm_collection.rb +56 -0
- data/app/models/hyrax/permission_template.rb +11 -5
- data/app/models/hyrax/work.rb +91 -0
- data/app/models/proxy_deposit_request.rb +1 -1
- data/app/presenters/hyrax/admin_set_presenter.rb +2 -2
- data/app/presenters/hyrax/pcdm_member_presenter_factory.rb +2 -2
- data/app/presenters/hyrax/work_show_presenter.rb +7 -3
- data/app/search_builders/hyrax/dashboard/collections_search_builder.rb +2 -2
- data/app/search_builders/hyrax/dashboard/managed_search_filters.rb +44 -4
- data/app/search_builders/hyrax/dashboard/nested_collections_search_builder.rb +2 -2
- data/app/search_builders/hyrax/my/collections_search_builder.rb +11 -4
- data/app/services/hyrax/access_control_list.rb +13 -0
- data/app/services/hyrax/admin_set_create_service.rb +21 -37
- data/app/services/hyrax/change_content_depositor_service.rb +2 -2
- data/app/services/hyrax/change_depositor_service.rb +70 -0
- data/app/services/hyrax/characterization/valkyrie_characterization_service.rb +1 -1
- data/app/services/hyrax/collections/nested_collection_query_service.rb +23 -11
- data/app/services/hyrax/custom_queries/navigators/child_file_sets_navigator.rb +45 -0
- data/app/services/hyrax/custom_queries/navigators/child_filesets_navigator.rb +7 -2
- data/app/services/hyrax/custom_queries/navigators/parent_work_navigator.rb +54 -0
- data/app/services/hyrax/default_middleware_stack.rb +3 -0
- data/app/services/hyrax/file_set_derivatives_service.rb +21 -2
- data/app/services/hyrax/file_set_type_service.rb +2 -5
- data/app/services/hyrax/listeners/file_metadata_listener.rb +20 -1
- data/app/services/hyrax/listeners/member_cleanup_listener.rb +23 -3
- data/app/services/hyrax/listeners/metadata_index_listener.rb +39 -0
- data/app/services/hyrax/listeners/proxy_deposit_listener.rb +14 -8
- data/app/services/hyrax/location_service.rb +33 -0
- data/app/services/hyrax/multiple_membership_checker.rb +44 -1
- data/app/services/hyrax/resource_visibility_propagator.rb +1 -1
- data/app/services/hyrax/simple_schema_loader.rb +5 -1
- data/app/services/hyrax/solr_query_service.rb +12 -7
- data/app/services/hyrax/thumbnail_path_service.rb +1 -1
- data/app/services/hyrax/work_uploads_handler.rb +0 -10
- data/app/validators/hyrax/collection_membership_validator.rb +38 -0
- data/app/views/catalog/_index_header_list_hyrax_pcdm_collection.html.erb +4 -0
- data/app/views/hyrax/admin/admin_sets/_form_participant_table.html.erb +2 -2
- data/app/views/hyrax/admin/admin_sets/_form_participants.html.erb +2 -2
- data/app/views/hyrax/admin/admin_sets/_form_visibility.html.erb +2 -2
- data/app/views/hyrax/admin/admin_sets/_form_workflow.erb +1 -1
- data/app/views/hyrax/admin/collection_types/index.html.erb +1 -1
- data/app/views/hyrax/base/_form.html.erb +1 -1
- data/app/views/hyrax/base/_form_child_work_relationships.html.erb +1 -1
- data/app/views/hyrax/dashboard/collections/_default_group.html.erb +2 -2
- data/app/views/hyrax/dashboard/collections/_form.html.erb +21 -15
- data/app/views/hyrax/dashboard/collections/_form_discovery.html.erb +6 -3
- data/app/views/hyrax/dashboard/collections/_form_share.html.erb +2 -2
- data/app/views/hyrax/dashboard/collections/_form_share_table.html.erb +3 -3
- data/app/views/hyrax/dashboard/collections/_list_collections.html.erb +2 -2
- data/app/views/hyrax/dashboard/works/_default_group.html.erb +1 -1
- data/app/views/hyrax/dashboard/works/_list_works.html.erb +1 -1
- data/app/views/hyrax/file_sets/_actions.html.erb +2 -2
- data/app/views/hyrax/my/_work_action_menu.html.erb +8 -9
- data/app/views/hyrax/my/collections/_default_group.html.erb +2 -2
- data/app/views/hyrax/my/collections/_list_collections.html.erb +2 -2
- data/app/views/hyrax/my/collections/index.html.erb +3 -2
- data/app/views/hyrax/my/works/_default_group.html.erb +1 -1
- data/app/views/hyrax/my/works/_list_works.html.erb +1 -2
- data/app/views/hyrax/my/works/index.html.erb +4 -2
- data/chart/hyrax/Chart.yaml +2 -2
- data/chart/hyrax/README.md +22 -1
- data/config/initializers/listeners.rb +0 -1
- data/config/locales/hyrax.de.yml +6 -5
- data/config/locales/hyrax.en.yml +30 -28
- data/config/locales/hyrax.es.yml +10 -9
- data/config/locales/hyrax.fr.yml +2 -1
- data/config/locales/hyrax.it.yml +3 -2
- data/config/locales/hyrax.pt-BR.yml +2 -1
- data/config/locales/hyrax.zh.yml +2 -1
- data/config/metadata/basic_metadata.yaml +2 -0
- data/config/metadata/core_metadata.yaml +1 -1
- data/docker-compose.yml +46 -42
- data/documentation/developing-your-hyrax-based-app.md +1 -1
- data/documentation/legacyREADME.md +1 -1
- data/lib/hyrax/administrative_set_name.rb +18 -0
- data/lib/hyrax/collection_name.rb +2 -0
- data/lib/hyrax/configuration.rb +10 -0
- data/lib/hyrax/controlled_vocabularies/location.rb +9 -2
- data/lib/hyrax/controlled_vocabularies/resource_label_caching.rb +42 -0
- data/lib/hyrax/controlled_vocabularies.rb +1 -0
- data/lib/hyrax/publisher.rb +45 -0
- data/lib/hyrax/specs/capybara.rb +1 -1
- data/lib/hyrax/specs/shared_specs/hydra_works.rb +11 -4
- data/lib/hyrax/specs/shared_specs/indexers.rb +117 -3
- data/lib/hyrax/transactions/admin_set_create.rb +2 -1
- data/lib/hyrax/transactions/admin_set_destroy.rb +22 -0
- data/lib/hyrax/transactions/admin_set_update.rb +21 -0
- data/lib/hyrax/transactions/collection_destroy.rb +22 -0
- data/lib/hyrax/transactions/collection_update.rb +3 -2
- data/lib/hyrax/transactions/container.rb +87 -23
- data/lib/hyrax/transactions/create_work.rb +3 -0
- data/lib/hyrax/transactions/destroy_work.rb +3 -0
- data/lib/hyrax/transactions/steps/apply_collection_permission_template.rb +2 -0
- data/lib/hyrax/transactions/steps/apply_permission_template.rb +2 -0
- data/lib/hyrax/transactions/steps/apply_visibility.rb +2 -0
- data/lib/hyrax/transactions/steps/change_depositor.rb +46 -0
- data/lib/hyrax/transactions/steps/check_for_empty_admin_set.rb +36 -0
- data/lib/hyrax/transactions/steps/delete_access_control.rb +32 -0
- data/lib/hyrax/transactions/steps/delete_resource.rb +19 -3
- data/lib/hyrax/transactions/steps/destroy_work.rb +3 -1
- data/lib/hyrax/transactions/steps/ensure_permission_template.rb +2 -0
- data/lib/hyrax/transactions/steps/save.rb +24 -6
- data/lib/hyrax/transactions/steps/save_access_control.rb +2 -2
- data/lib/hyrax/transactions/steps/save_work.rb +3 -0
- data/lib/hyrax/transactions/steps/set_user_as_creator.rb +41 -0
- data/lib/hyrax/transactions/steps/update_work_members.rb +51 -0
- data/lib/hyrax/transactions/update_work.rb +4 -3
- data/lib/hyrax/transactions/work_create.rb +1 -1
- data/lib/hyrax/transactions/work_destroy.rb +2 -1
- data/lib/hyrax/transactions/work_update.rb +19 -0
- data/lib/hyrax/version.rb +1 -1
- data/lib/wings/attribute_transformer.rb +5 -1
- data/lib/wings/setup.rb +3 -1
- data/lib/wings/valkyrie/query_service.rb +2 -1
- data/lib/wings/valkyrie/storage.rb +7 -1
- data/template.rb +1 -1
- 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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 :
|
|
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(
|
|
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:
|
|
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
|
|
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.
|
|
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
|
-
#
|
|
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]
|
|
14
|
+
# @param [Dry::Events::Event] _event
|
|
12
15
|
# @return [void]
|
|
13
|
-
def on_object_deposited(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
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.
|
|
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']
|
|
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
|
|