hyrax 5.1.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.dassie/Gemfile +1 -1
  3. data/.dassie/config/application.rb +1 -1
  4. data/.dassie/config/initializers/devise.rb +1 -0
  5. data/.dassie/db/schema.rb +110 -109
  6. data/.github/workflows/lint-build-test.yml +1 -1
  7. data/.koppie/config/initializers/devise.rb +1 -1
  8. data/CONTAINERS.md +10 -10
  9. data/Gemfile +2 -1
  10. data/app/controllers/concerns/hyrax/valkyrie_downloads_controller_behavior.rb +1 -0
  11. data/app/controllers/concerns/hyrax/works_controller_behavior.rb +2 -1
  12. data/app/controllers/hyrax/admin/analytics/work_reports_controller.rb +4 -4
  13. data/app/controllers/hyrax/file_sets_controller.rb +1 -1
  14. data/app/helpers/hyrax/hyrax_helper_behavior.rb +2 -2
  15. data/app/helpers/hyrax/trophy_helper.rb +1 -1
  16. data/app/jobs/concerns/hyrax/queued_job_behavior.rb +22 -0
  17. data/app/jobs/hyrax/propagate_change_depositor_job.rb +1 -1
  18. data/app/jobs/hyrax/queued_delete_job.rb +11 -0
  19. data/app/jobs/hyrax/queued_indexing_job.rb +11 -0
  20. data/app/jobs/migrate_files_to_valkyrie_job.rb +33 -21
  21. data/app/jobs/migrate_sipity_entity_job.rb +21 -0
  22. data/app/models/concerns/hyrax/ability.rb +4 -2
  23. data/app/models/concerns/hyrax/solr_document_behavior.rb +5 -2
  24. data/app/models/hyrax/file_metadata.rb +22 -7
  25. data/app/services/hyrax/analytics/ga4/base.rb +1 -1
  26. data/app/services/hyrax/analytics/ga4.rb +5 -1
  27. data/app/services/hyrax/change_depositor_service.rb +1 -1
  28. data/app/services/hyrax/characterization/valkyrie_characterization_service.rb +21 -13
  29. data/app/services/hyrax/custom_queries/find_ids_by_model.rb +31 -6
  30. data/app/services/hyrax/edit_permissions_service.rb +9 -8
  31. data/app/services/hyrax/workflow/workflow_factory.rb +3 -3
  32. data/app/services/migrate_resource_service.rb +1 -1
  33. data/app/views/_user_util_links.html.erb +2 -1
  34. data/app/views/hyrax/admin/analytics/collection_reports/_top_collections.html.erb +3 -7
  35. data/app/views/hyrax/admin/analytics/work_reports/_top_file_set_downloads.html.erb +3 -6
  36. data/app/views/hyrax/admin/analytics/work_reports/_top_works.html.erb +2 -3
  37. data/app/views/hyrax/admin/analytics/work_reports/_work_files.html.erb +1 -6
  38. data/app/views/hyrax/base/_social_media.html.erb +2 -0
  39. data/app/views/hyrax/dashboard/collections/_show_document_list_menu.html.erb +13 -12
  40. data/app/views/hyrax/my/_admin_set_action_menu.html.erb +31 -27
  41. data/app/views/hyrax/my/_collection_action_menu.html.erb +40 -35
  42. data/app/views/hyrax/my/_work_action_menu.html.erb +23 -22
  43. data/config/features.rb +50 -40
  44. data/config/initializers/indexing_adapter_initializer.rb +4 -0
  45. data/config/initializers/new_framework_defaults_7_2.rb +6 -4
  46. data/config/initializers/reform_rails_6_1_monkey_patch.rb +29 -0
  47. data/config/metadata/core_metadata.yaml +1 -0
  48. data/docker-compose-dassie.yml +4 -4
  49. data/documentation/developing-your-hyrax-based-app.md +2 -2
  50. data/hyrax.gemspec +2 -2
  51. data/lib/freyja/persister.rb +11 -4
  52. data/lib/hyrax/configuration.rb +22 -7
  53. data/lib/hyrax/controlled_vocabulary/importer/language.rb +5 -1
  54. data/lib/hyrax/transactions/steps/add_file_sets.rb +2 -1
  55. data/lib/hyrax/version.rb +1 -1
  56. data/lib/hyrax.rb +1 -0
  57. data/lib/valkyrie/indexing/redis_queue/indexing_adapter.rb +144 -0
  58. data/lib/wings/valkyrie/query_service.rb +3 -4
  59. data/template.rb +1 -1
  60. metadata +20 -12
  61. data/.github/workflows/main.yml +0 -17
@@ -133,8 +133,23 @@ module Hyrax
133
133
  attr_writer :analytics_reporting
134
134
  attr_reader :analytics_reporting
135
135
  def analytics_reporting?
136
- @analytics_reporting ||=
136
+ @analytics_reporting ||= begin
137
+ required_env_vars = %w[
138
+ HYRAX_ANALYTICS_REPORTING
139
+ GOOGLE_ANALYTICS_ID
140
+ GOOGLE_ANALYTICS_PROPERTY_ID
141
+ ]
142
+
143
+ required_env_vars << if ENV['GOOGLE_ACCOUNT_JSON'].blank?
144
+ 'GOOGLE_ACCOUNT_JSON_PATH'
145
+ else
146
+ 'GOOGLE_ACCOUNT_JSON'
147
+ end
148
+
149
+ return false if required_env_vars.any? { |var| ENV.fetch(var, '').blank? }
150
+
137
151
  ActiveModel::Type::Boolean.new.cast(ENV.fetch('HYRAX_ANALYTICS_REPORTING', false))
152
+ end
138
153
  end
139
154
 
140
155
  # Currently supports 'google' or 'matomo'
@@ -142,7 +157,7 @@ module Hyrax
142
157
  attr_writer :analytics_provider
143
158
  def analytics_provider
144
159
  @analytics_provider ||=
145
- ENV.fetch('HYRAX_ANALYTICS_PROVIDER', 'google')
160
+ ENV.fetch('HYRAX_ANALYTICS_PROVIDER', 'ga4')
146
161
  end
147
162
 
148
163
  ##
@@ -478,31 +493,31 @@ module Hyrax
478
493
  # Path on the local file system where derivatives will be stored
479
494
  attr_writer :derivatives_path
480
495
  def derivatives_path
481
- @derivatives_path ||= ENV.fetch('HYRAX_DERIVATIVES_PATH', Rails.root.join('tmp', 'derivatives'))
496
+ @derivatives_path ||= Pathname.new(ENV.fetch('HYRAX_DERIVATIVES_PATH', Rails.root.join('tmp', 'derivatives')))
482
497
  end
483
498
 
484
499
  # Path on the local file system where originals will be staged before being ingested into Fedora.
485
500
  attr_writer :working_path
486
501
  def working_path
487
- @working_path ||= ENV.fetch('HYRAX_UPLOAD_PATH', Rails.root.join('tmp', 'uploads'))
502
+ @working_path ||= Pathname.new(ENV.fetch('HYRAX_UPLOAD_PATH', Rails.root.join('tmp', 'uploads')))
488
503
  end
489
504
 
490
505
  # @todo do we use both upload_path and working path?
491
506
  # Path on the local file system where originals will be staged before being ingested into Fedora.
492
507
  attr_writer :upload_path
493
508
  def upload_path
494
- @upload_path ||= ->() { ENV.fetch('HYRAX_UPLOAD_PATH') { Rails.root.join('tmp', 'uploads') } }
509
+ @upload_path ||= ->() { Pathname.new(ENV.fetch('HYRAX_UPLOAD_PATH') { Rails.root.join('tmp', 'uploads') }) }
495
510
  end
496
511
 
497
512
  attr_writer :cache_path
498
513
  def cache_path
499
- @cache_path ||= ->() { ENV.fetch('HYRAX_CACHE_PATH') { Rails.root.join('tmp', 'cache') } }
514
+ @cache_path ||= ->() { Pathname.new(ENV.fetch('HYRAX_CACHE_PATH') { Rails.root.join('tmp', 'cache') }) }
500
515
  end
501
516
 
502
517
  # Path on the local file system where where log and banners will be stored.
503
518
  attr_writer :branding_path
504
519
  def branding_path
505
- @branding_path ||= ENV.fetch('HYRAX_BRANDING_PATH', Rails.root.join('public', 'branding'))
520
+ @branding_path ||= Pathname.new(ENV.fetch('HYRAX_BRANDING_PATH', Rails.root.join('public', 'branding')))
506
521
  end
507
522
 
508
523
  # @!endgroup
@@ -13,7 +13,11 @@ module Hyrax
13
13
  stdout_logger.formatter = proc do |_severity, _datetime, _progname, msg|
14
14
  "#{msg}\n"
15
15
  end
16
- ActiveSupport::BroadcastLogger.new(Hyrax.logger, stdout_logger)
16
+ if Rails.version >= '7.1'
17
+ ActiveSupport::BroadcastLogger.new(Hyrax.logger, stdout_logger)
18
+ else
19
+ Hyrax.logger.extend(ActiveSupport::Logger.broadcast(stdout_logger))
20
+ end
17
21
  end
18
22
 
19
23
  def import
@@ -20,10 +20,11 @@ module Hyrax
20
20
  ##
21
21
  # @param [Hyrax::Work] obj
22
22
  # @param [Enumerable<UploadedFile>] uploaded_files
23
- # @param [Enumerable<Hash>] file_set_params
23
+ # @param [Enumerable<Hash>] file_set_params or nil
24
24
  #
25
25
  # @return [Dry::Monads::Result]
26
26
  def call(obj, uploaded_files: [], file_set_params: [])
27
+ return Success(obj) if uploaded_files.empty? && file_set_params.blank? # Skip if no files to attach
27
28
  if @handler.new(work: obj).add(files: uploaded_files, file_set_params: file_set_params).attach
28
29
  file_sets = obj.member_ids.map do |member|
29
30
  Hyrax.query_service.find_by(id: member) if Hyrax.query_service.find_by(id: member).is_a? Hyrax::FileSet
data/lib/hyrax/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Hyrax
3
- VERSION = '5.1.0'
3
+ VERSION = '5.2.0'
4
4
  end
data/lib/hyrax.rb CHANGED
@@ -23,6 +23,7 @@ require 'hyrax/valkyrie_can_can_adapter'
23
23
  require 'retriable'
24
24
  require 'valkyrie/indexing_adapter'
25
25
  require 'valkyrie/indexing/solr/indexing_adapter'
26
+ require 'valkyrie/indexing/redis_queue/indexing_adapter'
26
27
  require 'valkyrie/indexing/null_indexing_adapter'
27
28
 
28
29
  ##
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+ module Valkyrie
3
+ module Indexing
4
+ module RedisQueue
5
+ class IndexingAdapter
6
+ ##
7
+ # @!attribute [r] connection
8
+ # @return [RSolr::Client]
9
+ attr_writer :connection
10
+ attr_accessor :index_queue_name, :delete_queue_name, :index_error_name, :delete_error_name
11
+
12
+ ##
13
+ # @param connection [RSolr::Client] The RSolr connection to index to.
14
+ def initialize(connection: nil, index_queue_name: 'toindex', delete_queue_name: 'todelete')
15
+ @connection = connection
16
+ @index_queue_name = index_queue_name
17
+ @delete_queue_name = delete_queue_name
18
+ @index_error_name = index_queue_name + "-error"
19
+ @delete_error_name = delete_queue_name + "-error"
20
+ end
21
+
22
+ def connection
23
+ @connection ||= default_connection
24
+ end
25
+
26
+ def save(resource:)
27
+ persist([resource])
28
+ end
29
+
30
+ def save_all(resources:)
31
+ persist(resources)
32
+ end
33
+
34
+ # Deletes a Solr Document using the ID
35
+ # @return [Array<Valkyrie::Resource>] resources which have been deleted from Solr
36
+ def delete(resource:)
37
+ connection.zadd(delete_queue_name, Time.current.to_i, resource.id.to_s)
38
+ end
39
+
40
+ # Delete the Solr index of all Documents
41
+ def wipe!
42
+ connection.del(index_queue_name)
43
+ connection.del(index_error_name)
44
+ connection.del(delete_queue_name)
45
+ connection.del(delete_error_name)
46
+ end
47
+
48
+ def reset!
49
+ self.connection = default_connection
50
+ end
51
+
52
+ def index_queue(size: 200)
53
+ set = connection.zpopmin(index_queue_name, size)
54
+ return [] if set.blank?
55
+ # we have to load these one at a time because find_all_by_id gets duplicates during wings transition
56
+ resources = set.map { |id, _time| Hyrax.query_service.find_by(id: id) }
57
+ solr_indexer = Valkyrie::IndexingAdapter.find(:solr_index)
58
+ solr_indexer.save_all(resources: resources)
59
+ solr_indexer.connection.commit
60
+ rescue
61
+ # if anything goes wrong, try to requeue the items
62
+ set.each { |id, time| connection.zadd(index_error_name, time, id) }
63
+ raise
64
+ end
65
+
66
+ # If a batch fails, try running them one at a time to get down to just records that really fail
67
+ def index_error_queue(size: 200)
68
+ @set = []
69
+ solr_indexer = Valkyrie::IndexingAdapter.find(:solr_index)
70
+
71
+ size.times do
72
+ @set = queue.connection.zpopmin(index_error_name, 1)
73
+ return [] if @set.blank?
74
+ # we have to load these one at a time because find_all_by_id gets duplicates during wings transition
75
+ resource = Hyrax.query_service.find_by(id: @set[0])
76
+ solr_indexer.save(resource: resource)
77
+ rescue
78
+ # if anything goes wrong, try to requeue the items
79
+ @set.each { |id, _time| queue.connection.zadd(index_error_name + "-twice", Time.now.to_i, id) }
80
+ end
81
+ solr_indexer.connection.commit
82
+ end
83
+
84
+ # We reach in to solr directly here to prevent needing to load the objects unnecessarily
85
+ def delete_queue(size: 200)
86
+ set = connection.zpopmin(delete_queue_name, size)
87
+ return [] if set.blank?
88
+ solr_indexer = Valkyrie::IndexingAdapter.find(:solr_index)
89
+ set.each do |id, _time|
90
+ solr_indexer.connection.delete_by_id id.to_s, { softCommit: true }
91
+ end
92
+ solr_indexer.connection.commit
93
+ rescue
94
+ # if anything goes wrong, try to requeue the items
95
+ set.each { |id, time| connection.zadd(delete_error_name, time, id) }
96
+ raise
97
+ end
98
+
99
+ # If a batch fails, try running them one at a time to get down to just records that really fail
100
+ def delete_error_queue(size: 200)
101
+ @set = []
102
+ size.times do
103
+ @set = connection.zpopmin(delete_error_name, 1)
104
+ return [] if @set.blank?
105
+ solr_indexer = Valkyrie::IndexingAdapter.find(:solr_index)
106
+ solr_indexer.connection.delete_by_id @set[0].to_s, { softCommit: true }
107
+ solr_indexer.connection.commit
108
+ rescue
109
+ # if anything goes wrong, try to requeue the items
110
+ @set.each { |id, _time| connection.zadd(delete_error_name, Time.now.to_i, id) }
111
+ end
112
+ end
113
+
114
+ def list_index
115
+ connection.zrange(index_queue_name, 0, -1, with_scores: true)
116
+ end
117
+
118
+ def list_delete
119
+ connection.zrange(delete_queue_name, 0, -1, with_scores: true)
120
+ end
121
+
122
+ def list_index_errors
123
+ connection.zrange(index_error_name, 0, -1, with_scores: true)
124
+ end
125
+
126
+ def list_delete_errors
127
+ connection.zrange(delete_error_name, 0, -1, with_scores: true)
128
+ end
129
+
130
+ private
131
+
132
+ def persist(resources)
133
+ resources.map do |r|
134
+ connection.zadd(index_queue_name, Time.current.to_i, r.id.to_s)
135
+ end
136
+ end
137
+
138
+ def default_connection
139
+ Hyrax.config.redis_connection
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -63,11 +63,10 @@ module Wings
63
63
  #
64
64
  # @param model [Class]
65
65
  # @return [Array<Valkyrie::Resource>]
66
- #
67
- # @note Due to implementation details, .find_all_of_model and .count_all_of_model may not
68
- # return the same number of results. Is that a bug? Probably.
69
66
  def find_all_of_model(model:)
70
- model_class_for(model).all.map do |obj|
67
+ ActiveFedora::Base
68
+ .where(has_model_ssim: [model_class_for(model).to_rdf_representation,
69
+ model.to_rdf_representation]).map do |obj|
71
70
  resource_factory.to_resource(object: obj)
72
71
  end
73
72
  end
data/template.rb CHANGED
@@ -4,6 +4,6 @@ insert_into_file 'config/application.rb', after: /config\.load_defaults [0-9.]+$
4
4
  "\n config.add_autoload_paths_to_load_path = true"
5
5
  end
6
6
 
7
- gem 'hyrax', '5.1.0'
7
+ gem 'hyrax', '5.2.0'
8
8
  run 'bundle install'
9
9
  generate 'hyrax:install', '-f'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hyrax
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Coyne
@@ -11,17 +11,18 @@ authors:
11
11
  - Jeremy Friesen
12
12
  - Trey Pendragon
13
13
  - Esmé Cowles
14
+ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
- date: 2025-04-23 00:00:00.000000000 Z
17
+ date: 2025-07-09 00:00:00.000000000 Z
17
18
  dependencies:
18
19
  - !ruby/object:Gem::Dependency
19
20
  name: rails
20
21
  requirement: !ruby/object:Gem::Requirement
21
22
  requirements:
22
- - - "~>"
23
+ - - ">"
23
24
  - !ruby/object:Gem::Version
24
- version: '7.2'
25
+ version: '6.1'
25
26
  - - "<"
26
27
  - !ruby/object:Gem::Version
27
28
  version: '8.0'
@@ -29,9 +30,9 @@ dependencies:
29
30
  prerelease: false
30
31
  version_requirements: !ruby/object:Gem::Requirement
31
32
  requirements:
32
- - - "~>"
33
+ - - ">"
33
34
  - !ruby/object:Gem::Version
34
- version: '7.2'
35
+ version: '6.1'
35
36
  - - "<"
36
37
  - !ruby/object:Gem::Version
37
38
  version: '8.0'
@@ -971,16 +972,16 @@ dependencies:
971
972
  name: rspec-rails
972
973
  requirement: !ruby/object:Gem::Requirement
973
974
  requirements:
974
- - - "~>"
975
+ - - ">"
975
976
  - !ruby/object:Gem::Version
976
- version: '7.0'
977
+ version: '6.1'
977
978
  type: :development
978
979
  prerelease: false
979
980
  version_requirements: !ruby/object:Gem::Requirement
980
981
  requirements:
981
- - - "~>"
982
+ - - ">"
982
983
  - !ruby/object:Gem::Version
983
- version: '7.0'
984
+ version: '6.1'
984
985
  - !ruby/object:Gem::Dependency
985
986
  name: rspec_junit_formatter
986
987
  requirement: !ruby/object:Gem::Requirement
@@ -1420,7 +1421,6 @@ files:
1420
1421
  - ".github/stale.yml"
1421
1422
  - ".github/workflows/build.yml"
1422
1423
  - ".github/workflows/lint-build-test.yml"
1423
- - ".github/workflows/main.yml"
1424
1424
  - ".github/workflows/release.yml"
1425
1425
  - ".github/workflows/test-results.yml"
1426
1426
  - ".gitignore"
@@ -2073,6 +2073,7 @@ files:
2073
2073
  - app/jobs/characterize_job.rb
2074
2074
  - app/jobs/concerns/hyrax/members_permission_job_behavior.rb
2075
2075
  - app/jobs/concerns/hyrax/permission_job_behavior.rb
2076
+ - app/jobs/concerns/hyrax/queued_job_behavior.rb
2076
2077
  - app/jobs/content_delete_event_job.rb
2077
2078
  - app/jobs/content_deposit_event_job.rb
2078
2079
  - app/jobs/content_event_job.rb
@@ -2091,6 +2092,8 @@ files:
2091
2092
  - app/jobs/hyrax/grant_read_job.rb
2092
2093
  - app/jobs/hyrax/grant_read_to_members_job.rb
2093
2094
  - app/jobs/hyrax/propagate_change_depositor_job.rb
2095
+ - app/jobs/hyrax/queued_delete_job.rb
2096
+ - app/jobs/hyrax/queued_indexing_job.rb
2094
2097
  - app/jobs/hyrax/revoke_edit_from_members_job.rb
2095
2098
  - app/jobs/hyrax/revoke_edit_job.rb
2096
2099
  - app/jobs/iiif_manifest_cache_prewarm_job.rb
@@ -2102,6 +2105,7 @@ files:
2102
2105
  - app/jobs/lease_expiry_job.rb
2103
2106
  - app/jobs/migrate_files_to_valkyrie_job.rb
2104
2107
  - app/jobs/migrate_resources_job.rb
2108
+ - app/jobs/migrate_sipity_entity_job.rb
2105
2109
  - app/jobs/resolrize_job.rb
2106
2110
  - app/jobs/stream_notifications_job.rb
2107
2111
  - app/jobs/user_edit_profile_event_job.rb
@@ -3011,6 +3015,7 @@ files:
3011
3015
  - config/initializers/kaminari_engine_patch.rb
3012
3016
  - config/initializers/listeners.rb
3013
3017
  - config/initializers/new_framework_defaults_7_2.rb
3018
+ - config/initializers/reform_rails_6_1_monkey_patch.rb
3014
3019
  - config/initializers/simple_form.rb
3015
3020
  - config/initializers/storage_adapter_initializer.rb
3016
3021
  - config/initializers/valkyrie_id_equality.rb
@@ -3371,6 +3376,7 @@ files:
3371
3376
  - lib/tasks/universal_viewer.rake
3372
3377
  - lib/tasks/workflow.rake
3373
3378
  - lib/valkyrie/indexing/null_indexing_adapter.rb
3379
+ - lib/valkyrie/indexing/redis_queue/indexing_adapter.rb
3374
3380
  - lib/valkyrie/indexing/solr/indexing_adapter.rb
3375
3381
  - lib/valkyrie/indexing_adapter.rb
3376
3382
  - lib/wings.rb
@@ -3432,6 +3438,7 @@ licenses:
3432
3438
  - Apache-2.0
3433
3439
  metadata:
3434
3440
  rubygems_mfa_required: 'true'
3441
+ post_install_message:
3435
3442
  rdoc_options: []
3436
3443
  require_paths:
3437
3444
  - lib
@@ -3446,7 +3453,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
3446
3453
  - !ruby/object:Gem::Version
3447
3454
  version: '0'
3448
3455
  requirements: []
3449
- rubygems_version: 3.6.2
3456
+ rubygems_version: 3.5.22
3457
+ signing_key:
3450
3458
  specification_version: 4
3451
3459
  summary: Hyrax is a front-end based on the robust Samvera framework, providing a user
3452
3460
  interface for common repository features. Hyrax offers the ability to create repository
@@ -1,17 +0,0 @@
1
- name: Trigger Nurax build
2
- on:
3
- workflow_dispatch:
4
- # push:
5
- # branches:
6
- # - 'main'
7
-
8
- jobs:
9
- trigger:
10
- runs-on: ubuntu-latest
11
- steps:
12
- - uses: peter-evans/repository-dispatch@v3
13
- with:
14
- token: ${{ secrets.NURAX_ACCESS_TOKEN }}
15
- event-type: push
16
- repository: samvera-labs/nurax
17
- client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}'