iiif_print 3.2.0 → 3.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 01fd18bb505838305103b589e56d196fbc346eb00390de671c53bb15083a39d6
4
- data.tar.gz: c7bf87ae11b5ae00b597076254d897c9519fc2bf9facd777763861dcca511adf
3
+ metadata.gz: 2ccac310b7d9a84e367cec7ea055b4b2b9af2d9817cbef9ccfaf3c68d467ce40
4
+ data.tar.gz: 621bb63d8990014bd1a4842ef6897628386515e6a3bcc76f0d212ba59a318f0e
5
5
  SHA512:
6
- metadata.gz: 3eee1a13ecdf5693e0dd987a9331d527fcf884d0c8dc10ea12b14f7b99165a3f13b712ac5cfddb6d1579fbd33ce6fc0945c1bc2c3195ff1aff7c56637e3aee46
7
- data.tar.gz: 1a85675fa01612c0fc9115e2f1cf524fae745d6a2c4b8b897b283c4272bd1583ef3c40e5e9b605591cc36a641a4f8aedb8c5b022a90534bbb31f725aa670e8be
6
+ metadata.gz: 687953bee3a35e17c5ed41e34e29324c7166736397356131f40f3e56b7f623004f9c07a8ff55019347288811740e8f3064dd5673f71357e4d6f153a79dcf8bb6
7
+ data.tar.gz: cdb91f7cbe9a6fd603f3ad5e38058dac9e8da93f1d10bd932e0a843deae24cb830f090c533b9e7bfb4dbdd9eb1fcd8509377d5bc34db41df92cd5321e19ee9bc
data/Gemfile.lock CHANGED
@@ -1,13 +1,12 @@
1
1
  PATH
2
2
  remote: /app/samvera/hyrax-webapp
3
3
  specs:
4
- iiif_print (3.2.0)
4
+ iiif_print (3.2.1)
5
5
  blacklight_iiif_search (>= 1.0, < 3.0)
6
6
  derivative-rodeo (~> 0.5)
7
7
  hyrax (>= 2.5, < 6)
8
8
  nokogiri (>= 1.13.2)
9
9
  rdf-vocab (~> 3.0)
10
- request_store (>= 1.0)
11
10
 
12
11
  GEM
13
12
  remote: https://rubygems.org/
@@ -66,7 +65,7 @@ GEM
66
65
  activemodel (= 5.2.8.1)
67
66
  activesupport (= 5.2.8.1)
68
67
  arel (>= 9.0)
69
- activerecord-import (2.2.0)
68
+ activerecord-import (2.3.0)
70
69
  activerecord (>= 4.2)
71
70
  activestorage (5.2.8.1)
72
71
  actionpack (= 5.2.8.1)
@@ -81,19 +80,19 @@ GEM
81
80
  public_suffix (>= 2.0.2, < 8.0)
82
81
  almond-rails (0.3.0)
83
82
  rails (>= 4.2)
84
- anonymous_loader (0.1.2)
85
- version_gem (~> 1.1, >= 1.1.13)
83
+ anonymous_loader (0.1.3)
84
+ version_gem (~> 1.1, >= 1.1.14)
86
85
  arel (9.0.0)
87
86
  ast (2.4.2)
88
- auth-sanitizer (0.2.2)
89
- version_gem (~> 1.1, >= 1.1.10)
87
+ auth-sanitizer (0.2.3)
88
+ version_gem (~> 1.1, >= 1.1.14)
90
89
  autoprefixer-rails (10.4.16.0)
91
90
  execjs (~> 2)
92
91
  awesome_nested_set (3.9.0)
93
92
  activerecord (>= 4.0.0, < 8.2)
94
93
  aws-eventstream (1.4.0)
95
- aws-partitions (1.1262.0)
96
- aws-sdk-core (3.252.0)
94
+ aws-partitions (1.1293.0)
95
+ aws-sdk-core (3.254.1)
97
96
  aws-eventstream (~> 1, >= 1.3.0)
98
97
  aws-partitions (~> 1, >= 1.992.0)
99
98
  aws-sigv4 (~> 1.9)
@@ -101,15 +100,15 @@ GEM
101
100
  bigdecimal
102
101
  jmespath (~> 1, >= 1.6.1)
103
102
  logger
104
- aws-sdk-kms (1.129.0)
105
- aws-sdk-core (~> 3, >= 3.248.0)
103
+ aws-sdk-kms (1.130.0)
104
+ aws-sdk-core (~> 3, >= 3.254.0)
106
105
  aws-sigv4 (~> 1.5)
107
- aws-sdk-s3 (1.226.0)
108
- aws-sdk-core (~> 3, >= 3.248.0)
106
+ aws-sdk-s3 (1.230.0)
107
+ aws-sdk-core (~> 3, >= 3.254.1)
109
108
  aws-sdk-kms (~> 1)
110
109
  aws-sigv4 (~> 1.5)
111
- aws-sdk-sqs (1.116.0)
112
- aws-sdk-core (~> 3, >= 3.248.0)
110
+ aws-sdk-sqs (1.117.0)
111
+ aws-sdk-core (~> 3, >= 3.254.0)
113
112
  aws-sigv4 (~> 1.5)
114
113
  aws-sigv4 (1.12.1)
115
114
  aws-eventstream (~> 1, >= 1.0.2)
@@ -123,7 +122,7 @@ GEM
123
122
  i18n
124
123
  bcrypt (3.1.21)
125
124
  benchmark (0.3.0)
126
- bigdecimal (4.1.2)
125
+ bigdecimal (4.1.3)
127
126
  bindex (0.8.1)
128
127
  bixby (5.0.2)
129
128
  rubocop (= 1.28.2)
@@ -193,7 +192,7 @@ GEM
193
192
  thor (>= 0.19.4, < 2.0)
194
193
  tins (~> 1.6)
195
194
  crass (1.0.6)
196
- csv (3.3.5)
195
+ csv (3.3.6)
197
196
  database_cleaner (1.99.0)
198
197
  date (3.3.4)
199
198
  declarative (0.0.20)
@@ -354,8 +353,8 @@ GEM
354
353
  logger
355
354
  hiredis (0.6.3)
356
355
  htmlentities (4.4.2)
357
- http_logger (1.0.2)
358
- httparty (0.24.2)
356
+ http_logger (1.0.3)
357
+ httparty (0.24.3)
359
358
  csv
360
359
  mini_mime (>= 1.0.0)
361
360
  multi_xml (>= 0.5.2)
@@ -468,7 +467,7 @@ GEM
468
467
  activesupport (>= 3.2.18)
469
468
  faraday (>= 0.9)
470
469
  json
471
- iiif_manifest (1.6.0)
470
+ iiif_manifest (1.7.0)
472
471
  activesupport (>= 4)
473
472
  jaro_winkler (1.5.6)
474
473
  jbuilder (2.12.0)
@@ -498,7 +497,7 @@ GEM
498
497
  json-ld-preloaded (3.1.6)
499
498
  json-ld (~> 3.1)
500
499
  rdf (~> 3.1)
501
- json-schema (6.2.0)
500
+ json-schema (6.2.1)
502
501
  addressable (~> 2.8)
503
502
  bigdecimal (>= 3.1, < 5)
504
503
  jwt (2.10.3)
@@ -590,7 +589,7 @@ GEM
590
589
  mime-types (3.7.0)
591
590
  logger
592
591
  mime-types-data (~> 3.2025, >= 3.2025.0507)
593
- mime-types-data (3.2026.0414)
592
+ mime-types-data (3.2026.1006)
594
593
  mini_magick (4.13.2)
595
594
  mini_mime (1.1.5)
596
595
  mini_portile2 (2.8.5)
@@ -622,16 +621,17 @@ GEM
622
621
  nokogiri (1.15.5)
623
622
  mini_portile2 (~> 2.8.2)
624
623
  racc (~> 1.4)
625
- oauth (1.1.7)
626
- auth-sanitizer (~> 0.2, >= 0.2.1)
624
+ oauth (1.1.8)
625
+ anonymous_loader (~> 0.1, >= 0.1.3)
626
+ auth-sanitizer (~> 0.2, >= 0.2.3)
627
627
  base64 (~> 0.1)
628
- oauth-tty (~> 1.0, >= 1.0.10)
629
- snaky_hash (~> 2.0, >= 2.0.5)
630
- version_gem (~> 1.1, >= 1.1.12)
631
- oauth-tty (1.0.12)
632
- anonymous_loader (~> 0.1, >= 0.1.1)
633
- auth-sanitizer (~> 0.2, >= 0.2.2)
634
- version_gem (~> 1.1, >= 1.1.12)
628
+ oauth-tty (~> 1.0, >= 1.0.13)
629
+ snaky_hash (~> 2.0, >= 2.0.7)
630
+ version_gem (~> 1.1, >= 1.1.14)
631
+ oauth-tty (1.0.13)
632
+ anonymous_loader (~> 0.1, >= 0.1.3)
633
+ auth-sanitizer (~> 0.2, >= 0.2.3)
634
+ version_gem (~> 1.1, >= 1.1.14)
635
635
  oauth2 (1.4.11)
636
636
  faraday (>= 0.17.3, < 3.0)
637
637
  jwt (>= 1.0, < 3.0)
@@ -891,9 +891,9 @@ GEM
891
891
  simplecov-html (~> 0.10.0)
892
892
  simplecov-html (0.10.2)
893
893
  slop (4.10.1)
894
- snaky_hash (2.0.6)
894
+ snaky_hash (2.0.7)
895
895
  hashie (>= 0.1.0, < 6)
896
- version_gem (>= 1.1.8, < 3)
896
+ version_gem (~> 1.1, >= 1.1.14)
897
897
  solargraph (0.48.0)
898
898
  backport (~> 1.2)
899
899
  benchmark
@@ -944,7 +944,7 @@ GEM
944
944
  matrix (~> 0.4)
945
945
  rdf (~> 3.2)
946
946
  sync (0.5.0)
947
- temple (0.10.4)
947
+ temple (0.10.7)
948
948
  term-ansicolor (1.7.1)
949
949
  tins (~> 1.0)
950
950
  terminal-table (4.0.0)
@@ -972,7 +972,7 @@ GEM
972
972
  uglifier (4.2.0)
973
973
  execjs (>= 0.3.0, < 3)
974
974
  unicode-display_width (2.5.0)
975
- unicode-types (1.11.0)
975
+ unicode-types (1.12.0)
976
976
  valkyrie (2.2.0)
977
977
  activemodel
978
978
  activesupport
@@ -988,7 +988,7 @@ GEM
988
988
  rdf-vocab
989
989
  reform (~> 2.2)
990
990
  reform-rails
991
- version_gem (1.1.13)
991
+ version_gem (1.1.15)
992
992
  warden (1.2.9)
993
993
  rack (>= 2.0.9)
994
994
  web-console (3.7.0)
@@ -1,7 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IiifPrint
4
+ # @deprecated IiifPrint has not applied this since 3.0; Hyrax::WorkShowPresenter#iiif_viewer? now counts
5
+ # images on child works. Removed in IiifPrint 4.0.
4
6
  module WorkShowPresenterDecorator
7
+ DEPRECATION_MESSAGE = "IiifPrint::WorkShowPresenterDecorator is deprecated and will be removed in IiifPrint 4.0; " \
8
+ "Hyrax::WorkShowPresenter#iiif_viewer? now counts images on child works."
9
+
10
+ def self.prepended(_base)
11
+ Deprecation.warn(self, DEPRECATION_MESSAGE)
12
+ end
13
+
14
+ def self.included(_base)
15
+ Deprecation.warn(self, DEPRECATION_MESSAGE)
16
+ end
17
+
5
18
  delegate :member_ids, to: :solr_document
6
19
  alias file_set_ids member_ids
7
20
 
@@ -65,14 +65,19 @@ module IiifPrint
65
65
  end
66
66
 
67
67
  def sanitize_v3(hash:, presenter:, solr_doc_hits:)
68
- hash['label']['none'].map! { |text| CGI.unescapeHTML(sanitize_value(text)) } if hash.key('label')
68
+ sanitize_v3_label(hash)
69
69
  hash['items'].each do |canvas|
70
- canvas['label']['none'].map! { |text| CGI.unescapeHTML(sanitize_value(text)) }
70
+ sanitize_v3_label(canvas)
71
71
  apply_metadata_to_canvas(canvas: canvas, presenter: presenter, solr_doc_hits: solr_doc_hits)
72
72
  end
73
73
  hash
74
74
  end
75
75
 
76
+ # A v3 label is a language map: { 'none' => [...] }, { 'en' => [...] }, ...
77
+ def sanitize_v3_label(node)
78
+ node['label']&.each_value { |texts| texts.map! { |text| CGI.unescapeHTML(sanitize_value(text)) } }
79
+ end
80
+
76
81
  def apply_metadata_to_canvas(canvas:, presenter:, solr_doc_hits:)
77
82
  return if @child_works.empty?
78
83
 
@@ -152,6 +157,6 @@ module IiifPrint
152
157
  results
153
158
  end
154
159
  end
155
- # rubocop:enable Metrics/ClassLength
160
+ # rubocop:enable Metrics/ModuleLength
156
161
  end
157
162
  Hyrax::ManifestBuilderService.prepend(IiifPrint::ManifestBuilderServiceDecorator)
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # OVERRIDE Hyrax 5.0.0rc2 to add file_set.iiif_print_conditionally_destroy_spawned_children with user args
3
+ # OVERRIDE Hyrax 5.3.1
4
+ # - add file_set.iiif_print_conditionally_destroy_spawned_children with user args
5
+ # - skip file_set.remove_from_work when WorkDestroy can repair the work itself (samvera/hyrax#7682)
6
+ # - fail without a user, as Hyrax does from samvera/hyrax#7682
4
7
 
5
8
  module Hyrax
6
9
  module Transactions
@@ -16,19 +19,39 @@ module Hyrax
16
19
  def call(resource, user: nil)
17
20
  return Failure(:resource_not_persisted) unless resource.persisted?
18
21
 
22
+ # OVERRIDE: iiif_print step args, and remove_from_work only when needed
23
+ destroy = iiif_print_file_set_destroy(user: user)
19
24
  @query_service.custom_queries.find_child_file_sets(resource: resource).each do |file_set|
20
- return Failure[:failed_to_delete_file_set, file_set] unless
21
- Hyrax::Transactions::Container['file_set.destroy']
22
- .with_step_args('file_set.remove_from_work' => { user: user },
23
- 'file_set.delete' => { user: user },
24
- 'file_set.iiif_print_conditionally_destroy_spawned_children' => { user: user })
25
- .call(file_set).success?
25
+ # OVERRIDE: require a user
26
+ return Failure[:failed_to_delete_file_set, file_set] unless user && destroy.call(file_set).success?
26
27
  rescue ::Ldp::Gone
27
28
  nil
28
29
  end
29
30
 
30
31
  Success(resource)
31
32
  end
33
+
34
+ private
35
+
36
+ def iiif_print_file_set_destroy(user:)
37
+ transaction = Hyrax::Transactions::Container['file_set.destroy'].dup
38
+ step_args = { 'file_set.delete' => { user: user },
39
+ 'file_set.iiif_print_conditionally_destroy_spawned_children' => { user: user } }
40
+ if work_destroy_repairs_members?
41
+ # The work is about to be deleted; removing each file set from it would only re-save
42
+ # and re-index it, and queue update events for an object that will be gone.
43
+ transaction.steps -= ['file_set.remove_from_work']
44
+ else
45
+ step_args['file_set.remove_from_work'] = { user: user }
46
+ end
47
+ transaction.with_step_args(**step_args)
48
+ end
49
+
50
+ # Without this repair, a failed later step would leave the surviving work listing
51
+ # file sets that no longer exist, so they must be removed from it as they go.
52
+ def work_destroy_repairs_members?
53
+ Hyrax::Transactions::WorkDestroy.private_method_defined?(:remove_missing_members)
54
+ end
32
55
  end
33
56
  end
34
57
  end
data/iiif_print.gemspec CHANGED
@@ -25,7 +25,6 @@ SUMMARY
25
25
  spec.add_dependency 'hyrax', '>= 2.5', '< 6'
26
26
  spec.add_dependency 'nokogiri', '>=1.13.2'
27
27
  spec.add_dependency 'rdf-vocab', '~> 3.0'
28
- spec.add_dependency 'request_store', '>= 1.0'
29
28
 
30
29
  spec.add_development_dependency 'bixby'
31
30
  spec.add_development_dependency 'database_cleaner', '~> 1.3'
@@ -24,12 +24,10 @@ module IiifPrint
24
24
 
25
25
  # rubocop:enable Naming/PredicateName
26
26
  def show_parents_only(solr_parameters)
27
- query = if blacklight_params["include_child_works"] == 'true'
28
- IiifPrint.solr_construct_query(is_child_bsi: 'true')
29
- else
30
- IiifPrint.solr_construct_query(is_child_bsi: nil)
31
- end
32
- solr_parameters[:fq] += [query]
27
+ return if blacklight_params["include_child_works"] == 'true'
28
+
29
+ # Only child works are left out; a work indexed with is_child_bsi false is not a child.
30
+ solr_parameters[:fq] += ['-is_child_bsi:true']
33
31
  end
34
32
  end
35
33
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IiifPrint
4
+ ##
5
+ # Per-request state. Rails resets it around every request and job, where RequestStore was only cleared by its
6
+ # Rack middleware and so lived as long as a job's thread.
7
+ class Current < ActiveSupport::CurrentAttributes
8
+ attribute :anonymous_ability, :presenters, :view_definitions
9
+ end
10
+ end
@@ -2,7 +2,6 @@ require 'active_fedora'
2
2
  require 'hyrax'
3
3
  require 'blacklight_iiif_search'
4
4
  require 'derivative_rodeo'
5
- require 'request_store'
6
5
 
7
6
  module IiifPrint
8
7
  # module constants:
@@ -43,11 +42,10 @@ module IiifPrint
43
42
  # To configure specific services, inject each service, in desired order
44
43
  # to IiifPrint::PluggableDerivativeService.plugins array.
45
44
 
46
- Hyrax::DerivativeService.services.unshift(
47
- IiifPrint::PluggableDerivativeService
48
- )
45
+ derivative_services = Hyrax::DerivativeService.services
46
+ derivative_services.unshift(IiifPrint::PluggableDerivativeService) unless derivative_services.include?(IiifPrint::PluggableDerivativeService)
49
47
 
50
- Hyrax.publisher.subscribe(IiifPrint::Listener.new) if Hyrax.respond_to?(:publisher)
48
+ IiifPrint.subscribe_listener if Hyrax.respond_to?(:publisher)
51
49
 
52
50
  # Load locales early so decorators can use them during initialization
53
51
  I18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.yml'),
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IiifPrint
4
+ module Flexibility
5
+ ##
6
+ # A compound field's entries, one paragraph each, rendered the way its show page renders them.
7
+ class CompoundValues
8
+ # @return [Boolean] whether the entry has no value to show
9
+ def self.blank_entry?(entry)
10
+ Array(entry.try(:values) || entry).none?(&:present?)
11
+ end
12
+
13
+ # @param field_name [Symbol]
14
+ # @param presenter [#solr_document] the record's show presenter
15
+ # @param sanitize [#call] makes rendered html IIIF-safe
16
+ def initialize(field_name, presenter:, sanitize:)
17
+ @field_name = field_name
18
+ @presenter = presenter
19
+ @sanitize = sanitize
20
+ end
21
+
22
+ # @param entries [Array<Hash>]
23
+ # @return [String] html
24
+ def to_html(entries)
25
+ subproperties = compound_subproperties
26
+ entries.reject { |entry| self.class.blank_entry?(entry) }
27
+ .map { |entry| "<p>#{entry_html(entry, subproperties)}</p>" }.join
28
+ end
29
+
30
+ private
31
+
32
+ # As Hyrax's compound renderer, and any decorator the app gives it, renders the entry, with a line break where
33
+ # each innermost block ended, since IIIF allows no blocks.
34
+ def entry_html(entry, subproperties)
35
+ return plain_html(entry) unless defined?(Hyrax::Renderers::CompoundAttributeRenderer)
36
+
37
+ renderer = Hyrax::Renderers::CompoundAttributeRenderer.new(@field_name, [entry], subproperties: subproperties)
38
+ fragment = Nokogiri::HTML.fragment(renderer.render_value)
39
+ fragment.css('div').each { |div| div.add_next_sibling('<br>') unless div.at_css('div') }
40
+ @sanitize.call(fragment.to_html).gsub(/\s*<br>\s*/, '<br>').sub(/(<br>)+\z/, '')
41
+ end
42
+
43
+ def plain_html(entry)
44
+ Array(entry.try(:values) || entry).reject(&:blank?).map { |value| ERB::Util.h(value) }.join('<br>')
45
+ end
46
+
47
+ # The lookup Hyrax's PresentsAttributes#compound_subproperties_for makes, which is private.
48
+ def compound_subproperties
49
+ return unless defined?(Hyrax::CompoundSchema) && @presenter.respond_to?(:solr_document)
50
+
51
+ Hyrax::CompoundSchema.for_solr_document(@presenter.solr_document).definition_for(@field_name)&.fetch(:subproperties, nil)
52
+ rescue StandardError => e
53
+ Rails.logger.debug("IiifPrint could not read #{@field_name}'s compound sub-properties: #{e.message}")
54
+ nil
55
+ end
56
+ end
57
+ end
58
+ end
@@ -52,6 +52,7 @@ module IiifPrint
52
52
  !(options[:admin_only] || options[:editor_only] || options[:show_page] == false)
53
53
  end
54
54
 
55
+ # Mirrors Hyrax's AttributesHelper#conform_options, so a field is labeled as its show page labels it.
55
56
  def label(name, options)
56
57
  labels = options[:display_label] || {}
57
58
  label = (labels[I18n.locale.to_s] || labels['default'] || name).to_s
@@ -22,16 +22,16 @@ module IiifPrint
22
22
  @autolink = autolink
23
23
  end
24
24
 
25
- # @return [Array] the field's values as stored, blanks dropped
25
+ # @return [Array] the field's values as stored, blanks and all-blank compound entries dropped
26
26
  def raw(field_name)
27
- Array(@presenter.try(field_name)).reject(&:blank?)
27
+ Array(@presenter.try(field_name)).reject { |value| CompoundValues.blank_entry?(value) }
28
28
  end
29
29
 
30
30
  # @return [Array<String>] the field's values as html
31
31
  def rendered(field_name, options)
32
32
  options ||= {}
33
33
  values = Array(@presenter.try(field_name)).reject(&:blank?)
34
- return [values.map { |entry| "<p>#{compound_value(field_name, entry)}</p>" }.join] if options[:render_as].to_s == 'compound'
34
+ return [compound_values(field_name, values)] if options[:render_as].to_s == 'compound'
35
35
 
36
36
  labels = labels_for(field_name, values)
37
37
  values.map { |value| value(field_name, value.to_s, labels[value.to_s], options) }
@@ -127,17 +127,8 @@ module IiifPrint
127
127
  url
128
128
  end
129
129
 
130
- # One entry as Hyrax's compound renderer, and any decorator the app gives it, renders it on the show page, with
131
- # a line break where each innermost block ended, since IIIF allows no blocks.
132
- def compound_value(field_name, entry)
133
- renderer = Hyrax::Renderers::CompoundAttributeRenderer.new(field_name, [entry], subproperties: compound_subproperties(field_name))
134
- fragment = Nokogiri::HTML.fragment(renderer.render_value)
135
- fragment.css('div').each { |div| div.add_next_sibling('<br>') unless div.at_css('div') }
136
- sanitize_html(fragment.to_html).gsub(/\s*<br>\s*/, '<br>').sub(/(<br>)+\z/, '')
137
- end
138
-
139
- def compound_subproperties(field_name)
140
- @presenter.send(:compound_subproperties_for, field_name) if @presenter.respond_to?(:compound_subproperties_for, true)
130
+ def compound_values(field_name, entries)
131
+ CompoundValues.new(field_name, presenter: @presenter, sanitize: method(:sanitize_html)).to_html(entries)
141
132
  end
142
133
 
143
134
  def link(href, text)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "iiif_print/flexibility/fields"
4
+ require "iiif_print/flexibility/compound_values"
4
5
  require "iiif_print/flexibility/values"
5
6
 
6
7
  module IiifPrint
@@ -15,40 +16,57 @@ module IiifPrint
15
16
  Hyrax.config.try(:flexible?) && document.try(:flexible?)
16
17
  end
17
18
 
19
+ ##
20
+ # One per request for each record and ability, since both the fields and their values read from it.
18
21
  def presenter_for(document, ability)
19
- return Hyrax::FileSetPresenter.new(document, ability) if document.file_set?
20
-
21
- controller = "Hyrax::#{document.hydra_model.model_name.collection.camelize}Controller".safe_constantize
22
- (controller&.show_presenter || Hyrax::WorkShowPresenter).new(document, ability)
22
+ presenters = Current.presenters ||= {}
23
+ presenters[[document.id, ability]] ||= build_presenter(document, ability)
23
24
  end
24
25
 
25
26
  ##
26
27
  # The profile's view definitions for the record, plus its title, description and abstract when the profile
27
- # gives them no view block, since the show page shows those outside its field list. Read once per request
28
+ # gives them no view block and allows them in the record's contexts, since the show page shows those outside
29
+ # its field list. Read once per request
28
30
  # for each tenant, schema, version and context; each tenant numbers its own profile versions.
29
31
  def view_definitions_for(document)
30
- cache = RequestStore.store[:iiif_print_view_definitions] ||= {}
32
+ cache = Current.view_definitions ||= {}
31
33
  version = document.try(:schema_version)
32
34
  contexts = document.try(:contexts)
33
35
  tenant = Apartment::Tenant.current if defined?(Apartment::Tenant)
34
36
  cache[[tenant, schema_name(document), version, contexts]] ||= begin
35
37
  definitions = Hyrax::Schema.m3_schema_loader.view_definitions_for(schema: schema_name(document), version: version, contexts: contexts)
36
- lead_definitions(document, definitions).merge(definitions.to_h.symbolize_keys)
38
+ lead_definitions(document, definitions, contexts).merge(definitions.to_h.symbolize_keys)
37
39
  end
38
40
  end
39
41
 
40
42
  private
41
43
 
44
+ def build_presenter(document, ability)
45
+ return Hyrax::FileSetPresenter.new(document, ability) if document.file_set?
46
+
47
+ controller = "Hyrax::#{document.hydra_model.model_name.collection.camelize}Controller".safe_constantize
48
+ (controller&.show_presenter || Hyrax::WorkShowPresenter).new(document, ability)
49
+ end
50
+
42
51
  LEAD_FIELDS = %i[title description abstract].freeze
43
52
 
44
- def lead_definitions(document, definitions)
53
+ def lead_definitions(document, definitions, contexts)
45
54
  attributes = profile_attributes(document)
46
55
  (LEAD_FIELDS - definitions.to_h.keys.map(&:to_sym)).each_with_object({}) do |name, lead|
47
56
  config = attributes[name.to_s]
48
- lead[name] = Hyrax::SchemaLoader::AttributeDefinition.new(name.to_s, config).view_options if config
57
+ next unless config && in_context?(config, contexts)
58
+
59
+ lead[name] = Hyrax::SchemaLoader::AttributeDefinition.new(name.to_s, config).view_options
49
60
  end
50
61
  end
51
62
 
63
+ # The filter Hyrax's M3SchemaLoader applies to every field the profile gives a view block.
64
+ def in_context?(config, contexts)
65
+ return true if config['context'].blank?
66
+
67
+ (Array(contexts) & Array(config['context'])).any?
68
+ end
69
+
52
70
  def profile_attributes(document)
53
71
  schema = Hyrax::FlexibleSchema.find_by(id: document.try(:schema_version)) || Hyrax::FlexibleSchema.order(:created_at).last
54
72
  schema.try(:attributes_for, schema_name(document)) || {}
@@ -6,12 +6,10 @@ module IiifPrint
6
6
  self.default_processor_chain += [:show_parents_only]
7
7
 
8
8
  def show_parents_only(solr_parameters)
9
- query = if blacklight_params["include_child_works"] == 'true'
10
- IiifPrint.solr_construct_query(is_child_bsi: 'true')
11
- else
12
- IiifPrint.solr_construct_query(is_child_bsi: nil)
13
- end
14
- solr_parameters[:fq] += [query]
9
+ return if blacklight_params["include_child_works"] == 'true'
10
+
11
+ # Only child works are left out; a work indexed with is_child_bsi false is not a child.
12
+ solr_parameters[:fq] += ['-is_child_bsi:true']
15
13
  end
16
14
  end
17
15
  end
@@ -60,7 +60,12 @@ module IiifPrint
60
60
  parent_of_file_set&.member_of&.find(&:work?)
61
61
  end
62
62
 
63
+ # @deprecated Use Hyrax::SolrQueryBuilderService.construct_query, or ActiveFedora::SolrQueryBuilder.construct_query
64
+ # on a Hyrax without it; removed in IiifPrint 4.0.
63
65
  def self.solr_construct_query(*args)
66
+ Deprecation.warn(self, "solr_construct_query is deprecated and will be removed in IiifPrint 4.0; " \
67
+ "use Hyrax::SolrQueryBuilderService.construct_query instead, or " \
68
+ "ActiveFedora::SolrQueryBuilder.construct_query on a Hyrax without it.")
64
69
  if defined?(Hyrax::SolrQueryBuilderService)
65
70
  Hyrax::SolrQueryBuilderService.construct_query(*args)
66
71
  else
@@ -68,7 +68,10 @@ module IiifPrint
68
68
  Hyrax.query_service.find_parents(resource: parent).first
69
69
  end
70
70
 
71
+ # @deprecated Use Hyrax::SolrQueryBuilderService.construct_query; removed in IiifPrint 4.0.
71
72
  def self.solr_construct_query(*args)
73
+ Deprecation.warn(self, "solr_construct_query is deprecated and will be removed in IiifPrint 4.0; " \
74
+ "use Hyrax::SolrQueryBuilderService.construct_query instead.")
72
75
  Hyrax::SolrQueryBuilderService.construct_query(*args)
73
76
  end
74
77
 
@@ -94,7 +97,7 @@ module IiifPrint
94
97
  # Building a custom query to find these child works directly via the attribute would be more efficient.
95
98
  # However, it would require more effort for a lesser-used feature, and would not allow for the fallback
96
99
  # of finding child works by title.
97
- # rubocop:disable Lint/UnusedMethodArgument, Metrics/AbcSize, Metrics/MethodLength
100
+ # rubocop:disable Lint/UnusedMethodArgument
98
101
  def self.destroy_children_split_from(file_set:, work:, model:, user:)
99
102
  all_child_works = Hyrax.custom_queries.find_child_works(resource: work).to_a
100
103
  return if all_child_works.blank?
@@ -114,14 +117,20 @@ module IiifPrint
114
117
  Hyrax.index_adapter.save(resource: work)
115
118
  Hyrax.publisher.publish('object.membership.updated', object: work, user: user)
116
119
 
117
- children.each do |rcd|
118
- Hyrax.persister.delete(resource: rcd)
119
- Hyrax.index_adapter.delete(resource: rcd)
120
- Hyrax.publisher.publish('object.deleted', object: rcd, user: user)
121
- end
120
+ children.each { |child| destroy_child_work(child, user: user || ::User.system_user) }
122
121
  true
123
122
  end
124
- # rubocop:enable Lint/UnusedMethodArgument, Metrics/AbcSize, Metrics/MethodLength
123
+ # rubocop:enable Lint/UnusedMethodArgument
124
+
125
+ # Not persister.delete: the work transaction also deletes the child's own file sets.
126
+ def self.destroy_child_work(child, user:)
127
+ result = Hyrax::Transactions::Container['work_resource.destroy']
128
+ .with_step_args('work_resource.delete_all_file_sets' => { user: user },
129
+ 'work_resource.delete' => { user: user })
130
+ .call(child)
131
+ Rails.logger.error("IiifPrint could not destroy split child work #{child.id}: #{result.failure.inspect}") if result.failure?
132
+ result
133
+ end
125
134
 
126
135
  def self.pdf?(file_set)
127
136
  file_set.original_file&.pdf?
@@ -1,3 +1,3 @@
1
1
  module IiifPrint
2
- VERSION = '3.2.0'.freeze
2
+ VERSION = '3.2.1'.freeze
3
3
  end
data/lib/iiif_print.rb CHANGED
@@ -13,6 +13,8 @@ require "iiif_print/text_formats_from_alto_service"
13
13
  require "iiif_print/tiff_derivative_service"
14
14
  require "iiif_print/lineage_service"
15
15
  require "iiif_print/metadata"
16
+ # ActiveSupport::CurrentAttributes arrived in Rails 5.2; only the flexible metadata path, which needs a newer Hyrax, uses it.
17
+ require "iiif_print/current" if defined?(ActiveSupport::CurrentAttributes)
16
18
  require "iiif_print/flexibility"
17
19
  require "iiif_print/split_pdfs/base_splitter"
18
20
  require "iiif_print/split_pdfs/child_work_creation_from_pdf_service"
@@ -42,6 +44,16 @@ module IiifPrint
42
44
  @config
43
45
  end
44
46
 
47
+ ##
48
+ # Subscribes a new IiifPrint::Listener in place of the one an earlier prepare subscribed.
49
+ # Dry::Events neither deduplicates subscriptions nor unsubscribes by class, and after a code
50
+ # reload the earlier listener's class is the unloaded one, so it is tracked here.
51
+ def self.subscribe_listener(publisher: Hyrax.publisher)
52
+ publisher.unsubscribe(@listener) if @listener
53
+ @listener = IiifPrint::Listener.new
54
+ publisher.subscribe(@listener)
55
+ end
56
+
45
57
  class << self
46
58
  delegate(
47
59
  :persistence_adapter,
@@ -221,7 +233,7 @@ module IiifPrint
221
233
 
222
234
  # One per request: an Ability is costly to build, and a manifest can ask once for every page.
223
235
  def self.anonymous_ability
224
- RequestStore.store[:iiif_print_anonymous_ability] ||= ::Ability.new(nil)
236
+ Current.anonymous_ability ||= ::Ability.new(nil)
225
237
  end
226
238
  private_class_method :flexible_manifest_metadata_for, :anonymous_ability
227
239
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iiif_print
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Upton
@@ -11,9 +11,10 @@ authors:
11
11
  - Kirk Wang
12
12
  - LaRita Robinson
13
13
  - Jeremy Friesen
14
+ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
- date: 1980-01-02 00:00:00.000000000 Z
17
+ date: 2026-10-09 00:00:00.000000000 Z
17
18
  dependencies:
18
19
  - !ruby/object:Gem::Dependency
19
20
  name: blacklight_iiif_search
@@ -97,20 +98,6 @@ dependencies:
97
98
  - - "~>"
98
99
  - !ruby/object:Gem::Version
99
100
  version: '3.0'
100
- - !ruby/object:Gem::Dependency
101
- name: request_store
102
- requirement: !ruby/object:Gem::Requirement
103
- requirements:
104
- - - ">="
105
- - !ruby/object:Gem::Version
106
- version: '1.0'
107
- type: :runtime
108
- prerelease: false
109
- version_requirements: !ruby/object:Gem::Requirement
110
- requirements:
111
- - - ">="
112
- - !ruby/object:Gem::Version
113
- version: '1.0'
114
101
  - !ruby/object:Gem::Dependency
115
102
  name: bixby
116
103
  requirement: !ruby/object:Gem::Requirement
@@ -462,6 +449,7 @@ files:
462
449
  - lib/iiif_print/blacklight_iiif_search/annotation_decorator.rb
463
450
  - lib/iiif_print/catalog_search_builder.rb
464
451
  - lib/iiif_print/configuration.rb
452
+ - lib/iiif_print/current.rb
465
453
  - lib/iiif_print/data.rb
466
454
  - lib/iiif_print/data/fileset_helper.rb
467
455
  - lib/iiif_print/data/path_helper.rb
@@ -471,6 +459,7 @@ files:
471
459
  - lib/iiif_print/engine.rb
472
460
  - lib/iiif_print/errors.rb
473
461
  - lib/iiif_print/flexibility.rb
462
+ - lib/iiif_print/flexibility/compound_values.rb
474
463
  - lib/iiif_print/flexibility/fields.rb
475
464
  - lib/iiif_print/flexibility/values.rb
476
465
  - lib/iiif_print/homepage_search_builder.rb
@@ -513,6 +502,7 @@ homepage: https://github.com/notch8/iiif_print/
513
502
  licenses:
514
503
  - Apache-2.0
515
504
  metadata: {}
505
+ post_install_message:
516
506
  rdoc_options: []
517
507
  require_paths:
518
508
  - lib
@@ -527,7 +517,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
527
517
  - !ruby/object:Gem::Version
528
518
  version: '0'
529
519
  requirements: []
530
- rubygems_version: 3.6.7
520
+ rubygems_version: 3.3.20
521
+ signing_key:
531
522
  specification_version: 4
532
523
  summary: IiifPrint is a gem (Rails "engine") for Hyrax-based digital repository applications
533
524
  to support displaying parent/child works in the same viewer (Universal Viewer) and