hyrax 3.4.1 → 3.4.2

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 (104) 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/assets/javascripts/hyrax/collections_v2.es6 +13 -0
  21. data/app/controllers/concerns/hyrax/collections_controller_behavior.rb +1 -3
  22. data/app/controllers/hyrax/dashboard/collections_controller.rb +159 -75
  23. data/app/controllers/hyrax/single_use_links_viewer_controller.rb +1 -1
  24. data/app/forms/hyrax/forms/collection_form.rb +1 -1
  25. data/app/forms/hyrax/forms/pcdm_collection_form.rb +29 -2
  26. data/app/forms/hyrax/forms/resource_form.rb +15 -1
  27. data/app/forms/hyrax/forms/widgets/admin_set_visibility.rb +1 -1
  28. data/app/indexers/hyrax/pcdm_collection_indexer.rb +0 -1
  29. data/app/jobs/characterize_job.rb +5 -1
  30. data/app/jobs/import_url_job.rb +4 -6
  31. data/app/jobs/valkyrie_ingest_job.rb +15 -77
  32. data/app/models/admin_set.rb +8 -0
  33. data/app/models/concerns/hyrax/collection_behavior.rb +1 -1
  34. data/app/models/concerns/hyrax/user.rb +11 -0
  35. data/app/models/concerns/hyrax/work_behavior.rb +1 -1
  36. data/app/models/featured_work_list.rb +0 -1
  37. data/app/models/hyrax/file_metadata.rb +32 -2
  38. data/app/models/hyrax/file_set.rb +1 -3
  39. data/app/models/hyrax/group.rb +19 -0
  40. data/app/models/hyrax/pcdm_collection.rb +0 -1
  41. data/app/models/job_io_wrapper.rb +1 -1
  42. data/app/presenters/hyrax/member_presenter_factory.rb +2 -4
  43. data/app/presenters/hyrax/work_show_presenter.rb +3 -3
  44. data/app/services/hyrax/access_control_list.rb +7 -6
  45. data/app/services/hyrax/adapters/nesting_index_adapter.rb +3 -3
  46. data/app/services/hyrax/characterization/valkyrie_characterization_service.rb +3 -5
  47. data/app/services/hyrax/collections/collection_member_service.rb +3 -5
  48. data/app/services/hyrax/listeners/file_metadata_listener.rb +11 -0
  49. data/app/services/hyrax/listeners/member_cleanup_listener.rb +14 -18
  50. data/app/services/hyrax/multiple_membership_checker.rb +2 -0
  51. data/app/services/hyrax/valkyrie_persist_derivatives.rb +50 -0
  52. data/app/services/hyrax/valkyrie_upload.rb +94 -0
  53. data/app/services/hyrax/workflow/workflow_importer.rb +7 -9
  54. data/app/services/hyrax/workflow/workflow_schema.rb +3 -5
  55. data/app/strategies/hyrax/strategies/yaml_strategy.rb +4 -6
  56. data/app/uploaders/hyrax/uploaded_file_uploader.rb +4 -4
  57. data/app/validators/hyrax/collection_membership_validator.rb +16 -15
  58. data/app/views/catalog/_index_header_list_default.html.erb +8 -1
  59. data/app/views/catalog/_thumbnail_list_default.html.erb +8 -3
  60. data/app/views/collections/edit_fields/_based_near.html.erb +7 -7
  61. data/app/views/hyrax/dashboard/collections/_form.html.erb +3 -2
  62. data/app/views/hyrax/dashboard/collections/_form_branding.html.erb +1 -0
  63. data/app/views/hyrax/dashboard/sidebar/_activity.html.erb +1 -1
  64. data/app/views/hyrax/my/works/_tabs.html.erb +6 -1
  65. data/chart/hyrax/Chart.yaml +10 -6
  66. data/chart/hyrax/templates/_helpers.tpl +4 -0
  67. data/chart/hyrax/templates/cron-embargo.yaml +5 -0
  68. data/chart/hyrax/templates/cron-lease.yaml +5 -0
  69. data/chart/hyrax/templates/deployment-worker.yaml +11 -0
  70. data/chart/hyrax/templates/ingress.yaml +7 -6
  71. data/chart/hyrax/values.yaml +152 -0
  72. data/config/features.rb +48 -50
  73. data/config/initializers/{valkryrie_storage.rb → storage_adapter_initializer.rb} +5 -0
  74. data/config/locales/hyrax.de.yml +12 -12
  75. data/docker-compose.yml +1 -0
  76. data/documentation/developing-your-hyrax-based-app.md +1 -1
  77. data/documentation/legacyREADME.md +1 -1
  78. data/hyrax.gemspec +5 -3
  79. data/lib/generators/hyrax/collection_resource/USAGE +20 -0
  80. data/lib/generators/hyrax/collection_resource/collection_resource_generator.rb +133 -0
  81. data/lib/generators/hyrax/collection_resource/templates/collection.rb.erb +34 -0
  82. data/lib/generators/hyrax/collection_resource/templates/collection_form.rb.erb +7 -0
  83. data/lib/generators/hyrax/collection_resource/templates/collection_form_spec.rb.erb +13 -0
  84. data/lib/generators/hyrax/collection_resource/templates/collection_indexer.rb.erb +7 -0
  85. data/lib/generators/hyrax/collection_resource/templates/collection_indexer_spec.rb.erb +13 -0
  86. data/lib/generators/hyrax/collection_resource/templates/collection_metadata.yaml +22 -0
  87. data/lib/generators/hyrax/collection_resource/templates/collection_spec.rb.erb +12 -0
  88. data/lib/generators/hyrax/install_generator.rb +9 -0
  89. data/lib/hyrax/configuration.rb +12 -0
  90. data/lib/hyrax/publisher.rb +4 -0
  91. data/lib/hyrax/specs/shared_specs/hydra_works.rb +0 -1
  92. data/lib/hyrax/transactions/collection_update.rb +2 -0
  93. data/lib/hyrax/transactions/container.rb +10 -0
  94. data/lib/hyrax/transactions/steps/save_collection_banner.rb +59 -0
  95. data/lib/hyrax/transactions/steps/save_collection_logo.rb +109 -0
  96. data/lib/hyrax/version.rb +1 -1
  97. data/lib/wings/active_fedora_converter/file_metadata_node.rb +48 -0
  98. data/lib/wings/active_fedora_converter/instance_builder.rb +68 -0
  99. data/lib/wings/active_fedora_converter.rb +3 -3
  100. data/lib/wings/services/custom_queries/find_file_metadata.rb +19 -8
  101. data/lib/wings/valkyrie/query_service.rb +4 -6
  102. data/template.rb +1 -1
  103. metadata +63 -13
  104. data/app/views/catalog/_index_header_list_hyrax_pcdm_collection.html.erb +0 -4
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+ require 'rails/generators'
3
+ require 'rails/generators/model_helpers'
4
+
5
+ class Hyrax::CollectionResourceGenerator < Rails::Generators::NamedBase # rubocop:disable Metrics/ClassLength
6
+ # ActiveSupport can interpret models as plural which causes
7
+ # counter-intuitive route paths. Pull in ModelHelpers from
8
+ # Rails which warns users about pluralization when generating
9
+ # new models or scaffolds.
10
+ include Rails::Generators::ModelHelpers
11
+
12
+ source_root File.expand_path('../templates', __FILE__)
13
+
14
+ argument :with_basic_metadata, type: :string, default: "", banner: 'with_basic_metadata'
15
+
16
+ desc 'This generator makes the following changes to your application:
17
+ 1. Creates a collection model and model spec, optionally including basic metadata.
18
+ 2. Creates a collection form and spec.
19
+ 3. Creates a collection indexer and spec.
20
+ 4. Creates a collection metadata config.
21
+ 5. Sets this to be the collection class.
22
+ '
23
+
24
+ def self.exit_on_failure?
25
+ true
26
+ end
27
+
28
+ def validate_name
29
+ return unless name.strip.casecmp("collection").zero?
30
+ raise Thor::MalformattedArgumentError,
31
+ set_color("Error: A collection resource with the name '#{name}' would cause name-space clashes. "\
32
+ "Please use a different name.", :red)
33
+ end
34
+
35
+ def banner
36
+ if revoking?
37
+ say_status("info", "DESTROYING VALKYRIE COLLECTION MODEL: #{class_name}", :blue)
38
+ else
39
+ say_status("info", "GENERATING VALKYRIE COLLECTION MODEL: #{class_name}", :blue)
40
+ end
41
+ end
42
+
43
+ def create_metadata_config
44
+ template('collection_metadata.yaml', File.join('config/metadata/', "#{file_name}.yaml"))
45
+ end
46
+
47
+ def create_model
48
+ filepath = File.join('app/models', "#{file_name}.rb")
49
+ template('collection.rb.erb', filepath)
50
+ return unless include_basic_metadata?
51
+ inject_into_file filepath, before: /include Hyrax::Schema/ do
52
+ "include Hyrax::Schema(:basic_metadata)\n "
53
+ end
54
+ end
55
+
56
+ def create_model_spec
57
+ return unless rspec_installed?
58
+ filepath = File.join('spec/models/', "#{file_name}_spec.rb")
59
+ template('collection_spec.rb.erb', filepath)
60
+
61
+ return unless include_basic_metadata?
62
+ inject_into_file filepath, after: /it_behaves_like 'a Hyrax::PcdmCollection'/ do
63
+ "\n it_behaves_like 'a model with basic metadata'"
64
+ end
65
+ end
66
+
67
+ def create_form
68
+ filepath = File.join('app/forms/', "#{file_name}_form.rb")
69
+ template('collection_form.rb.erb', filepath)
70
+ return unless include_basic_metadata?
71
+ inject_into_file filepath, before: /include Hyrax::FormFields/ do
72
+ "include Hyrax::FormFields(:basic_metadata)\n "
73
+ end
74
+ end
75
+
76
+ # @todo If shared specs are expanded to test for basic metadata, inject calling that test here.
77
+ def create_form_spec
78
+ return unless rspec_installed?
79
+ template('collection_form_spec.rb.erb', File.join('spec/forms/', "#{file_name}_form_spec.rb"))
80
+ end
81
+
82
+ def create_indexer
83
+ filepath = File.join('app/indexers/', "#{file_name}_indexer.rb")
84
+ template('collection_indexer.rb.erb', filepath)
85
+ return unless include_basic_metadata?
86
+ inject_into_file filepath, before: /include Hyrax::Indexer/ do
87
+ "include Hyrax::Indexer(:basic_metadata)\n "
88
+ end
89
+ end
90
+
91
+ def create_indexer_spec
92
+ return unless rspec_installed?
93
+ filepath = File.join('spec/indexers/', "#{file_name}_indexer_spec.rb")
94
+ template('collection_indexer_spec.rb.erb', filepath)
95
+
96
+ return unless include_basic_metadata?
97
+ inject_into_file filepath, after: /it_behaves_like 'a Hyrax::Resource indexer'/ do
98
+ "\n it_behaves_like 'a Basic metadata indexer'"
99
+ end
100
+ end
101
+
102
+ # Inserts after the last registered work, or at the top of the config block
103
+ def set_as_the_collection_class
104
+ config = 'config/initializers/hyrax.rb'
105
+ lastmatch = nil
106
+ in_root do
107
+ File.open(config).each_line do |line|
108
+ lastmatch = line if line.match?(/config.collection_model = /)
109
+ end
110
+ content = " # Injected via `rails g hyrax:collection_resource #{class_name}`\n" \
111
+ " config.collection_model = '#{class_name}'\n"
112
+
113
+ anchor = lastmatch || "Hyrax.config do |config|\n"
114
+ inject_into_file config, after: anchor do
115
+ content
116
+ end
117
+ end
118
+ end
119
+
120
+ private
121
+
122
+ def include_basic_metadata?
123
+ with_basic_metadata.present? && with_basic_metadata == "with_basic_metadata"
124
+ end
125
+
126
+ def rspec_installed?
127
+ defined?(RSpec) && defined?(RSpec::Rails)
128
+ end
129
+
130
+ def revoking?
131
+ behavior == :revoke
132
+ end
133
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated via
4
+ # `rails generate hyrax:collection_resource <%= class_name %>`
5
+ class <%= class_name %> < Hyrax::PcdmCollection
6
+ # @note Do not directly update `basic_metadata.yaml`. It is also used by works.
7
+ #
8
+ # To change metadata for collections
9
+ # * extend by adding fields to `/config/metadata/<%= file_name %>.yaml`
10
+ # * remove all basic metadata
11
+ # * if you generated `with_basic_metadata` and now don't want any basic metadata,
12
+ # comment out or delete the schema include statement for `:basic_metadata`
13
+ # * update form and indexer classes to also remove the `:basic_metadata` schema include
14
+ # * remove some basic metadata
15
+ # * comment out or delete the schema include statement for `:basic_metadata`
16
+ # * update form and indexer classes to also remove the `:basic_metadata` schema include
17
+ # * copy fields you want to keep from `/config/metadata/basic_metadata.yaml`
18
+ # to `/config/metadata/<%= file_name %>.yaml`
19
+ # * override some basic metadata
20
+ # * fields are not allowed to repeat, so to override any basic metadata, you
21
+ # must first comment out or delete the schema include statement for `:basic_metadata`
22
+ # * update form and indexer classes to also remove the `:basic_metadata` schema include
23
+ # * copy all fields you want to keep unchanged or to override from `/config/metadata/basic_metadata.yaml`
24
+ # to `/config/metadata/<%= file_name %>.yaml`
25
+ # * update them in `config/metadata/<%= file_name %>.yaml to have the desired
26
+ # characteristics
27
+ #
28
+ # Alternative:
29
+ # * comment out or delete schema include statements
30
+ # * add Valkyrie attributes to this class
31
+ # * update form and indexer to process the attributes
32
+ #
33
+ include Hyrax::Schema(:<%= file_name %>)
34
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated via
4
+ # `rails generate hyrax:collection_resource <%= class_name %>`
5
+ class <%= class_name %>Form < Hyrax::Forms::PcdmCollectionForm
6
+ include Hyrax::FormFields(:<%= file_name %>)
7
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated via
4
+ # `rails generate hyrax:collection_resource <%= class_name %>`
5
+ require 'rails_helper'
6
+ require 'valkyrie/specs/shared_specs'
7
+
8
+ RSpec.describe <%= class_name %>Form do
9
+ let(:change_set) { described_class.new(resource) }
10
+ let(:resource) { <%= class_name %>.new }
11
+
12
+ it_behaves_like 'a Valkyrie::ChangeSet'
13
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated via
4
+ # `rails generate hyrax:collection_resource <%= class_name %>`
5
+ class <%= class_name %>Indexer < Hyrax::PcdmCollectionIndexer
6
+ include Hyrax::Indexer(:<%= file_name %>)
7
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated via
4
+ # `rails generate hyrax:collection_resource <%= class_name %>`
5
+ require 'rails_helper'
6
+ require 'hyrax/specs/shared_specs/indexers'
7
+
8
+ RSpec.describe <%= class_name %>Indexer do
9
+ let(:indexer_class) { described_class }
10
+ let(:resource) { <%= class_name %>.new }
11
+
12
+ it_behaves_like 'a Hyrax::Resource indexer'
13
+ end
@@ -0,0 +1,22 @@
1
+ # Simple yaml config-driven schema which is used to define model attributes,
2
+ # index key names, and form properties.
3
+ #
4
+ # Attributes must have a type but all other configuration options are optional.
5
+ #
6
+ # attributes:
7
+ # attribute_name:
8
+ # type: string
9
+ # multiple: false
10
+ # index_keys:
11
+ # - "attribute_name_sim"
12
+ # form:
13
+ # required: true
14
+ # primary: true
15
+ # multiple: false
16
+ #
17
+ # @see config/metadata/basic_metadata.yaml for an example configuration
18
+ #
19
+ # Generated via
20
+ # `rails generate hyrax:collection_resource <%= class_name %>`
21
+
22
+ attributes: {}
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated via
4
+ # `rails generate hyrax:collection_resource <%= class_name %>`
5
+ require 'rails_helper'
6
+ require 'hyrax/specs/shared_specs/hydra_works'
7
+
8
+ RSpec.describe <%= class_name %> do
9
+ subject(:collection) { described_class.new }
10
+
11
+ it_behaves_like 'a Hyrax::PcdmCollection'
12
+ end
@@ -35,6 +35,15 @@ module Hyrax
35
35
  18. Generates RIIIF image server implementation
36
36
  """
37
37
 
38
+ # browse-everything 1.2.0 is not compatible with Ruby 2.5
39
+ def pin_browse_everything
40
+ return unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.6')
41
+ gem 'browse-everything', '~> 1.1.2'
42
+ Bundler.with_clean_env do
43
+ run "bundle update browse-everything"
44
+ end
45
+ end
46
+
38
47
  def run_required_generators
39
48
  say_status('info', '[Hyrax] GENERATING BLACKLIGHT', :blue)
40
49
  generate 'blacklight:install --devise'
@@ -427,6 +427,18 @@ module Hyrax
427
427
  @branding_storage_adapter = Valkyrie::StorageAdapter.find(adapter.to_sym)
428
428
  end
429
429
 
430
+ ##
431
+ # @return [Valkyrie::StorageAdapter]
432
+ def derivatives_storage_adapter
433
+ @derivatives_storage_adapter ||= Valkyrie::StorageAdapter.find(:derivatives_disk)
434
+ end
435
+
436
+ ##
437
+ # @param [#to_sym] adapter
438
+ def derivatives_storage_adapter=(adapter)
439
+ @derivatives_storage_adapter = Valkyrie::StorageAdapter.find(adapter.to_sym)
440
+ end
441
+
430
442
  ##
431
443
  # @return [#save, #save_all, #delete, #wipe!] an indexing adapter
432
444
  def index_adapter
@@ -119,6 +119,10 @@ module Hyrax
119
119
  # unique id), AND a `:user` (the ::User responsible for the update).
120
120
  register_event('collection.membership.updated')
121
121
 
122
+ # @since 3.5.0
123
+ # @macro a_registered_event
124
+ register_event('file.characterized')
125
+
122
126
  # @since 3.3.0
123
127
  # @macro a_registered_event
124
128
  register_event('file.downloaded')
@@ -135,7 +135,6 @@ RSpec.shared_examples 'a Hyrax::PcdmCollection' do
135
135
 
136
136
  it_behaves_like 'a Hyrax::Resource'
137
137
  it_behaves_like 'a model with core metadata'
138
- it_behaves_like 'a model with basic metadata'
139
138
  it_behaves_like 'has members'
140
139
 
141
140
  describe '#collection_type_gid' do
@@ -9,6 +9,8 @@ module Hyrax
9
9
  # @since 3.2.0
10
10
  class CollectionUpdate < Transaction
11
11
  DEFAULT_STEPS = ['change_set.apply',
12
+ 'collection_resource.save_collection_banner',
13
+ 'collection_resource.save_collection_logo',
12
14
  'collection_resource.save_acl'].freeze
13
15
 
14
16
  ##
@@ -42,6 +42,8 @@ module Hyrax
42
42
  require 'hyrax/transactions/steps/remove_file_set_from_work'
43
43
  require 'hyrax/transactions/steps/save'
44
44
  require 'hyrax/transactions/steps/save_access_control'
45
+ require 'hyrax/transactions/steps/save_collection_banner'
46
+ require 'hyrax/transactions/steps/save_collection_logo'
45
47
  require 'hyrax/transactions/steps/set_default_admin_set'
46
48
  require 'hyrax/transactions/steps/set_modified_date'
47
49
  require 'hyrax/transactions/steps/set_uploaded_date_unless_present'
@@ -195,6 +197,14 @@ module Hyrax
195
197
  ops.register 'save_acl' do
196
198
  Steps::SaveAccessControl.new
197
199
  end
200
+
201
+ ops.register 'save_collection_banner' do
202
+ Steps::SaveCollectionBanner.new
203
+ end
204
+
205
+ ops.register 'save_collection_logo' do
206
+ Steps::SaveCollectionLogo.new
207
+ end
198
208
  end
199
209
 
200
210
  namespace 'work_resource' do |ops| # Hyrax::Work resource
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module Transactions
4
+ module Steps
5
+ ##
6
+ # Adds banner info via `ChangeSet`.
7
+ #
8
+ # During the update collection process this step is called to update the file
9
+ # to be used as a the banner for the collection.
10
+ #
11
+ class SaveCollectionBanner
12
+ include Dry::Transaction::Operation
13
+
14
+ ##
15
+ # @param [Hyrax::ChangeSet] change_set
16
+ # @param [Array<Integer>] update_banner_file_ids
17
+ # @param [Boolean] banner_unchanged_indicator
18
+ #
19
+ # @return [Dry::Monads::Result] `Failure` if the banner info fails to save;
20
+ # `Success(input)`, otherwise.
21
+ def call(collection_resource, update_banner_file_ids: nil, banner_unchanged_indicator: true)
22
+ return Success(collection_resource) if ActiveModel::Type::Boolean.new.cast(banner_unchanged_indicator)
23
+ collection_id = collection_resource.id.to_s
24
+ process_banner_input(collection_id: collection_id, update_banner_file_ids: update_banner_file_ids)
25
+ Success(collection_resource)
26
+ end
27
+
28
+ private
29
+
30
+ def process_banner_input(collection_id:, update_banner_file_ids:)
31
+ remove_banner(collection_id: collection_id)
32
+ add_new_banner(collection_id: collection_id, uploaded_file_ids: update_banner_file_ids) if update_banner_file_ids
33
+ end
34
+
35
+ def remove_banner(collection_id:)
36
+ banner_info = CollectionBrandingInfo.where(collection_id: collection_id).where(role: "banner")
37
+ banner_info&.delete_all
38
+ end
39
+
40
+ def add_new_banner(collection_id:, uploaded_file_ids:)
41
+ f = uploaded_files(uploaded_file_ids).first
42
+ banner_info = CollectionBrandingInfo.new(
43
+ collection_id: collection_id,
44
+ filename: File.split(f.file_url).last,
45
+ role: "banner",
46
+ alt_txt: "",
47
+ target_url: ""
48
+ )
49
+ banner_info.save f.file_url
50
+ end
51
+
52
+ def uploaded_files(uploaded_file_ids)
53
+ return [] if uploaded_file_ids.empty?
54
+ UploadedFile.find(uploaded_file_ids)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module Transactions
4
+ module Steps
5
+ ##
6
+ # Adds logo info via `ChangeSet`.
7
+ #
8
+ # During the update collection process this step is called to update the file(s)
9
+ # to be used as logo(s) for the collection.
10
+ #
11
+ class SaveCollectionLogo
12
+ include Dry::Transaction::Operation
13
+
14
+ ##
15
+ # @param [Hyrax::ChangeSet] change_set
16
+ # @param [Array<#Integer>] update_logo_file_ids
17
+ # @param [Array<String>] alttext_values
18
+ # @param [Array<String>] linkurl_values
19
+ #
20
+ # @return [Dry::Monads::Result] `Failure` if the work fails to save;
21
+ # `Success(input)`, otherwise.
22
+ def call(collection_resource, update_logo_file_ids: nil, alttext_values: nil, linkurl_values: nil)
23
+ collection_id = collection_resource.id.to_s
24
+ process_logo_input(collection_id: collection_id, update_logo_file_ids: update_logo_file_ids, alttext_values: alttext_values, linkurl_values: linkurl_values)
25
+ Success(collection_resource)
26
+ end
27
+
28
+ private
29
+
30
+ def process_logo_input(collection_id:, update_logo_file_ids:, alttext_values:, linkurl_values:)
31
+ uploaded_file_ids = update_logo_file_ids
32
+ public_files = []
33
+
34
+ if uploaded_file_ids.nil?
35
+ # all logo files were removed, so delete all files previously uploaded
36
+ remove_redundant_files(collection_id: collection_id, public_files: public_files)
37
+ return
38
+ end
39
+
40
+ public_files = process_logo_records(collection_id: collection_id, uploaded_file_ids: uploaded_file_ids, alttext_values: alttext_values, linkurl_values: linkurl_values)
41
+ remove_redundant_files(collection_id: collection_id, public_files: public_files)
42
+ end
43
+
44
+ def process_logo_records(collection_id:, uploaded_file_ids:, alttext_values:, linkurl_values:)
45
+ public_files = []
46
+ uploaded_file_ids.each_with_index do |ufi, i|
47
+ # If the user has chosen a new logo, the ufi will be an integer
48
+ # If the logo was previously chosen, the ufi will be a path
49
+ # If it is a path, update the rec, else create a new rec
50
+ if !ufi.match(/\D/).nil?
51
+ update_logo_info(collection_id: collection_id, uploaded_file_id: ufi, alttext: alttext_values[i], linkurl: verify_linkurl(linkurl_values[i]))
52
+ public_files << ufi
53
+ else # brand new one, insert in the database
54
+ logo_info = create_logo_info(collection_id: collection_id, uploaded_file_id: ufi, alttext: alttext_values[i], linkurl: verify_linkurl(linkurl_values[i]))
55
+ public_files << logo_info.local_path
56
+ end
57
+ end
58
+ public_files
59
+ end
60
+
61
+ def update_logo_info(collection_id:, uploaded_file_id:, alttext:, linkurl:)
62
+ logo_info = CollectionBrandingInfo.where(collection_id: collection_id).where(role: "logo").where(local_path: uploaded_file_id.to_s).first
63
+ logo_info.alt_text = alttext
64
+ logo_info.target_url = linkurl
65
+ logo_info.local_path = uploaded_file_id
66
+ logo_info.save(uploaded_file_id, false)
67
+ end
68
+
69
+ def create_logo_info(collection_id:, uploaded_file_id:, alttext:, linkurl:)
70
+ file = uploaded_files(uploaded_file_id)
71
+ logo_info = CollectionBrandingInfo.new(
72
+ collection_id: collection_id,
73
+ filename: File.split(file.file_url).last,
74
+ role: "logo",
75
+ alt_txt: alttext,
76
+ target_url: linkurl
77
+ )
78
+ logo_info.save file.file_url
79
+ logo_info
80
+ end
81
+
82
+ def uploaded_files(uploaded_file_ids)
83
+ return [] if uploaded_file_ids.empty?
84
+ UploadedFile.find(uploaded_file_ids)
85
+ end
86
+
87
+ def remove_redundant_files(collection_id:, public_files:)
88
+ # remove any public ones that were not included in the selection.
89
+ logos_info = CollectionBrandingInfo.where(collection_id: collection_id).where(role: "logo")
90
+ logos_info.each do |logo_info|
91
+ logo_info.delete(logo_info.local_path) unless public_files.include? logo_info.local_path
92
+ logo_info.destroy unless public_files.include? logo_info.local_path
93
+ end
94
+ end
95
+
96
+ # Only accept HTTP|HTTPS urls;
97
+ # @return <String> the url
98
+ def verify_linkurl(linkurl)
99
+ url = Loofah.scrub_fragment(linkurl, :prune).to_s
100
+ url if valid_url?(url)
101
+ end
102
+
103
+ def valid_url?(url)
104
+ (url =~ URI.regexp(['http', 'https']))
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
data/lib/hyrax/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Hyrax
3
- VERSION = '3.4.1'
3
+ VERSION = '3.4.2'
4
4
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wings
4
+ class ActiveFedoraConverter
5
+ def self.FileMetadataNode(resource_class) # rubocop:disable Naming/MethodName
6
+ class_cache[resource_class] ||= Class.new(FileMetadataNode) do
7
+ self.valkyrie_class = resource_class
8
+
9
+ # skip reserved attributes, we assume we don't need to translate valkyrie internals
10
+ schema = resource_class.schema.reject do |key|
11
+ resource_class.reserved_attributes.include?(key.name) ||
12
+ key.name == :size
13
+ end
14
+
15
+ Wings::ActiveFedoraConverter.apply_properties(self, schema)
16
+ end
17
+ end
18
+ end
19
+
20
+ class FileMetadataNode < ActiveFedora::Base
21
+ property :file_set_id, predicate: ::RDF::URI.intern("http://hyrax.samvera.org/ns/wings#file_set_id")
22
+ property :file_identifier, predicate: ::RDF::URI.intern("http://hyrax.samvera.org/ns/wings#file_identifier")
23
+
24
+ class_attribute :valkyrie_class
25
+
26
+ class << self
27
+ def model_name(*)
28
+ Hyrax::Name.new(valkyrie_class)
29
+ end
30
+
31
+ def to_rdf_representation
32
+ "Wings(#{valkyrie_class})"
33
+ end
34
+ alias inspect to_rdf_representation
35
+ alias to_s inspect
36
+ end
37
+
38
+ def indexing_service
39
+ Hyrax::ValkyrieIndexer.for(resource: valkyrie_resource)
40
+ end
41
+
42
+ def to_solr
43
+ super.tap do |doc|
44
+ doc[:file_identifier_ssim] = file_identifier
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wings
4
+ class ActiveFedoraConverter
5
+ ##
6
+ # Constructs an instance for the given converter. +converter+ must provide
7
+ # an +id+, +resource+, and +active_fedora_class+.
8
+ #
9
+ # This interface allows handling for special cases based on the target
10
+ # class, instance data for +resource+, or the id format. This originated as
11
+ # an extraction of some such special handling from the converter code.
12
+ class InstanceBuilder
13
+ ##
14
+ # @!attribute [r] converter
15
+ # @return [#active_fedora_class, #id, #resource]
16
+ # @!attribute [r] resource
17
+ # @return [Valkyrie::Resource]
18
+ attr_reader :converter, :resource
19
+
20
+ ##
21
+ # @param [#active_fedora_class, #id, #resource]
22
+ def initialize(converter)
23
+ @converter = converter
24
+ @resource = converter.resource
25
+ end
26
+
27
+ ##
28
+ # @return [ActiveFedora::Common]
29
+ def build
30
+ if builds_file_metadata? && !builds_metadata_for_active_fedora_file?
31
+ # convert to a generic/generated FileMetadataNode class with
32
+ # properties matching the source class
33
+ Wings::ActiveFedoraConverter::FileMetadataNode(resource.class)
34
+ .new(file_identifier: Array(resource.file_identifier)
35
+ .map(&:to_s))
36
+ elsif converter.id.present?
37
+ converter.active_fedora_class.find(converter.id)
38
+ else
39
+ converter.active_fedora_class.new
40
+ end
41
+ rescue ActiveFedora::ObjectNotFoundError
42
+ converter.active_fedora_class.new
43
+ end
44
+
45
+ ##
46
+ # @return [Boolean]
47
+ def builds_file_metadata?
48
+ resource.try(:file_identifier).present?
49
+ end
50
+
51
+ ##
52
+ # @return [Boolean]
53
+ def builds_metadata_for_active_fedora_file?
54
+ return false unless builds_file_metadata?
55
+
56
+ adapter_for_file = begin
57
+ ::Valkyrie::StorageAdapter.adapter_for(id: resource.file_identifier)
58
+ rescue ::Valkyrie::StorageAdapter::AdapterNotFoundError => err
59
+ Hyrax.logger.warn "Processing a FileMetadata (id: #{converter.id}) referencing " \
60
+ "a file #{resource.file_identifier}; could not find a " \
61
+ "storage adapter to handle that file.\n\t#{err.message}"
62
+ end
63
+
64
+ adapter_for_file.is_a?(::Valkyrie::Storage::Fedora)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require 'wings/converter_value_mapper'
4
4
  require 'wings/active_fedora_converter/default_work'
5
+ require 'wings/active_fedora_converter/file_metadata_node'
6
+ require 'wings/active_fedora_converter/instance_builder'
5
7
  require 'wings/active_fedora_converter/nested_resource'
6
8
 
7
9
  module Wings
@@ -100,9 +102,7 @@ module Wings
100
102
  private
101
103
 
102
104
  def instance
103
- id.present? ? active_fedora_class.find(id) : active_fedora_class.new
104
- rescue ActiveFedora::ObjectNotFoundError
105
- active_fedora_class.new
105
+ InstanceBuilder.new(self).build
106
106
  end
107
107
 
108
108
  def attributes_class