hyrax 3.4.1 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +26 -17
  3. data/.dassie/.env +1 -1
  4. data/.dassie/Gemfile +1 -1
  5. data/.dassie/app/forms/collection_resource_form.rb +8 -0
  6. data/.dassie/app/indexers/collection_resource_indexer.rb +8 -0
  7. data/.dassie/app/models/collection_resource.rb +35 -0
  8. data/.dassie/config/initializers/file_services.rb +4 -0
  9. data/.dassie/config/initializers/hyrax.rb +2 -1
  10. data/.dassie/config/metadata/collection_resource.yaml +23 -0
  11. data/.dassie/db/seeds.rb +2 -0
  12. data/.dassie/spec/forms/collection_resource_form_spec.rb +13 -0
  13. data/.dassie/spec/indexers/collection_resource_indexer_spec.rb +14 -0
  14. data/.dassie/spec/models/collection_resource_spec.rb +13 -0
  15. data/.regen +1 -1
  16. data/.rubocop.yml +1 -1
  17. data/.rubocop_fixme.yml +19 -2
  18. data/CONTAINERS.md +18 -13
  19. data/Dockerfile +2 -2
  20. data/app/actors/hyrax/actors/collections_membership_actor.rb +1 -1
  21. data/app/actors/hyrax/actors/embargo_actor.rb +11 -4
  22. data/app/actors/hyrax/actors/lease_actor.rb +11 -4
  23. data/app/assets/javascripts/hyrax/app.js.erb +1 -1
  24. data/app/assets/javascripts/hyrax/collections_v2.es6 +13 -0
  25. data/app/assets/javascripts/hyrax/permissions/control.es6 +8 -1
  26. data/app/controllers/concerns/hyrax/collections_controller_behavior.rb +1 -3
  27. data/app/controllers/concerns/hyrax/embargoes_controller_behavior.rb +11 -2
  28. data/app/controllers/concerns/hyrax/leases_controller_behavior.rb +11 -2
  29. data/app/controllers/concerns/hyrax/manages_embargoes.rb +13 -1
  30. data/app/controllers/hyrax/dashboard/collections_controller.rb +159 -76
  31. data/app/controllers/hyrax/downloads_controller.rb +16 -1
  32. data/app/controllers/hyrax/file_sets_controller.rb +10 -1
  33. data/app/controllers/hyrax/permissions_controller.rb +1 -1
  34. data/app/controllers/hyrax/single_use_links_viewer_controller.rb +1 -1
  35. data/app/controllers/hyrax/transfers_controller.rb +0 -2
  36. data/app/controllers/hyrax/workflow_actions_controller.rb +3 -2
  37. data/app/forms/hyrax/forms/collection_form.rb +1 -1
  38. data/app/forms/hyrax/forms/dashboard/nest_collection_form.rb +2 -0
  39. data/app/forms/hyrax/forms/embargo.rb +13 -0
  40. data/app/forms/hyrax/forms/file_set_edit_form.rb +1 -1
  41. data/app/forms/hyrax/forms/file_set_form.rb +8 -35
  42. data/app/forms/hyrax/forms/lease.rb +13 -0
  43. data/app/forms/hyrax/forms/pcdm_collection_form.rb +29 -2
  44. data/app/forms/hyrax/forms/pcdm_object_form.rb +46 -0
  45. data/app/forms/hyrax/forms/resource_form.rb +39 -21
  46. data/app/forms/hyrax/forms/widgets/admin_set_visibility.rb +1 -1
  47. data/app/forms/hyrax/forms/work_embargo_form.rb +35 -0
  48. data/app/forms/hyrax/forms/work_lease_form.rb +35 -0
  49. data/app/helpers/hyrax/embargo_helper.rb +11 -0
  50. data/app/helpers/hyrax/lease_helper.rb +11 -0
  51. data/app/indexers/hyrax/pcdm_collection_indexer.rb +1 -1
  52. data/app/indexers/hyrax/valkyrie_file_set_indexer.rb +2 -8
  53. data/app/jobs/characterize_job.rb +5 -1
  54. data/app/jobs/content_event_job.rb +1 -1
  55. data/app/jobs/import_url_job.rb +4 -6
  56. data/app/jobs/valkyrie_ingest_job.rb +15 -77
  57. data/app/models/admin_set.rb +8 -0
  58. data/app/models/concerns/hyrax/collection_behavior.rb +3 -2
  59. data/app/models/concerns/hyrax/collection_nesting.rb +10 -1
  60. data/app/models/concerns/hyrax/file_set_behavior.rb +1 -0
  61. data/app/models/concerns/hyrax/user.rb +11 -0
  62. data/app/models/concerns/hyrax/work_behavior.rb +3 -2
  63. data/app/models/featured_work_list.rb +0 -1
  64. data/app/models/hyrax/embargo.rb +1 -1
  65. data/app/models/hyrax/file_metadata.rb +32 -2
  66. data/app/models/hyrax/file_set.rb +4 -6
  67. data/app/models/hyrax/group.rb +19 -0
  68. data/app/models/hyrax/lease.rb +1 -1
  69. data/app/models/hyrax/pcdm_collection.rb +0 -1
  70. data/app/models/job_io_wrapper.rb +1 -1
  71. data/app/models/proxy_deposit_request.rb +1 -1
  72. data/app/presenters/hyrax/member_presenter_factory.rb +2 -4
  73. data/app/presenters/hyrax/permission_badge.rb +3 -2
  74. data/app/presenters/hyrax/version_list_presenter.rb +6 -1
  75. data/app/presenters/hyrax/work_show_presenter.rb +3 -3
  76. data/app/search_builders/hyrax/dashboard/nested_collections_search_builder.rb +13 -5
  77. data/app/services/hyrax/access_control_list.rb +7 -6
  78. data/app/services/hyrax/adapters/nesting_index_adapter.rb +3 -3
  79. data/app/services/hyrax/characterization/valkyrie_characterization_service.rb +3 -5
  80. data/app/services/hyrax/collections/collection_member_service.rb +3 -5
  81. data/app/services/hyrax/collections/nested_collection_query_service.rb +1 -1
  82. data/app/services/hyrax/edit_permissions_service.rb +21 -3
  83. data/app/services/hyrax/embargo_manager.rb +9 -0
  84. data/app/services/hyrax/file_set_file_service.rb +55 -0
  85. data/app/services/hyrax/lease_manager.rb +9 -0
  86. data/app/services/hyrax/listeners/file_metadata_listener.rb +11 -0
  87. data/app/services/hyrax/listeners/member_cleanup_listener.rb +14 -18
  88. data/app/services/hyrax/multiple_membership_checker.rb +2 -0
  89. data/app/services/hyrax/user_stat_importer.rb +2 -0
  90. data/app/services/hyrax/valkyrie_persist_derivatives.rb +50 -0
  91. data/app/services/hyrax/valkyrie_upload.rb +94 -0
  92. data/app/services/hyrax/versioning_service.rb +77 -9
  93. data/app/services/hyrax/visibility_propagator.rb +5 -5
  94. data/app/services/hyrax/work_resource_query_service.rb +45 -0
  95. data/app/services/hyrax/workflow/workflow_importer.rb +7 -9
  96. data/app/services/hyrax/workflow/workflow_schema.rb +3 -5
  97. data/app/strategies/hyrax/strategies/yaml_strategy.rb +4 -6
  98. data/app/uploaders/hyrax/uploaded_file_uploader.rb +4 -4
  99. data/app/validators/hyrax/collection_membership_validator.rb +16 -15
  100. data/app/views/catalog/_index_header_list_default.html.erb +8 -1
  101. data/app/views/catalog/_thumbnail_list_default.html.erb +8 -3
  102. data/app/views/collections/edit_fields/_based_near.html.erb +7 -7
  103. data/app/views/hyrax/base/_form_progress.html.erb +1 -1
  104. data/app/views/hyrax/base/_form_visibility_component.html.erb +5 -1
  105. data/app/views/hyrax/dashboard/collections/_form.html.erb +3 -2
  106. data/app/views/hyrax/dashboard/collections/_form_branding.html.erb +1 -0
  107. data/app/views/hyrax/dashboard/sidebar/_activity.html.erb +1 -1
  108. data/app/views/hyrax/embargoes/edit.html.erb +3 -3
  109. data/app/views/hyrax/file_sets/_permission.html.erb +1 -1
  110. data/app/views/hyrax/file_sets/_permission_form.html.erb +1 -6
  111. data/app/views/hyrax/file_sets/edit.html.erb +2 -2
  112. data/app/views/hyrax/leases/edit.html.erb +3 -3
  113. data/app/views/hyrax/my/works/_tabs.html.erb +6 -1
  114. data/app/views/hyrax/transfers/new.html.erb +1 -1
  115. data/chart/hyrax/Chart.yaml +10 -6
  116. data/chart/hyrax/templates/_helpers.tpl +4 -0
  117. data/chart/hyrax/templates/cron-embargo.yaml +5 -0
  118. data/chart/hyrax/templates/cron-lease.yaml +5 -0
  119. data/chart/hyrax/templates/deployment-worker.yaml +11 -0
  120. data/chart/hyrax/templates/ingress.yaml +7 -6
  121. data/chart/hyrax/values.yaml +152 -0
  122. data/config/features.rb +48 -50
  123. data/config/initializers/{valkryrie_storage.rb → storage_adapter_initializer.rb} +5 -0
  124. data/config/locales/hyrax.de.yml +14 -13
  125. data/config/locales/hyrax.en.yml +3 -0
  126. data/config/locales/hyrax.es.yml +1 -0
  127. data/config/locales/hyrax.fr.yml +2 -1
  128. data/config/locales/hyrax.it.yml +1 -0
  129. data/config/locales/hyrax.pt-BR.yml +1 -0
  130. data/config/locales/hyrax.zh.yml +1 -0
  131. data/config/metadata/file_set_metadata.yaml +130 -0
  132. data/docker-compose.yml +1 -0
  133. data/documentation/developing-your-hyrax-based-app.md +2 -2
  134. data/documentation/legacyREADME.md +3 -3
  135. data/hyrax.gemspec +7 -3
  136. data/lib/generators/hyrax/collection_resource/USAGE +20 -0
  137. data/lib/generators/hyrax/collection_resource/collection_resource_generator.rb +133 -0
  138. data/lib/generators/hyrax/collection_resource/templates/collection.rb.erb +34 -0
  139. data/lib/generators/hyrax/collection_resource/templates/collection_form.rb.erb +7 -0
  140. data/lib/generators/hyrax/collection_resource/templates/collection_form_spec.rb.erb +13 -0
  141. data/lib/generators/hyrax/collection_resource/templates/collection_indexer.rb.erb +7 -0
  142. data/lib/generators/hyrax/collection_resource/templates/collection_indexer_spec.rb.erb +13 -0
  143. data/lib/generators/hyrax/collection_resource/templates/collection_metadata.yaml +22 -0
  144. data/lib/generators/hyrax/collection_resource/templates/collection_spec.rb.erb +12 -0
  145. data/lib/generators/hyrax/install_generator.rb +9 -0
  146. data/lib/generators/hyrax/templates/config/locales/hyrax.de.yml +1 -1
  147. data/lib/generators/hyrax/templates/config/locales/hyrax.en.yml +1 -1
  148. data/lib/generators/hyrax/templates/config/locales/hyrax.es.yml +1 -1
  149. data/lib/generators/hyrax/templates/config/locales/hyrax.fr.yml +1 -1
  150. data/lib/generators/hyrax/templates/config/locales/hyrax.it.yml +1 -1
  151. data/lib/generators/hyrax/templates/config/locales/hyrax.zh.yml +1 -1
  152. data/lib/hyrax/active_fedora_dummy_model.rb +13 -0
  153. data/lib/hyrax/configuration.rb +21 -2
  154. data/lib/hyrax/errors.rb +2 -0
  155. data/lib/hyrax/publisher.rb +4 -0
  156. data/lib/hyrax/specs/shared_specs/factories/strategies/valkyrie_resource.rb +6 -0
  157. data/lib/hyrax/specs/shared_specs/hydra_works.rb +0 -1
  158. data/lib/hyrax/specs/shared_specs/indexers.rb +5 -0
  159. data/lib/hyrax/transactions/collection_update.rb +2 -0
  160. data/lib/hyrax/transactions/container.rb +10 -0
  161. data/lib/hyrax/transactions/steps/save_collection_banner.rb +59 -0
  162. data/lib/hyrax/transactions/steps/save_collection_logo.rb +109 -0
  163. data/lib/hyrax/version.rb +1 -1
  164. data/lib/wings/active_fedora_converter/file_metadata_node.rb +48 -0
  165. data/lib/wings/active_fedora_converter/instance_builder.rb +68 -0
  166. data/lib/wings/active_fedora_converter.rb +3 -3
  167. data/lib/wings/services/custom_queries/find_file_metadata.rb +19 -8
  168. data/lib/wings/valkyrie/query_service.rb +4 -6
  169. data/lib/wings/valkyrie/storage.rb +6 -2
  170. data/template.rb +1 -1
  171. metadata +99 -13
  172. data/app/views/catalog/_index_header_list_hyrax_pcdm_collection.html.erb +0 -4
@@ -31,12 +31,10 @@ module Hyrax
31
31
  # @see https://wiki.duraspace.org/display/samvera/Hydra%3A%3AWorks+Shared+Modeling
32
32
  class FileSet < Hyrax::Resource
33
33
  include Hyrax::Schema(:core_metadata)
34
- include Hyrax::Schema(:basic_metadata)
34
+ include Hyrax::Schema(:file_set_metadata)
35
35
 
36
36
  def self.model_name(name_class: Hyrax::Name)
37
- @_model_name ||= begin
38
- name_class.new(self, nil, 'FileSet')
39
- end
37
+ @_model_name ||= name_class.new(self, nil, 'FileSet')
40
38
  end
41
39
 
42
40
  class_attribute :characterization_proxy
@@ -44,8 +42,8 @@ module Hyrax
44
42
 
45
43
  attribute :file_ids, Valkyrie::Types::Array.of(Valkyrie::Types::ID) # id for FileMetadata resources
46
44
  attribute :thumbnail_id, Valkyrie::Types::ID.optional # id for FileMetadata resource
47
- attribute :original_file_id, Valkyrie::Types::ID # id for FileMetadata resource
48
- attribute :extracted_text_id, Valkyrie::Types::ID # id for FileMetadata resource
45
+ attribute :original_file_id, Valkyrie::Types::ID.optional # id for FileMetadata resource
46
+ attribute :extracted_text_id, Valkyrie::Types::ID.optional # id for FileMetadata resource
49
47
 
50
48
  ##
51
49
  # @return [Valkyrie::ID]
@@ -7,12 +7,31 @@ module Hyrax
7
7
  DEFAULT_NAME_PREFIX
8
8
  end
9
9
 
10
+ ##
11
+ # @return [Hyrax::Group]
12
+ def self.from_agent_key(key)
13
+ new(key.delete_prefix(name_prefix))
14
+ end
15
+
10
16
  def initialize(name)
11
17
  @name = name
12
18
  end
13
19
 
14
20
  attr_reader :name
15
21
 
22
+ ##
23
+ # @return [Boolean]
24
+ def ==(other)
25
+ other.class == self.class && other.name == name
26
+ end
27
+
28
+ ##
29
+ # @return [String] a local identifier for this group; for use (e.g.) in ACL
30
+ # data
31
+ def agent_key
32
+ self.class.name_prefix + name
33
+ end
34
+
16
35
  def to_sipity_agent
17
36
  sipity_agent || create_sipity_agent!
18
37
  end
@@ -15,7 +15,7 @@ module Hyrax
15
15
  attribute :lease_history, Valkyrie::Types::Array
16
16
 
17
17
  def active?
18
- (lease_expiration_date.present? && Time.zone.today < lease_expiration_date)
18
+ (lease_expiration_date.present? && Hyrax::TimeService.time_in_utc < lease_expiration_date)
19
19
  end
20
20
  end
21
21
  end
@@ -41,7 +41,6 @@ module Hyrax
41
41
  #
42
42
  class PcdmCollection < Hyrax::Resource
43
43
  include Hyrax::Schema(:core_metadata)
44
- include Hyrax::Schema(:basic_metadata)
45
44
 
46
45
  attribute :collection_type_gid, Valkyrie::Types::String
47
46
  attribute :member_ids, Valkyrie::Types::Array.of(Valkyrie::Types::ID).meta(ordered: true)
@@ -94,7 +94,7 @@ class JobIoWrapper < ApplicationRecord
94
94
 
95
95
  def extracted_original_name
96
96
  eon = uploaded_file.uploader.filename if uploaded_file
97
- eon ||= File.basename(path) if path.present? # note: uploader.filename is `nil` with uncached remote files (e.g. AWSFile)
97
+ eon ||= File.basename(path) if path.present? # NOTE: uploader.filename is `nil` with uncached remote files (e.g. AWSFile)
98
98
  eon
99
99
  end
100
100
 
@@ -7,7 +7,7 @@ class ProxyDepositRequest < ActiveRecord::Base
7
7
  include ActionView::Helpers::UrlHelper
8
8
 
9
9
  class_attribute :work_query_service_class
10
- self.work_query_service_class = Hyrax::WorkQueryService
10
+ self.work_query_service_class = Hyrax.config.use_valkyrie? ? Hyrax::WorkResourceQueryService : Hyrax::WorkQueryService
11
11
 
12
12
  delegate :deleted_work?, :work, :to_s, to: :work_query_service
13
13
 
@@ -47,13 +47,11 @@ module Hyrax
47
47
  # in order.
48
48
  # Arbitrarily maxed at 10 thousand; had to specify rows due to solr's default of 10
49
49
  def file_set_ids
50
- @file_set_ids ||= begin
51
- Hyrax::SolrService.query("{!field f=has_model_ssim}FileSet",
50
+ @file_set_ids ||= Hyrax::SolrService.query("{!field f=has_model_ssim}FileSet",
52
51
  rows: 10_000,
53
52
  fl: Hyrax.config.id_field,
54
53
  fq: "{!join from=ordered_targets_ssim to=id}id:\"#{id}/list_source\"")
55
- .flat_map { |x| x.fetch(Hyrax.config.id_field, []) }
56
- end
54
+ .flat_map { |x| x.fetch(Hyrax.config.id_field, []) }
57
55
  end
58
56
 
59
57
  def presenter_factory_arguments
@@ -24,14 +24,15 @@ module Hyrax
24
24
  private
25
25
 
26
26
  def dom_label_class
27
- VISIBILITY_LABEL_CLASS.fetch(@visibility.to_sym, 'label-info')
27
+ VISIBILITY_LABEL_CLASS.fetch(@visibility&.to_sym, 'label-info')
28
28
  end
29
29
 
30
30
  def text
31
31
  if registered?
32
32
  Institution.name
33
33
  else
34
- I18n.t("hyrax.visibility.#{@visibility}.text")
34
+ visibility_key = @visibility || 'unknown'
35
+ I18n.t("hyrax.visibility.#{visibility_key}.text")
35
36
  end
36
37
  end
37
38
 
@@ -19,7 +19,12 @@ module Hyrax
19
19
  #
20
20
  # @raise [ArgumentError] if we can't build an enu
21
21
  def self.for(file_set:)
22
- new(file_set.original_file&.versions&.all.to_a)
22
+ original_file = if file_set.respond_to?(:original_file)
23
+ file_set.original_file
24
+ else
25
+ Hyrax::FileSetFileService.new(file_set: file_set).original_file
26
+ end
27
+ new(Hyrax::VersioningService.new(resource: original_file).versions)
23
28
  rescue NoMethodError
24
29
  raise ArgumentError
25
30
  end
@@ -154,9 +154,9 @@ module Hyrax
154
154
  def grouped_presenters(filtered_by: nil, except: nil)
155
155
  # TODO: we probably need to retain collection_presenters (as parent_presenters)
156
156
  # and join this with member_of_collection_presenters
157
- grouped = member_of_collection_presenters.group_by(&:model_name).transform_keys { |key| key.to_s.underscore }
158
- grouped.select! { |obj| obj.downcase == filtered_by } unless filtered_by.nil?
159
- grouped.except!(*except) unless except.nil?
157
+ grouped = member_of_collection_presenters.group_by(&:model_name).transform_keys(&:human)
158
+ grouped.select! { |obj| obj.casecmp(filtered_by).zero? } unless filtered_by.nil?
159
+ grouped.reject! { |obj| except.map(&:downcase).include? obj.downcase } unless except.nil?
160
160
  grouped
161
161
  end
162
162
 
@@ -26,11 +26,19 @@ module Hyrax
26
26
 
27
27
  def show_only_other_collections_of_the_same_collection_type(solr_parameters)
28
28
  solr_parameters[:fq] ||= []
29
- solr_parameters[:fq] += [
30
- "-" + Hyrax::SolrQueryBuilderService.construct_query_for_ids([limit_ids]),
31
- Hyrax::SolrQueryBuilderService.construct_query(Hyrax.config.collection_type_index_field => @collection.collection_type_gid)
32
- ]
33
- solr_parameters[:fq] += limit_clause if limit_clause # add limits to prevent illegal nesting arrangements
29
+ if Hyrax.config.use_solr_graph_for_collection_nesting
30
+ solr_parameters[:fq] += [
31
+ Hyrax::SolrQueryBuilderService.construct_query(Hyrax.config.collection_type_index_field => @collection.collection_type_gid),
32
+ "-{!graph from=id to=member_of_collection_ids_ssim#{' maxDepth=1' if @nest_direction == :as_parent}}id:#{@collection.id}",
33
+ "-{!graph to=id from=member_of_collection_ids_ssim#{' maxDepth=1' if @nest_direction == :as_child}}id:#{@collection.id}"
34
+ ]
35
+ else
36
+ solr_parameters[:fq] += [
37
+ "-" + Hyrax::SolrQueryBuilderService.construct_query_for_ids([limit_ids]),
38
+ Hyrax::SolrQueryBuilderService.construct_query(Hyrax.config.collection_type_index_field => @collection.collection_type_gid)
39
+ ]
40
+ solr_parameters[:fq] += limit_clause if limit_clause # add limits to prevent illegal nesting arrangements
41
+ end
34
42
  end
35
43
 
36
44
  private
@@ -200,13 +200,14 @@ module Hyrax
200
200
 
201
201
  private
202
202
 
203
+ ##
204
+ # Returns the identifier used by ACLs to identify agents.
205
+ #
206
+ # This defaults to the `:agent_key`, but if that method doesn’t exist,
207
+ # `:user_key` will be used as a fallback.
203
208
  def id_for(agent:)
204
- case agent
205
- when Hyrax::Group
206
- "#{Hyrax::Group.name_prefix}#{agent.name}"
207
- else
208
- agent.user_key.to_s
209
- end
209
+ key = agent.try(:agent_key) || agent.user_key
210
+ key.to_s
210
211
  end
211
212
  end
212
213
 
@@ -60,7 +60,7 @@ module Hyrax
60
60
  object = ActiveFedora::Base.find(id)
61
61
  parent_ids = object.try(:member_of_collection_ids) || []
62
62
 
63
- # note: we do not yield when the object has parents. Calling the nested indexer for the
63
+ # NOTE: we do not yield when the object has parents. Calling the nested indexer for the
64
64
  # top id will reindex all descendants as well.
65
65
  if object.try(:use_nested_reindexing?)
66
66
  yield(id, parent_ids) if parent_ids.empty?
@@ -119,11 +119,11 @@ module Hyrax
119
119
  # @yield Samvera::NestingIndexer::Documents::IndexDocument
120
120
  #
121
121
  # @return [void]
122
- def self.each_child_document_of(document:, extent:, &block)
122
+ def self.each_child_document_of(document:, extent:, &block) # rubocop:disable Lint/UnusedMethodArgument
123
123
  raw_child_solr_documents_of(parent_document: document).each do |solr_document|
124
124
  child_document = coerce_solr_document_to_index_document(original_solr_document: solr_document, id: solr_document.fetch('id'))
125
125
  # during light reindexing, we want to reindex the child only if fields aren't already there
126
- block.call(child_document) if full_reindex?(extent: extent) || child_document.pathnames.empty?
126
+ yield(child_document) if full_reindex?(extent: extent) || child_document.pathnames.empty?
127
127
  end
128
128
  end
129
129
 
@@ -91,11 +91,9 @@ class Hyrax::Characterization::ValkyrieCharacterizationService
91
91
  h = {}
92
92
 
93
93
  doc.class.terminology.terms.each_pair do |key, _target|
94
- begin
95
- h[key] = doc.public_send(key)
96
- rescue NoMethodError
97
- next
98
- end
94
+ h[key] = doc.public_send(key)
95
+ rescue NoMethodError
96
+ next
99
97
  end
100
98
 
101
99
  h.compact
@@ -83,11 +83,9 @@ module Hyrax
83
83
  def add_members(collection_id:, new_members:, user:)
84
84
  messages = []
85
85
  new_members.map do |new_member|
86
- begin
87
- add_member(collection_id: collection_id, new_member: new_member, user: user)
88
- rescue Hyrax::SingleMembershipError => err
89
- messages += [err.message]
90
- end
86
+ add_member(collection_id: collection_id, new_member: new_member, user: user)
87
+ rescue Hyrax::SingleMembershipError => err
88
+ messages += [err.message]
91
89
  end
92
90
  raise Hyrax::SingleMembershipError, messages if messages.present?
93
91
  end
@@ -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.to_s, scope: scope)
89
+ nesting_attributes = (Hyrax.config.use_solr_graph_for_collection_nesting ? nil : 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,
@@ -20,17 +20,35 @@ module Hyrax
20
20
  # @note
21
21
  # +form object.class = SimpleForm::FormBuilder+
22
22
  # For works (i.e. GenericWork):
23
- # * form object.object = Hyrax::GenericWorkForm
24
- # * form object.object.model = GenericWork
23
+ # * form_object.object = Hyrax::GenericWorkForm
24
+ # * form_object.object.model = GenericWork
25
25
  # * use the work itself
26
26
  # For file_sets:
27
- # * form object.object.class = FileSet
27
+ # * form_object.object.class = FileSet
28
+ # * use work the file_set is in
29
+ # For file set forms:
30
+ # * form_object.object.class = Hyrax::Forms::FileSetForm OR
31
+ # Hyrax::Forms::FileSetEditForm
32
+ # * form_object.object.model = FileSet
28
33
  # * use work the file_set is in
29
34
  # No other object types are supported by this view.
30
35
  def self.build_service_object_from(form:, ability:)
31
36
  if form.object.respond_to?(:model) && form.object.model.work?
37
+ # The provided form object is a work form.
32
38
  new(object: form.object, ability: ability)
39
+ elsif form.object.respond_to?(:model) && form.object.model.file_set?
40
+ # The provided form object is a FileSet form. For Valkyrie forms
41
+ # (+Hyrax::Forms::FileSetForm+), +:in_works_ids+ is prepopulated onto
42
+ # the form object itself. For +Hyrax::Forms::FileSetEditForm+, the
43
+ # +:in_works+ method is present on the wrapped +:model+.
44
+ if form.object.is_a?(Hyrax::Forms::FileSetForm)
45
+ object_id = form.object.in_works_ids.first
46
+ new(object: Hyrax.query_service.find_by(id: object_id), ability: ability)
47
+ else
48
+ new(object: form.object.model.in_works.first, ability: ability)
49
+ end
33
50
  elsif form.object.file_set?
51
+ # The provided form object is a FileSet.
34
52
  new(object: form.object.in_works.first, ability: ability)
35
53
  end
36
54
  end
@@ -161,6 +161,15 @@ module Hyrax
161
161
  resource.embargo || Embargo.new
162
162
  end
163
163
 
164
+ ##
165
+ # Drop the embargo by setting its release date to `nil`.
166
+ #
167
+ # @return [void]
168
+ def nullify
169
+ return unless under_embargo?
170
+ embargo.embargo_release_date = nil
171
+ end
172
+
164
173
  ##
165
174
  # Sets the visibility of the resource to the embargo's visibility condition.
166
175
  # no-op if the embargo period is current.
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hyrax
4
+ ##
5
+ # A service for accessing specific Hyrax::FileMetadata objects referenced by a
6
+ # Hyrax::FileSet.
7
+ class FileSetFileService
8
+ ##
9
+ # @!attribute [r] file_set
10
+ # @return [Hyrax::FileSet]
11
+ attr_reader :file_set
12
+
13
+ ##
14
+ # @!attribute [r] query_service
15
+ # @return [#find_by]
16
+ attr_reader :query_service
17
+
18
+ ##
19
+ # @param resource [Hyrax::FileSet]
20
+ def initialize(file_set:, query_service: Hyrax.query_service)
21
+ @query_service = query_service
22
+ @file_set = file_set
23
+ end
24
+
25
+ ##
26
+ # Return the Hyrax::FileMetadata which should be considered “original” for
27
+ # indexing and version‐tracking.
28
+ #
29
+ # If +file_set.original_file_id+ is defined, it will be used; otherwise,
30
+ # this requires a custom query. The ultimate fallback, if no
31
+ # pcdm:OriginalFile is associated with the :file_set, is to just use the
32
+ # first file in its :file_ids.
33
+ #
34
+ # @return [Hyrax::FileMetadata]
35
+ def original_file
36
+ if file_set.original_file_id
37
+ # Always just use original_file_id if it is defined.
38
+ #
39
+ # NOTE: This needs to use :find_file_metadata_by, not :find_by, because
40
+ # at time of writing the latter does not work in Wings.
41
+ query_service.custom_queries.find_file_metadata_by(id: file_set.original_file_id)
42
+ else
43
+ # Cache the fallback to avoid needing to do this query twice.
44
+ #
45
+ # See NOTE above regarding use of :find_file_metadata_by.
46
+ @original_file ||= begin
47
+ query_service.custom_queries.find_original_file(file_set: file_set)
48
+ rescue Valkyrie::Persistence::ObjectNotFoundError
49
+ fallback_id = file_set.file_ids.first
50
+ query_service.custom_queries.find_file_metadata_by(id: fallback_id) if fallback_id
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -89,6 +89,15 @@ module Hyrax
89
89
  resource.lease || Lease.new
90
90
  end
91
91
 
92
+ ##
93
+ # Drop the lease by setting its release date to `nil`.
94
+ #
95
+ # @return [void]
96
+ def nullify
97
+ return unless under_lease?
98
+ lease.lease_expiration_date = nil
99
+ end
100
+
92
101
  ##
93
102
  # @return [Boolean]
94
103
  def release
@@ -5,6 +5,17 @@ module Hyrax
5
5
  ##
6
6
  # Listens for events related to {Hyrax::FileMetadata}
7
7
  class FileMetadataListener
8
+ ##
9
+ # Called when 'file.characterized' event is published;
10
+ # allows post-characterization handling, like derivatives generation.
11
+ #
12
+ # @param [Dry::Events::Event] event
13
+ # @return [void]
14
+ def on_file_characterized(event)
15
+ CreateDerivativesJob
16
+ .perform_later(event[:file_set], event[:file_id], event[:path_hint])
17
+ end
18
+
8
19
  ##
9
20
  # Called when 'file.metadata.updated' event is published; reindexes a
10
21
  # {Hyrax::FileSet} when a file claiming to be its `pcdm_use:OriginalFile`
@@ -13,15 +13,13 @@ module Hyrax
13
13
  return if event[:object].is_a?(ActiveFedora::Base) # handled by legacy code
14
14
 
15
15
  Hyrax.custom_queries.find_child_file_sets(resource: event[:object]).each do |file_set|
16
- begin
17
- Hyrax.persister.delete(resource: file_set)
18
- Hyrax.publisher
19
- .publish('object.deleted', object: file_set, id: file_set.id, user: event[:user])
20
- rescue StandardError # we don't uncaught errors looping filesets
21
- Hyrax.logger.warn "Failed to delete #{file_set.class}:#{file_set.id} " \
22
- "during cleanup for resource: #{event[:object]}. " \
23
- 'This member may now be orphaned.'
24
- end
16
+ Hyrax.persister.delete(resource: file_set)
17
+ Hyrax.publisher
18
+ .publish('object.deleted', object: file_set, id: file_set.id, user: event[:user])
19
+ rescue StandardError # we don't uncaught errors looping filesets
20
+ Hyrax.logger.warn "Failed to delete #{file_set.class}:#{file_set.id} " \
21
+ "during cleanup for resource: #{event[:object]}. " \
22
+ 'This member may now be orphaned.'
25
23
  end
26
24
  end
27
25
 
@@ -33,15 +31,13 @@ module Hyrax
33
31
  return if event[:collection].is_a?(ActiveFedora::Base) # handled by legacy code
34
32
 
35
33
  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
34
+ resource.member_of_collection_ids -= [event[:collection].id]
35
+ Hyrax.persister.save(resource: resource)
36
+ Hyrax.publisher
37
+ .publish('collection.membership.updated', collection: event[:collection], user: event[:user])
38
+ rescue StandardError
39
+ Hyrax.logger.warn "Failed to remove collection reference from #{work.class}:#{work.id} " \
40
+ "during cleanup for collection: #{event[:collection]}. "
45
41
  end
46
42
  end
47
43
  end
@@ -21,7 +21,9 @@ module Hyrax
21
21
  # also belong to other collections of the same type.
22
22
  #
23
23
  # @return [True, String] true if no conflicts; otherwise, an error message string
24
+ # @deprecated Use #check instead; for removal in 4.0.0
24
25
  def validate
26
+ Deprecation.warn "#{self.class}##{__method__} is deprecated; use #check instead."
25
27
  return true if item.member_of_collection_ids.empty? || item.member_of_collection_ids.count <= 1
26
28
  return true unless single_membership_collection_types_exist?
27
29
 
@@ -84,6 +84,8 @@ module Hyrax
84
84
  rescue StandardError => exception
85
85
  log_message fail_message
86
86
  log_message "Last exception #{exception}"
87
+ # Without returning false, we return the results of log_message; which is true.
88
+ false
87
89
  end
88
90
 
89
91
  def date_since_last_cache(user)
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ class ValkyriePersistDerivatives < Hydra::Derivatives::PersistOutputFileService
4
+ # Persists a derivative using the defined Valkyrie storage adapter
5
+ #
6
+ # This Service conforms to the signature of `Hydra::Derivatives::PersistOutputFileService`.
7
+ # This service is a Valkyrized alternative to the default Hydra::Derivatives::PersistOutputFileService.
8
+ # This service will always update existing and does not do versioning of persisted files.
9
+ #
10
+ # to replace the default AF derivative pipeline, set
11
+ # ```
12
+ # Hydra::Derivatives.config.output_file_service = Hyrax::ValkyriePersistDerivatives
13
+ # Hydra::Derivatives.config.source_file_service = Hyrax::LocalFileService
14
+ # ```
15
+ #
16
+ # @param [#read] stream the derivative filestream
17
+ # @param [Hash] directives
18
+ # @option directives [String] :url a url to the file destination
19
+ def self.call(stream, directives)
20
+ filepath = URI(directives.fetch(:url)).path
21
+ fileset_id = fileset_id_from_path(filepath)
22
+ fileset = Hyrax.metadata_adapter.query_service.find_by(id: fileset_id)
23
+
24
+ # Valkyrie storage adapters will typically expect an IO-like object that
25
+ # responds to #path -- here we only have a StringIO, so some
26
+ # transformation is in order
27
+ tmpfile = Tempfile.new(fileset_id, encoding: 'ascii-8bit')
28
+ tmpfile.write stream.read
29
+
30
+ Rails.logger.debug "Uploading thumbnail for FileSet #{fileset_id} as #{filepath}"
31
+ Hyrax.config.derivatives_storage_adapter.upload(
32
+ file: tmpfile,
33
+ original_filename: filepath,
34
+ resource: fileset
35
+ )
36
+ end
37
+
38
+ # The filepath will look something like
39
+ # /app/samvera/hyrax-webapp/derivatives/95/93/tv/12/3-thumbnail.jpeg and
40
+ # we want to extract the FileSet id, which in this case would be 9593tv123
41
+ #
42
+ # @param [String] path
43
+ # @return [String]
44
+ def self.fileset_id_from_path(path)
45
+ path.sub(Hyrax.config.derivatives_path.to_s, "")
46
+ .sub(/-[^\/]+\..*$/, "")
47
+ .delete("/")
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hyrax::ValkyrieUpload
4
+ # @param [IO] io
5
+ # @param [String] filename
6
+ # @param [Hyrax::FileSet] file_set
7
+ # @param [RDF::URI] use
8
+ # @param [User] user
9
+ #
10
+ # @see Hyrax::FileMetadata::Use
11
+ # @return [Hyrax::FileMetadata] the metadata representing the uploaded file
12
+ # rubocop:disable Metrics/MethodLength
13
+ # rubocop:disable Metrics/ParameterLists
14
+ def self.file(
15
+ filename:,
16
+ file_set:,
17
+ io:,
18
+ storage_adapter: Hyrax.storage_adapter,
19
+ use: Hyrax::FileMetadata::Use::ORIGINAL_FILE,
20
+ user: nil
21
+ )
22
+
23
+ streamfile = storage_adapter.upload(
24
+ file: io,
25
+ original_filename: filename,
26
+ resource: file_set,
27
+ use: use
28
+ )
29
+ io.close
30
+
31
+ file_metadata = Hyrax::FileMetadata(streamfile)
32
+ file_metadata.file_set_id = file_set.id
33
+
34
+ case use
35
+ when Hyrax::FileMetadata::Use::ORIGINAL_FILE
36
+ # Set file set label.
37
+ reset_title = file_set.title.first == file_set.label
38
+ # set title to label if that's how it was before this characterization
39
+ file_set.title = file_metadata.original_filename if reset_title
40
+ # always set the label to the original_name
41
+ file_set.label = file_metadata.original_filename
42
+ when Hyrax::FileMetadata::Use::THUMBNAIL
43
+ # TODO: the parent work's thumbnail_id remains incorrect (it's set to the
44
+ # FileSet ID, rather than the ID of this thumbnail FileMetadata; but
45
+ # trying to update the parent attributes here doesn't seem to stick
46
+ file_set.thumbnail_id = file_metadata.id
47
+ end
48
+
49
+ saved_metadata = Hyrax.persister.save(resource: file_metadata)
50
+ Hyrax.publisher.publish("object.file.uploaded", metadata: saved_metadata)
51
+
52
+ add_file_to_file_set(file_set: file_set,
53
+ file_metadata: saved_metadata,
54
+ user: user)
55
+
56
+ Hyrax.publisher.publish('file.metadata.updated', metadata: saved_metadata, user: user)
57
+
58
+ saved_metadata
59
+ end
60
+
61
+ # @param [Hyrax::FileSet] file_set the file set to add to
62
+ # @param [Hyrax::FileMetadata] file_metadata the metadata object representing
63
+ # the file to add
64
+ # @param [::User] user the user performing the add
65
+ #
66
+ # @return [Hyrax::FileSet] updated file set
67
+ def self.add_file_to_file_set(file_set:, file_metadata:, user:)
68
+ file_set.file_ids << file_metadata.id
69
+ set_file_use_ids(file_set, file_metadata)
70
+
71
+ Hyrax.persister.save(resource: file_set)
72
+ Hyrax.publisher.publish('object.membership.updated', object: file_set, user: user)
73
+ end
74
+
75
+ # @api private
76
+ # @param [Hyrax::FileSet] file_set the file set to add to
77
+ # @param [Hyrax::FileMetadata] file_metadata the metadata object representing
78
+ # the file to add
79
+ # @return [void]
80
+ def self.set_file_use_ids(file_set, file_metadata)
81
+ file_metadata.type.each do |type|
82
+ case type
83
+ when Hyrax::FileMetadata::Use::ORIGINAL_FILE
84
+ file_set.original_file_id = file_metadata.id
85
+ when Hyrax::FileMetadata::Use::THUMBNAIL
86
+ file_set.thumbnail_id = file_metadata.id
87
+ when Hyrax::FileMetadata::Use::EXTRACTED_TEXT
88
+ file_set.extracted_text_id = file_metadata.id
89
+ else
90
+ Rails.logger.warn "Unknown file use #{file_metadata.type} specified for #{file_metadata.file_identifier}"
91
+ end
92
+ end
93
+ end
94
+ end