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
data/CONTAINERS.md CHANGED
@@ -53,7 +53,7 @@ With `docker compose up` running, any changes you make to your cloned Hyrax code
53
53
  Any changes you make to Hyrax should be tested. You can run the full test suite using the following command:
54
54
 
55
55
  ```sh
56
- docker compose exec -w /app/samvera/hyrax-engine app sh -c "bundle exec rspec"
56
+ docker compose exec -w /app/samvera/hyrax-engine web sh -c "bundle exec rspec"
57
57
  ```
58
58
 
59
59
  Let's break down the above command:
@@ -63,8 +63,8 @@ Let's break down the above command:
63
63
  <dd>Tell docker to run the following:</dd>
64
64
  <dt><code>-w /app/samvera/hyrax-engine</code></dt>
65
65
  <dd>In the working directory "/app/samvera/hyrax-engine" (e.g. your cloned Hyrax repository)</dd>
66
- <dt><code>app</code></dt>
67
- <dd>of the container named "app"</dd>
66
+ <dt><code>web</code></dt>
67
+ <dd>of the container named "web"</dd>
68
68
  <dt><code>sh -c</code>
69
69
  <dd>run the following shell script</dd>
70
70
  <dt><code>"bundle exec rspec"</code></dt>
@@ -73,26 +73,26 @@ Let's break down the above command:
73
73
 
74
74
  _**Note:**_ The `bundle exec rspec` portion of the command runs the whole test suite. See the [rspec command documentation](https://github.com/rspec/rspec-core#the-rspec-command) for how to refine your test runs.
75
75
 
76
- #### The Docker Container Named "app"
76
+ #### The Docker Container Named "web"
77
77
 
78
78
  As a developer, you may need to run commands against the Hyrax-based application and/or the Hyrax engine. Examples
79
79
  of those commands are `rails db:migrate` and `rspec`. You would run `rails db:migrate` on the Hyrax-based
80
80
  application, and `rspec` on the Hyrax engine.
81
81
 
82
- In the engine development `app` container, the `.dassie` test Hyrax-based application is setup as a docker
82
+ In the engine development `web` container, the `.dassie` test Hyrax-based application is setup as a docker
83
83
  bind mount to `/app/samvera/hyrax-webapp`, and your local development copy of Hyrax (eg. the clone [samvera/hyrax](https://github.com/samvera/hyrax)) is bound to
84
84
  `/app/samvera/hyrax-engine`. Those directories are defined as part of the [Dockerfile](Dockerfile) configuration.
85
85
  .
86
86
  What does this structure mean? Let's look at an example. The following command will list the rake tasks for the Hyrax-based application running in Docker:
87
87
 
88
88
  ```sh
89
- docker compose exec -w /app/samvera/hyrax-webapp app sh -c "bundle exec rake -T"
89
+ docker compose exec -w /app/samvera/hyrax-webapp web sh -c "bundle exec rake -T"
90
90
  ```
91
91
 
92
92
  And this command lists the rake tasks for the Hyrax engine that is in Docker:
93
93
 
94
94
  ```sh
95
- docker compose exec -w /app/samvera/hyrax-engine app sh -c "bundle exec rake -T"
95
+ docker compose exec -w /app/samvera/hyrax-engine web sh -c "bundle exec rake -T"
96
96
  ```
97
97
 
98
98
  In the two examples, note the difference in the `-w` switch. In the first case, it's referencing the Hyrax-based application. In the latter case, it's referencing the Hyrax engine.
@@ -165,13 +165,13 @@ Currently Koppie should not be used for running specs. See [Code Changes and Tes
165
165
 
166
166
  ```sh
167
167
  docker compose -f docker-compose-koppie.yml up
168
- docker compose -f docker-compose-koppie.yml exec app bundle exec rails c
168
+ docker compose -f docker-compose-koppie.yml exec web bundle exec rails c
169
169
  ```
170
170
  #### Troubleshooting Koppie
171
171
 
172
172
  If the postgres service logs show permissions errors, there may be old data from alternate versions of the postgres image. The old data volumes can deleted by using `docker compose -f docker-compose-koppie.yml down -v`
173
173
 
174
- Errors such as `exec /app/samvera/hyrax-entrypoint.sh: no such file or directory` in the app, sidekiq and db_migrate services may indicate an outdated cached hyrax-base image layer was used to build the koppie image. Try `docker compose -f docker-compose-koppie.yml build --no-cache` to rebuild all the image layers.
174
+ Errors such as `exec /app/samvera/hyrax-entrypoint.sh: no such file or directory` in the web, sidekiq and db_migrate services may indicate an outdated cached hyrax-base image layer was used to build the koppie image. Try `docker compose -f docker-compose-koppie.yml build --no-cache` to rebuild all the image layers.
175
175
 
176
176
  It was also seen on a Windows 10 host and was resolved by using the git `--core.autocrlf` option when cloning the repo.
177
177
 
@@ -217,7 +217,7 @@ Currently Sirenia should not be used for running specs. See [Code Changes and Te
217
217
 
218
218
  ```sh
219
219
  docker compose -f docker-compose-sirenia.yml up
220
- docker compose -f docker-compose-sirenia.yml exec app bundle exec rails c
220
+ docker compose -f docker-compose-sirenia.yml exec web bundle exec rails c
221
221
  ```
222
222
 
223
223
  ### Maintaining
data/Gemfile CHANGED
@@ -20,5 +20,6 @@ group :development, :test do
20
20
  gem 'rspec'
21
21
  gem 'ruby-prof', require: false
22
22
  gem 'semaphore_test_boosters'
23
- gem "simplecov", require: false
23
+ gem 'simplecov', require: false
24
+ gem 'timecop'
24
25
  end
@@ -102,6 +102,7 @@ module Hyrax
102
102
  def find_file_metadata(file_set:, use: :original_file, mime_type: nil)
103
103
  if mime_type.nil?
104
104
  use = :thumbnail_file if use == :thumbnail
105
+ use = :original_file unless Hyrax::FileMetadata::Use.keys.include?(use)
105
106
  use = Hyrax::FileMetadata::Use.uri_for(use: use)
106
107
  results = Hyrax.custom_queries.find_many_file_metadata_by_use(resource: file_set, use: use)
107
108
  else
@@ -359,8 +359,9 @@ module Hyrax
359
359
  # intersection, we get the files they added via BrowseEverything
360
360
  # that they have not removed from the upload widget.
361
361
  uploaded_files = params.fetch(:uploaded_files, [])
362
+ params.permit(selected_files: [:expires, :file_name, :url])
362
363
  selected_files = params.fetch(:selected_files, {}).values
363
- .map { |f| f.permit(:expires, :file_name, :url, {}) }
364
+
364
365
  browse_everything_urls = uploaded_files &
365
366
  selected_files.map { |f| f[:url] }
366
367
 
@@ -14,9 +14,9 @@ module Hyrax
14
14
  @works_count = @accessible_works.count
15
15
  @top_works = paginate(top_works_list, rows: 10)
16
16
  @top_file_set_downloads = paginate(top_files_list, rows: 10)
17
-
18
- @pageviews = Hyrax::Analytics.daily_events('work-view'), @downloads = Hyrax::Analytics.daily_events('file-set-download') if current_user.ability.admin?
19
-
17
+ # rubocop:disable Style/ParallelAssignment
18
+ @pageviews, @downloads = Hyrax::Analytics.daily_events('work-view'), Hyrax::Analytics.daily_events('file-set-download') if current_user.ability.admin?
19
+ # rubocop:enable Style/ParallelAssignment
20
20
  respond_to do |format|
21
21
  format.html
22
22
  format.csv { export_data }
@@ -34,7 +34,7 @@ module Hyrax
34
34
  end
35
35
  end
36
36
 
37
- private
37
+ private
38
38
 
39
39
  def accessible_works
40
40
  models = Hyrax::ModelRegistry.work_rdf_representations.map { |m| "\"#{m}\"" }
@@ -167,7 +167,7 @@ module Hyrax
167
167
  return unless @file_set.class == ::FileSet
168
168
  # We can tell if a Hyrax::FileSet was improperly cast because this AF method will
169
169
  # return nil since its parent is not a ActiveFedora work.
170
- @file_set = @file_set.valkyrie_resource if @file_set.parent&.id.nil?
170
+ @file_set = @file_set.valkyrie_resource if @file_set.respond_to?(:parent) && @file_set.parent&.id.nil?
171
171
  end
172
172
 
173
173
  def parent(file_set: curation_concern)
@@ -68,10 +68,10 @@ module Hyrax
68
68
  mailbox = UserMailbox.new(user)
69
69
  unread_notifications = mailbox.unread_count
70
70
  link_to(hyrax.notifications_path,
71
- 'aria-label' => mailbox.label(params[:locale]),
71
+ 'aria-description' => mailbox.label(params[:locale]),
72
72
  class: 'notify-number nav-link') do
73
73
  capture do
74
- concat tag.span('', class: 'fa fa-bell')
74
+ concat tag.span('', class: 'fa fa-bell', 'aria-label': t('hyrax.admin.sidebar.notifications'))
75
75
  concat "\n"
76
76
  concat tag.span(unread_notifications,
77
77
  class: count_classes_for(unread_notifications))
@@ -15,7 +15,7 @@ module Hyrax
15
15
  args[:data]['remove-text'] = args[:remove_text]
16
16
 
17
17
  args[:data][:url] = hyrax.trophy_work_path(id)
18
- link_to '#', id: 'action-highlight-work', class: args[:class], data: args[:data] do
18
+ link_to '#', id: 'action-highlight-work', role: args[:role], class: args[:class], data: args[:data] do
19
19
  yield(text)
20
20
  end
21
21
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ # Grants the user's edit access on the provided FileSet
4
+ module QueuedJobBehavior
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ queue_as Hyrax.config.ingest_queue_name
9
+ cattr_accessor :requeue_frequency
10
+ end
11
+
12
+ private
13
+
14
+ def redis_queue
15
+ Valkyrie::IndexingAdapter.find(:redis_queue)
16
+ end
17
+
18
+ def requeue(**args)
19
+ self.class.set(wait_until: (self.class.requeue_frequency || 5.minutes).from_now).perform_later(**args)
20
+ end
21
+ end
22
+ end
@@ -26,7 +26,7 @@ module Hyrax
26
26
  def apply_depositor_metadata(resource, depositor)
27
27
  depositor_id = depositor.respond_to?(:user_key) ? depositor.user_key : depositor
28
28
  resource.depositor = depositor_id if resource.respond_to? :depositor=
29
- Hyrax::AccessControlList.new(resource: resource).grant(:edit).to(::User.find_by_user_key(depositor_id)).save
29
+ resource.permission_manager.acl.grant(:edit).to(::User.find_by_user_key(depositor_id)).save
30
30
  end
31
31
  end
32
32
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ class QueuedDeleteJob < ApplicationJob
4
+ include QueuedJobBehavior
5
+
6
+ def perform(size: 200)
7
+ redis_queue.delete_queue(size: size)
8
+ requeue(size: size)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ class QueuedIndexingJob < ApplicationJob
4
+ include QueuedJobBehavior
5
+
6
+ def perform(size: 200)
7
+ redis_queue.index_queue(size: size)
8
+ requeue(size: size)
9
+ end
10
+ end
11
+ end
@@ -2,6 +2,11 @@
2
2
  # Responsible for conditionally enqueuing the file and thumbnail migration
3
3
  # logic of an ActiveFedora object.
4
4
  class MigrateFilesToValkyrieJob < Hyrax::ApplicationJob
5
+ # Define a logger for this job
6
+ def logger
7
+ FileUtils.mkdir_p(Hyrax.config.working_path)
8
+ @logger ||= Logger.new(Hyrax.config.working_path.join('migrate_files_to_valkyrie_job.log'))
9
+ end
5
10
  ##
6
11
  #
7
12
  # @param resource [Hyrax::FileSet]
@@ -33,6 +38,7 @@ class MigrateFilesToValkyrieJob < Hyrax::ApplicationJob
33
38
  # @todo should we trigger a job if the member is a child work?
34
39
  paths = Hyrax::DerivativePath.derivatives_for_reference(resource)
35
40
  paths.each do |path|
41
+ next unless File.size?(path) # skip blank files
36
42
  container = container_for(path)
37
43
  mime_type = Marcel::MimeType.for(extension: File.extname(path))
38
44
  directives = { url: path, container: container, mime_type: mime_type }
@@ -50,27 +56,33 @@ class MigrateFilesToValkyrieJob < Hyrax::ApplicationJob
50
56
 
51
57
  files = Hyrax.custom_queries.find_many_file_metadata_by_ids(ids: resource.file_ids)
52
58
  files.each do |file|
53
- # If it doesn't start with fedora, we've likely already migrated it.
54
- next unless /^fedora:/.match?(file.file_identifier.to_s)
55
- resource.file_ids.delete(file.id)
59
+ begin
60
+ # If it doesn't start with fedora, we've likely already migrated it.
61
+ next unless /^fedora:/.match?(file.file_identifier.to_s)
62
+ resource.file_ids.delete(file.id)
56
63
 
57
- Tempfile.create do |tempfile|
58
- tempfile.binmode
59
- tempfile.write(URI.open(file.file_identifier.to_s.gsub("fedora:", "http:")).read)
60
- tempfile.rewind
64
+ Tempfile.create do |tempfile|
65
+ tempfile.binmode
66
+ tempfile.write(URI.open(file.file_identifier.to_s.gsub("fedora:", "http:")).read)
67
+ tempfile.rewind
61
68
 
62
- # valkyrie_file = Hyrax.storage_adapter.upload(resource: resource, file: tempfile, original_filename: file.original_filename)
63
- valkyrie_file = Hyrax::ValkyrieUpload.file(
64
- filename: resource.label,
65
- file_set: resource,
66
- io: tempfile,
67
- use: file.pcdm_use.select {|use| Hyrax::FileMetadata::Use.use_list.include?(use)},
68
- user: User.find_or_initialize_by(User.user_key_field => resource.depositor),
69
- mime_type: file.mime_type,
70
- skip_derivatives: true
71
- )
72
- valkyrie_file = copy_attributes(valkyrie_file:, original_file: file)
73
- Hyrax.persister.save(resource: valkyrie_file)
69
+ # valkyrie_file = Hyrax.storage_adapter.upload(resource: resource, file: tempfile, original_filename: file.original_filename)
70
+ valkyrie_file = Hyrax::ValkyrieUpload.file(
71
+ filename: resource.label,
72
+ file_set: resource,
73
+ io: tempfile,
74
+ use: file.pcdm_use.select {|use| Hyrax::FileMetadata::Use.use_list.include?(use)},
75
+ user: User.find_or_initialize_by(User.user_key_field => resource.depositor),
76
+ mime_type: file.mime_type,
77
+ skip_derivatives: true
78
+ )
79
+ valkyrie_file = copy_attributes(valkyrie_file:, original_file: file)
80
+ Hyrax.persister.save(resource: valkyrie_file)
81
+ end
82
+ rescue StandardError => e
83
+ # Log errors specific to file migration
84
+ logger.error("Error migrating file #{file.id} for resource #{resource.id}: #{e.message}")
85
+ logger.error(e.backtrace.join("\n"))
74
86
  end
75
87
  end
76
88
  # reindex the file set after migrating files to include characterization info
@@ -93,9 +105,9 @@ class MigrateFilesToValkyrieJob < Hyrax::ApplicationJob
93
105
  #
94
106
  # @param filename [String] the name of the derivative file: i.e. 'x-thumbnail.jpg'
95
107
  # @return [String]
96
- def container_for(filename)
108
+ def container_for(path)
97
109
  # we want the portion between the '-' and the '.'
98
- file_blob = File.basename(filename, '.*').split('-').last
110
+ file_blob = File.basename(path, '.*').split('-').last
99
111
 
100
112
  case file_blob
101
113
  when 'thumbnail'
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # migrates a resource's sipity entity so it can be found
4
+ class MigrateSipityEntityJob < ApplicationJob
5
+ # input [String] id of a migrated resource
6
+ def perform(id:)
7
+ resource = Hyrax.query_service.find_by(id: id)
8
+ new_gid = Hyrax::GlobalID(resource).to_s
9
+ return if Sipity::Entity.find_by(proxy_for_global_id: new_gid)
10
+
11
+ work = resource.internal_resource.constantize.find(id)
12
+ original_gid = Hyrax::GlobalID(work).to_s
13
+ return if new_gid == original_gid
14
+ original_entity = Sipity::Entity.find_by(proxy_for_global_id: original_gid)
15
+ return if original_entity.nil?
16
+ original_entity.update(proxy_for_global_id: new_gid)
17
+ rescue ActiveFedora::ObjectNotFoundError
18
+ # this happens when the resource was never in Fedora so there is nothing to migrate.
19
+ # We don't want to retry the job so we don't raise an error.
20
+ end
21
+ end
@@ -404,8 +404,10 @@ module Hyrax
404
404
  .select(:source_id)
405
405
  .distinct
406
406
  .pluck(:source_id)
407
-
408
- Hyrax.custom_queries.find_ids_by_model(model: Hyrax::AdministrativeSet, ids: ids).any?
407
+ return false if ids.empty?
408
+ Hyrax::SolrQueryService.new.with_ids(ids: ids).query_result(rows: 1000)['response']['docs'].any? do |doc|
409
+ (Hyrax::ModelRegistry.admin_set_rdf_representations & doc['has_model_ssim']).present?
410
+ end
409
411
  end
410
412
 
411
413
  def registered_user?
@@ -82,9 +82,12 @@ module Hyrax
82
82
 
83
83
  # Method to return the model
84
84
  def hydra_model(classifier: nil)
85
+ # finds the model from the solr document
85
86
  model = first('has_model_ssim')&.safe_constantize
86
- model = (first('has_model_ssim')&.+ 'Resource')&.safe_constantize if Hyrax.config.valkyrie_transition?
87
- model || model_classifier(classifier).classifier(self).best_model
87
+ # this returns nil if it isn't a valid model
88
+ resource_model = (first('has_model_ssim')&.+ 'Resource')&.safe_constantize if Hyrax.config.valkyrie_transition?
89
+ # if valkyrie_transition is enabled, we generally want to use the resource model if it exists
90
+ resource_model || model || model_classifier(classifier).classifier(self).best_model
88
91
  end
89
92
 
90
93
  def depositor(default = '')
@@ -44,16 +44,31 @@ module Hyrax
44
44
 
45
45
  # @return [Array<RDF::URI>] list of all uses
46
46
  def use_list
47
- [ORIGINAL_FILE,
48
- THUMBNAIL_IMAGE,
49
- EXTRACTED_TEXT,
50
- INTERMEDIATE_FILE,
51
- PRESERVATION_FILE,
52
- SERVICE_FILE,
53
- TRANSCRIPT]
47
+ [
48
+ EXTRACTED_TEXT,
49
+ INTERMEDIATE_FILE,
50
+ ORIGINAL_FILE,
51
+ PRESERVATION_FILE,
52
+ SERVICE_FILE,
53
+ THUMBNAIL_IMAGE,
54
+ TRANSCRIPT
55
+ ]
54
56
  end
55
57
  module_function :use_list
56
58
 
59
+ def keys
60
+ [
61
+ :extracted_file,
62
+ :intermediate_file,
63
+ :original_file,
64
+ :preservation_file,
65
+ :service_file,
66
+ :thumbnail_file,
67
+ :transcript_file
68
+ ]
69
+ end
70
+ module_function :keys
71
+
57
72
  ##
58
73
  # @param use [RDF::URI, Symbol]
59
74
  #
@@ -88,7 +88,7 @@ module Hyrax
88
88
  end
89
89
 
90
90
  def unwrap_metric(metric)
91
- metric.metric_values.first.value.to_i
91
+ metric&.metric_values&.first&.value.to_i
92
92
  end
93
93
  end
94
94
  end
@@ -76,7 +76,11 @@ module Hyrax
76
76
  end
77
77
 
78
78
  def account_info
79
- @account_info ||= JSON.parse(account_json_string)
79
+ @account_info ||= if account_json_string.is_a? Hash
80
+ account_json_string
81
+ else
82
+ JSON.parse(account_json_string)
83
+ end
80
84
  end
81
85
 
82
86
  KEYS.each do |key|
@@ -63,7 +63,7 @@ module Hyrax
63
63
  def self.apply_depositor_metadata(resource, depositor)
64
64
  depositor_id = depositor.respond_to?(:user_key) ? depositor.user_key : depositor
65
65
  resource.depositor = depositor_id if resource.respond_to? :depositor=
66
- Hyrax::AccessControlList.new(resource: resource).grant(:edit).to(::User.find_by_user_key(depositor_id)).save
66
+ resource.permission_manager.acl.grant(:edit).to(::User.find_by_user_key(depositor_id)).save
67
67
  end
68
68
  private_class_method :apply_depositor_metadata
69
69
  end
@@ -107,13 +107,19 @@ class Hyrax::Characterization::ValkyrieCharacterizationService
107
107
  end
108
108
 
109
109
  # Assign values of the instance properties from the metadata mapping :prop => val
110
+ # @todo push exceptional per-property behavior into the mapping somehow?
110
111
  # @return [Hash]
111
112
  def apply_metadata(terms)
112
- terms.each_pair do |term, value|
113
- property = property_for(term)
114
- next if property.nil?
115
-
116
- Array(value).each { |v| append_property_value(property, v) }
113
+ values_by_property(terms).each do |property, values|
114
+ value = if property == :mime_type
115
+ values.last
116
+ elsif [:height, :width].include?(property)
117
+ # keep only the max height or width
118
+ values.map(&:to_i).max.to_s
119
+ else
120
+ values
121
+ end
122
+ metadata.public_send("#{property}=", value)
117
123
  end
118
124
  end
119
125
 
@@ -127,13 +133,15 @@ class Hyrax::Characterization::ValkyrieCharacterizationService
127
133
  end
128
134
  end
129
135
 
130
- ##
131
- # @todo push exceptional per-property behavior into the mapping somehow?
132
- def append_property_value(property, value)
133
- # We don't want multiple mime_types; this overwrites each time to accept last value
134
- value = Array(metadata.public_send(property)) + [value] unless property == :mime_type
135
- # We don't want multiple heights / widths, pick the max
136
- value = value.map(&:to_i).max.to_s if property == :height || property == :width
137
- metadata.public_send("#{property}=", value)
136
+ # Map each term to the corresponding property
137
+ # (multiple terms can map to the same property),
138
+ # and gather all values for each property
139
+ # @return [Hash]
140
+ def values_by_property(terms)
141
+ terms.each_with_object({}) do |(term, value), property_values|
142
+ next unless (property = property_for(term))
143
+
144
+ (property_values[property] ||= []).concat(Array(value))
145
+ end
138
146
  end
139
147
  end
@@ -8,8 +8,9 @@ module Hyrax
8
8
  [:find_ids_by_model]
9
9
  end
10
10
 
11
- def initialize(query_service:)
11
+ def initialize(query_service:, query_rows: 1_000)
12
12
  @query_service = query_service
13
+ @query_rows = query_rows
13
14
  end
14
15
 
15
16
  attr_reader :query_service
@@ -23,14 +24,38 @@ module Hyrax
23
24
  #
24
25
  # @param model [Class]
25
26
  # @param ids [Enumerable<#to_s>, Symbol]
27
+ # @param use_solr [Boolean]
26
28
  #
27
29
  # @return [Enumerable<Valkyrie::ID>]
28
- def find_ids_by_model(model:, ids: :all)
29
- return query_service.find_all_of_model(model: model).map(&:id) if ids == :all
30
+ def find_ids_by_model(model:, ids: :all, use_solr: true)
31
+ if use_solr
32
+ query_solr(model, ids)
33
+ else
34
+ return query_service.find_all_of_model(model: model).map(&:id) if ids == :all
35
+ query_service.find_many_by_ids(ids: ids).select do |resource|
36
+ resource.is_a?(model)
37
+ end.map(&:id)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def query_solr(model, ids)
44
+ return enum_for(:query_solr, model, ids) unless block_given?
45
+ model_name = Hyrax::ModelRegistry.rdf_representations_from(Array(model)).first
46
+
47
+ solr_query = "_query_:\"{!raw f=has_model_ssim}#{model_name}\""
48
+ solr_response = Hyrax::SolrService.get(solr_query, fl: 'id', rows: @query_rows)['response']
49
+
50
+ loop do
51
+ response_docs = solr_response['docs']
52
+ response_docs.select! { |doc| ids.include?(doc['id']) } unless ids == :all
53
+
54
+ response_docs.each { |doc| yield doc['id'] }
30
55
 
31
- query_service.find_many_by_ids(ids: ids).select do |resource|
32
- resource.is_a?(model)
33
- end.map(&:id)
56
+ break if (solr_response['start'] + solr_response['docs'].count) >= solr_response['numFound']
57
+ solr_response = Hyrax::SolrService.get(solr_query, fl: 'id', rows: @query_rows, start: solr_response['start'] + @query_rows)['response']
58
+ end
34
59
  end
35
60
  end
36
61
  end
@@ -33,23 +33,24 @@ module Hyrax
33
33
  # * use work the file_set is in
34
34
  # No other object types are supported by this view.
35
35
  def self.build_service_object_from(form:, ability:)
36
- if form.object.respond_to?(:model) && form.object.model.work?
36
+ obj = form.object
37
+ if obj.respond_to?(:model) && obj.model.work?
37
38
  # The provided form object is a work form.
38
- new(object: form.object, ability: ability)
39
- elsif form.object.respond_to?(:model) && form.object.model.file_set?
39
+ new(object: obj, ability: ability)
40
+ elsif obj.respond_to?(:model) && obj.model.file_set?
40
41
  # The provided form object is a FileSet form. For Valkyrie forms
41
42
  # (+Hyrax::Forms::FileSetForm+), +:in_works_ids+ is prepopulated onto
42
43
  # the form object itself. For +Hyrax::Forms::FileSetEditForm+, the
43
44
  # +: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
45
+ if obj.is_a?(Hyrax.config.file_set_form)
46
+ object_id = obj.in_works_ids.first
46
47
  new(object: Hyrax.query_service.find_by(id: object_id), ability: ability)
47
48
  else
48
- new(object: form.object.model.in_works.first, ability: ability)
49
+ new(object: obj.model.in_works.first, ability: ability)
49
50
  end
50
- elsif form.object.file_set?
51
+ elsif obj.file_set?
51
52
  # The provided form object is a FileSet.
52
- new(object: form.object.in_works.first, ability: ability)
53
+ new(object: obj.in_works.first, ability: ability)
53
54
  end
54
55
  end
55
56
 
@@ -50,9 +50,9 @@ module Hyrax
50
50
  private
51
51
 
52
52
  def create_workflow_entity!
53
- Sipity::Entity.create!(proxy_for_global_id: Hyrax::GlobalID(work).to_s,
54
- workflow: workflow_for(work),
55
- workflow_state: nil)
53
+ Sipity::Entity.find_or_create_by!(proxy_for_global_id: Hyrax::GlobalID(work).to_s,
54
+ workflow: workflow_for(work),
55
+ workflow_state: nil)
56
56
  end
57
57
 
58
58
  def assign_specific_roles_to(entity:)
@@ -8,7 +8,7 @@ class MigrateResourceService
8
8
  end
9
9
 
10
10
  def model
11
- @model || Wings::ModelRegistry.lookup(resource.class).to_s
11
+ @model ||= Wings::ModelRegistry.lookup(resource.class).to_s
12
12
  end
13
13
 
14
14
  def call
@@ -14,7 +14,8 @@
14
14
  <%= link_to "My Profile", hyrax.dashboard_profile_path(current_user), class: 'dropdown-item' %>
15
15
  <%= link_to t("hyrax.toolbar.dashboard.menu"), hyrax.dashboard_path, class: "dropdown-item" %>
16
16
  <div class="dropdown-divider"></div>
17
- <%= link_to t("hyrax.toolbar.profile.logout"), main_app.destroy_user_session_path, class: "dropdown-item" %>
17
+ <%= button_to t("hyrax.toolbar.profile.logout"), main_app.destroy_user_session_path, class: "dropdown-item",
18
+ method: ::Devise.sign_out_via %>
18
19
  </div>
19
20
  </li>
20
21
  <% else %>
@@ -1,5 +1,5 @@
1
1
  <div class="card">
2
-
2
+
3
3
  <div class="card-header">
4
4
  <h4><%= t('.top_collections') %>
5
5
  <div class="btn-group float-right">
@@ -41,15 +41,11 @@
41
41
  </tbody>
42
42
  </table>
43
43
  </div>
44
-
44
+
45
45
  <div class="card-footer">
46
46
  <div class="float-right">
47
- <%= paginate @top_collections %>
47
+ <%= paginate @top_collections, outer_window: 2, theme: 'blacklight' %>
48
48
  </div>
49
49
  </div>
50
50
 
51
51
  </div>
52
-
53
-
54
-
55
-