hyrax 5.1.0.pre.beta1 → 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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/.dassie/.env +4 -0
  3. data/.dassie/Gemfile +12 -7
  4. data/.dassie/app/controllers/application_controller.rb +4 -0
  5. data/.dassie/app/helpers/hyrax_helper.rb +4 -0
  6. data/.dassie/app/models/ability.rb +4 -0
  7. data/.dassie/app/models/user.rb +11 -0
  8. data/.dassie/app/views/shared/_footer.html.erb +17 -0
  9. data/.dassie/config/application.rb +1 -1
  10. data/.dassie/config/database.yml +18 -0
  11. data/.dassie/config/environments/production.rb +1 -1
  12. data/.dassie/config/fedora.yml +6 -6
  13. data/.dassie/config/initializers/devise.rb +1 -0
  14. data/.dassie/config/initializers/profiler.rb +5 -0
  15. data/.dassie/config/locales/hyrax.en.yml +1 -1
  16. data/.dassie/config/puma.rb +55 -5
  17. data/.dassie/config/routes.rb +2 -0
  18. data/.dassie/db/migrate/20250328100249_user_roles.rb +20 -0
  19. data/.dassie/db/schema.rb +123 -109
  20. data/.github/workflows/lint-build-test.yml +34 -5
  21. data/.koppie/.env +1 -1
  22. data/.koppie/Gemfile +10 -6
  23. data/.koppie/app/controllers/application_controller.rb +4 -0
  24. data/.koppie/app/helpers/hyrax_helper.rb +4 -0
  25. data/.koppie/app/models/ability.rb +4 -0
  26. data/.koppie/app/models/user.rb +10 -0
  27. data/.koppie/app/views/shared/_footer.html.erb +17 -0
  28. data/.koppie/config/database.yml +2 -9
  29. data/.koppie/config/environments/development.rb +9 -0
  30. data/.koppie/config/environments/production.rb +1 -1
  31. data/.koppie/config/initializers/1_valkyrie.rb +5 -5
  32. data/.koppie/config/initializers/devise.rb +1 -1
  33. data/.koppie/config/initializers/profiler.rb +5 -0
  34. data/.koppie/config/locales/hyrax.en.yml +2 -2
  35. data/.koppie/config/puma.rb +26 -7
  36. data/.koppie/config/routes.rb +2 -0
  37. data/.koppie/db/schema.rb +109 -110
  38. data/CONTAINERS.md +10 -10
  39. data/Dockerfile +108 -50
  40. data/Gemfile +2 -1
  41. data/app/controllers/concerns/hyrax/valkyrie_downloads_controller_behavior.rb +1 -0
  42. data/app/controllers/concerns/hyrax/works_controller_behavior.rb +2 -1
  43. data/app/controllers/hyrax/admin/analytics/work_reports_controller.rb +4 -4
  44. data/app/controllers/hyrax/file_sets_controller.rb +11 -0
  45. data/app/helpers/hyrax/hyrax_helper_behavior.rb +2 -2
  46. data/app/helpers/hyrax/trophy_helper.rb +1 -1
  47. data/app/jobs/concerns/hyrax/queued_job_behavior.rb +22 -0
  48. data/app/jobs/hyrax/propagate_change_depositor_job.rb +1 -1
  49. data/app/jobs/hyrax/queued_delete_job.rb +11 -0
  50. data/app/jobs/hyrax/queued_indexing_job.rb +11 -0
  51. data/app/jobs/migrate_files_to_valkyrie_job.rb +33 -21
  52. data/app/jobs/migrate_sipity_entity_job.rb +21 -0
  53. data/app/models/concerns/hyrax/ability.rb +4 -2
  54. data/app/models/concerns/hyrax/solr_document_behavior.rb +5 -2
  55. data/app/models/hyrax/file_metadata.rb +22 -7
  56. data/app/services/hyrax/analytics/ga4/base.rb +1 -1
  57. data/app/services/hyrax/analytics/ga4.rb +5 -1
  58. data/app/services/hyrax/change_depositor_service.rb +1 -1
  59. data/app/services/hyrax/characterization/valkyrie_characterization_service.rb +21 -13
  60. data/app/services/hyrax/custom_queries/find_ids_by_model.rb +31 -6
  61. data/app/services/hyrax/edit_permissions_service.rb +9 -8
  62. data/app/services/hyrax/workflow/workflow_factory.rb +3 -3
  63. data/app/services/migrate_resource_service.rb +1 -1
  64. data/app/views/_user_util_links.html.erb +2 -1
  65. data/app/views/hyrax/admin/analytics/collection_reports/_top_collections.html.erb +3 -7
  66. data/app/views/hyrax/admin/analytics/work_reports/_top_file_set_downloads.html.erb +3 -6
  67. data/app/views/hyrax/admin/analytics/work_reports/_top_works.html.erb +2 -3
  68. data/app/views/hyrax/admin/analytics/work_reports/_work_files.html.erb +1 -6
  69. data/app/views/hyrax/base/_social_media.html.erb +2 -0
  70. data/app/views/hyrax/base/iiif_viewers/_universal_viewer.html.erb +1 -1
  71. data/app/views/hyrax/dashboard/collections/_show_document_list_menu.html.erb +13 -12
  72. data/app/views/hyrax/my/_admin_set_action_menu.html.erb +31 -27
  73. data/app/views/hyrax/my/_collection_action_menu.html.erb +40 -35
  74. data/app/views/hyrax/my/_work_action_menu.html.erb +23 -22
  75. data/bin/db-migrate-seed.sh +1 -1
  76. data/bin/dev-entrypoint.sh +3 -0
  77. data/config/features.rb +50 -40
  78. data/config/initializers/indexing_adapter_initializer.rb +4 -0
  79. data/config/initializers/new_framework_defaults_7_2.rb +6 -4
  80. data/config/initializers/reform_rails_6_1_monkey_patch.rb +29 -0
  81. data/config/metadata/core_metadata.yaml +1 -0
  82. data/docker-compose-dassie.yml +6 -6
  83. data/docker-compose-koppie.yml +2 -2
  84. data/docker-compose-sirenia.yml +2 -2
  85. data/documentation/developing-your-hyrax-based-app.md +2 -2
  86. data/hyrax.gemspec +3 -3
  87. data/lib/freyja/persister.rb +11 -4
  88. data/lib/generators/hyrax/install_generator.rb +0 -5
  89. data/lib/generators/hyrax/templates/.env +1 -1
  90. data/lib/generators/hyrax/templates/config/initializers/1_valkyrie.rb +21 -19
  91. data/lib/generators/hyrax/templates/db/migrate/20170131142607_add_permission_template_to_sipity_workflow.rb.erb +1 -1
  92. data/lib/generators/hyrax/templates/db/migrate/20170810190549_update_collection_type_column_options.rb.erb +1 -1
  93. data/lib/generators/hyrax/templates/db/migrate/20230821153635_add_fields_to_counter_metric.rb.erb +1 -1
  94. data/lib/hyrax/configuration.rb +22 -7
  95. data/lib/hyrax/controlled_vocabulary/importer/language.rb +5 -1
  96. data/lib/hyrax/transactions/steps/add_file_sets.rb +2 -1
  97. data/lib/hyrax/version.rb +1 -1
  98. data/lib/hyrax.rb +1 -0
  99. data/lib/tasks/workflow.rake +1 -2
  100. data/lib/valkyrie/indexing/redis_queue/indexing_adapter.rb +144 -0
  101. data/lib/wings/valkyrie/query_service.rb +3 -4
  102. data/template.rb +1 -1
  103. metadata +28 -14
  104. data/.github/workflows/main.yml +0 -17
data/Dockerfile CHANGED
@@ -1,50 +1,98 @@
1
- ARG ALPINE_VERSION=3.21
2
- ARG RUBY_VERSION=3.3.6
3
-
4
- FROM ruby:$RUBY_VERSION-alpine$ALPINE_VERSION AS hyrax-base
5
-
6
- ARG DATABASE_APK_PACKAGE="postgresql-dev"
7
- ARG EXTRA_APK_PACKAGES="git"
8
- ARG RUBYGEMS_VERSION=""
9
-
10
- RUN addgroup -S --gid 101 app && \
11
- adduser -S -G app -u 1001 -s /bin/sh -h /app app
12
-
13
- RUN apk --no-cache upgrade && \
14
- apk --no-cache add acl \
15
- build-base \
16
- curl \
17
- gcompat \
18
- imagemagick \
19
- imagemagick-heic \
20
- imagemagick-jpeg \
21
- imagemagick-jxl \
22
- imagemagick-pdf \
23
- imagemagick-svg \
24
- imagemagick-tiff \
25
- imagemagick-webp \
26
- jemalloc \
27
- ruby-grpc \
28
- tzdata \
29
- nodejs \
30
- yarn \
31
- zip \
32
- $DATABASE_APK_PACKAGE \
33
- $EXTRA_APK_PACKAGES
1
+ ARG DEBIAN_VERSION=bookworm
2
+ ARG RUBY_VERSION=3.3
3
+
4
+ FROM ruby:$RUBY_VERSION-$DEBIAN_VERSION AS hyrax-base
5
+
6
+ RUN apt-get update && \
7
+ curl -sL "https://deb.nodesource.com/setup_20.x" | bash - && \
8
+ apt-get install -y --no-install-recommends \
9
+ acl \
10
+ build-essential \
11
+ curl \
12
+ exiftool \
13
+ ffmpeg \
14
+ ghostscript \
15
+ git \
16
+ less \
17
+ libgsf-1-dev \
18
+ libimagequant-dev \
19
+ libjemalloc2 \
20
+ libjpeg62-turbo-dev \
21
+ libopenjp2-7-dev \
22
+ libopenjp2-tools \
23
+ libpng-dev \
24
+ libpoppler-cpp-dev \
25
+ libpoppler-dev \
26
+ libpoppler-glib-dev \
27
+ libpoppler-private-dev \
28
+ libpoppler-qt5-dev \
29
+ libreoffice \
30
+ libreoffice-l10n-uk \
31
+ librsvg2-dev \
32
+ libtiff-dev \
33
+ libvips-dev \
34
+ libvips-tools \
35
+ libwebp-dev \
36
+ libxml2-dev \
37
+ lsof \
38
+ mediainfo \
39
+ netcat-openbsd \
40
+ nodejs \
41
+ perl \
42
+ poppler-utils \
43
+ postgresql-client \
44
+ rsync \
45
+ ruby-grpc \
46
+ screen \
47
+ tesseract-ocr \
48
+ tzdata \
49
+ vim \
50
+ zip \
51
+ && \
52
+ npm install --global yarn && \
53
+ apt-get clean && \
54
+ rm -rf /var/lib/apt/lists/* && \
55
+ ln -s /usr/lib/*-linux-gnu/libjemalloc.so.2 /usr/lib/libjemalloc.so.2 && \
56
+ echo "******** Packages Installed *********"
57
+
58
+ RUN bash -x -c "\
59
+ if [ $(dpkg --print-architecture) = 'amd64' ]; then \
60
+ wget https://github.com/ImageMagick/ImageMagick/releases/download/7.1.1-47/ImageMagick-82572af-gcc-x86_64.AppImage -O magick \
61
+ && chmod a+x magick \
62
+ && ./magick --appimage-extract \
63
+ && mv squashfs-root/usr/etc/ImageMagick* /etc \
64
+ && rm -rf squashfs-root/usr/share/doc \
65
+ && cp -rv squashfs-root/usr/* /usr/local \
66
+ && rm -rf magick squashfs-root ; \
67
+ else \
68
+ wget https://github.com/ImageMagick/ImageMagick/archive/refs/tags/7.1.1-47.tar.gz \
69
+ && tar xf 7.1.1-47.tar.gz \
70
+ && cd ImageMagick* \
71
+ && ./configure \
72
+ && make install \
73
+ && ldconfig /usr/local/lib \
74
+ && cd $OLDPWD \
75
+ && rm -rf 7.1.1-47.tar.gz ImageMagick* ; \
76
+ fi \
77
+ && identify -version"
34
78
 
35
79
  RUN setfacl -d -m o::rwx /usr/local/bundle && \
36
- gem update --silent --system $RUBYGEMS_VERSION
80
+ gem update --silent --system
37
81
 
38
- USER app
82
+ RUN useradd -m -u 1001 -U -s /bin/bash --home-dir /app app && \
83
+ mkdir -p /app/samvera/hyrax-webapp && \
84
+ chown -R app:app /app && \
85
+ echo "export PATH=/app/samvera/hyrax-webapp/bin:${PATH}" >> /etc/bash.bashrc
39
86
 
40
- RUN mkdir -p /app/samvera/hyrax-webapp
87
+ USER app
41
88
  WORKDIR /app/samvera/hyrax-webapp
42
89
 
43
- COPY --chown=1001:101 ./bin/*.sh /app/samvera/
90
+ COPY --chown=1001 ./bin/*.sh /app/samvera/
44
91
  ENV PATH="/app/samvera:$PATH" \
45
92
  RAILS_ROOT="/app/samvera/hyrax-webapp" \
46
93
  RAILS_SERVE_STATIC_FILES="1" \
47
- LD_PRELOAD="/usr/local/lib/libjemalloc.so.2"
94
+ LD_PRELOAD="/usr/lib/libjemalloc.so.2" \
95
+ MALLOC_CONF="dirty_decay_ms:1000,narenas:2,background_thread:true"
48
96
 
49
97
  ENTRYPOINT ["hyrax-entrypoint.sh"]
50
98
  CMD ["bundle", "exec", "puma", "-v", "-b", "tcp://0.0.0.0:3000"]
@@ -55,22 +103,23 @@ FROM hyrax-base AS hyrax
55
103
  ARG APP_PATH=.
56
104
  ARG BUNDLE_WITHOUT="development test"
57
105
 
58
- ONBUILD COPY --chown=1001:101 $APP_PATH /app/samvera/hyrax-webapp
106
+ ONBUILD COPY --chown=1001 $APP_PATH /app/samvera/hyrax-webapp
59
107
  ONBUILD RUN bundle install --jobs "$(nproc)"
60
108
  ONBUILD RUN RAILS_ENV=production SECRET_KEY_BASE=`bin/rake secret` DATABASE_URL='nulldb://nulldb' bundle exec rake assets:precompile
109
+ ARG BUILD_GITSHA
110
+ ARG BUILD_TIMESTAMP
111
+ ENV BUILD_GITSHA=$BUILD_GITSHA \
112
+ BUILD_TIMESTAMP=$BUILD_TIMESTAMP
61
113
 
62
114
 
63
115
  FROM hyrax-base AS hyrax-worker-base
64
-
65
116
  USER root
66
- RUN apk --no-cache add bash \
67
- ffmpeg \
68
- mediainfo \
69
- openjdk17-jre \
70
- perl
71
- USER app
72
117
 
73
- RUN mkdir -p /app/fits && \
118
+ RUN apt update && \
119
+ apt install -y --no-install-recommends default-jre-headless && \
120
+ apt-get clean && \
121
+ rm -rf /var/lib/apt/lists/* && \
122
+ mkdir -p /app/fits && \
74
123
  cd /app/fits && \
75
124
  wget https://github.com/harvard-lts/fits/releases/download/1.6.0/fits-1.6.0.zip -O fits.zip && \
76
125
  unzip fits.zip && \
@@ -90,6 +139,10 @@ ARG BUNDLE_WITHOUT="development test"
90
139
  ONBUILD COPY --chown=1001:101 $APP_PATH /app/samvera/hyrax-webapp
91
140
  ONBUILD RUN bundle install --jobs "$(nproc)"
92
141
  ONBUILD RUN RAILS_ENV=production SECRET_KEY_BASE=`bin/rake secret` DATABASE_URL='nulldb://nulldb' bundle exec rake assets:precompile
142
+ ARG BUILD_GITSHA
143
+ ARG BUILD_TIMESTAMP
144
+ ENV BUILD_GITSHA=$BUILD_GITSHA \
145
+ BUILD_TIMESTAMP=$BUILD_TIMESTAMP
93
146
 
94
147
 
95
148
  FROM hyrax-worker-base AS hyrax-engine-dev
@@ -98,8 +151,9 @@ USER app
98
151
  ARG BUNDLE_WITHOUT=
99
152
  ENV HYRAX_ENGINE_PATH=/app/samvera/hyrax-engine
100
153
 
101
- COPY --chown=1001:101 .dassie /app/samvera/hyrax-webapp
102
- COPY --chown=1001:101 . /app/samvera/hyrax-engine
154
+ COPY --chown=1001 .dassie /app/samvera/hyrax-webapp
155
+ COPY --chown=1001 .koppie /app/samvera/hyrax-koppie
156
+ COPY --chown=1001 . /app/samvera/hyrax-engine
103
157
 
104
158
  RUN bundle -v && \
105
159
  BUNDLE_GEMFILE=Gemfile.dassie bundle install --jobs "$(nproc)" && yarn && \
@@ -107,4 +161,8 @@ RUN bundle -v && \
107
161
  yarn cache clean
108
162
 
109
163
  ENTRYPOINT ["dev-entrypoint.sh"]
110
- CMD ["bundle", "exec", "puma", "-v", "-b", "tcp://0.0.0.0:3000"]
164
+ CMD ["bundle", "exec", "puma", "-v", "-b", "tcp://0.0.0.0:3000"]
165
+ ARG BUILD_GITSHA
166
+ ARG BUILD_TIMESTAMP
167
+ ENV BUILD_GITSHA=$BUILD_GITSHA \
168
+ BUILD_TIMESTAMP=$BUILD_TIMESTAMP
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}\"" }
@@ -9,6 +9,10 @@ module Hyrax
9
9
 
10
10
  before_action :authenticate_user!, except: [:show, :citation, :stats]
11
11
  load_and_authorize_resource class: Hyrax.config.file_set_class
12
+ # If Hyrax.config.file_set_class is set to ::FileSet, the load above will force-cast
13
+ # the instance as a ::FileSet, even if it is a Hyrax::FileSet. Re-cast it back to
14
+ # prevent method errors and nil objects later
15
+ before_action :cast_file_set
12
16
  before_action :build_breadcrumbs, only: [:show, :edit, :stats]
13
17
  before_action do
14
18
  blacklight_config.track_search_session = false
@@ -159,6 +163,13 @@ module Hyrax
159
163
  attrs
160
164
  end
161
165
 
166
+ def cast_file_set
167
+ return unless @file_set.class == ::FileSet
168
+ # We can tell if a Hyrax::FileSet was improperly cast because this AF method will
169
+ # return nil since its parent is not a ActiveFedora work.
170
+ @file_set = @file_set.valkyrie_resource if @file_set.respond_to?(:parent) && @file_set.parent&.id.nil?
171
+ end
172
+
162
173
  def parent(file_set: curation_concern)
163
174
  @parent ||=
164
175
  case file_set
@@ -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