iiif_print 3.1.2 → 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 +4 -4
- data/Gemfile.lock +36 -35
- data/app/presenters/iiif_print/work_show_presenter_decorator.rb +13 -0
- data/app/services/iiif_print/manifest_builder_service_decorator.rb +8 -3
- data/app/transactions/hyrax/transactions/steps/delete_all_file_sets_decorator.rb +30 -7
- data/lib/iiif_print/catalog_search_builder.rb +4 -6
- data/lib/iiif_print/configuration.rb +3 -1
- data/lib/iiif_print/current.rb +10 -0
- data/lib/iiif_print/engine.rb +3 -4
- data/lib/iiif_print/flexibility/compound_values.rb +58 -0
- data/lib/iiif_print/flexibility/fields.rb +67 -0
- data/lib/iiif_print/flexibility/values.rb +139 -0
- data/lib/iiif_print/flexibility.rb +82 -0
- data/lib/iiif_print/homepage_search_builder.rb +4 -6
- data/lib/iiif_print/metadata.rb +20 -3
- data/lib/iiif_print/persistence_layer/active_fedora_adapter.rb +5 -0
- data/lib/iiif_print/persistence_layer/valkyrie_adapter.rb +16 -7
- data/lib/iiif_print/version.rb +1 -1
- data/lib/iiif_print.rb +36 -4
- metadata +11 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2ccac310b7d9a84e367cec7ea055b4b2b9af2d9817cbef9ccfaf3c68d467ce40
|
|
4
|
+
data.tar.gz: 621bb63d8990014bd1a4842ef6897628386515e6a3bcc76f0d212ba59a318f0e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 687953bee3a35e17c5ed41e34e29324c7166736397356131f40f3e56b7f623004f9c07a8ff55019347288811740e8f3064dd5673f71357e4d6f153a79dcf8bb6
|
|
7
|
+
data.tar.gz: cdb91f7cbe9a6fd603f3ad5e38058dac9e8da93f1d10bd932e0a843deae24cb830f090c533b9e7bfb4dbdd9eb1fcd8509377d5bc34db41df92cd5321e19ee9bc
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: /app/samvera/hyrax-webapp
|
|
3
3
|
specs:
|
|
4
|
-
iiif_print (3.1
|
|
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)
|
|
@@ -65,7 +65,7 @@ GEM
|
|
|
65
65
|
activemodel (= 5.2.8.1)
|
|
66
66
|
activesupport (= 5.2.8.1)
|
|
67
67
|
arel (>= 9.0)
|
|
68
|
-
activerecord-import (2.
|
|
68
|
+
activerecord-import (2.3.0)
|
|
69
69
|
activerecord (>= 4.2)
|
|
70
70
|
activestorage (5.2.8.1)
|
|
71
71
|
actionpack (= 5.2.8.1)
|
|
@@ -80,19 +80,19 @@ GEM
|
|
|
80
80
|
public_suffix (>= 2.0.2, < 8.0)
|
|
81
81
|
almond-rails (0.3.0)
|
|
82
82
|
rails (>= 4.2)
|
|
83
|
-
anonymous_loader (0.1.
|
|
84
|
-
version_gem (~> 1.1, >= 1.1.
|
|
83
|
+
anonymous_loader (0.1.3)
|
|
84
|
+
version_gem (~> 1.1, >= 1.1.14)
|
|
85
85
|
arel (9.0.0)
|
|
86
86
|
ast (2.4.2)
|
|
87
|
-
auth-sanitizer (0.2.
|
|
88
|
-
version_gem (~> 1.1, >= 1.1.
|
|
87
|
+
auth-sanitizer (0.2.3)
|
|
88
|
+
version_gem (~> 1.1, >= 1.1.14)
|
|
89
89
|
autoprefixer-rails (10.4.16.0)
|
|
90
90
|
execjs (~> 2)
|
|
91
91
|
awesome_nested_set (3.9.0)
|
|
92
92
|
activerecord (>= 4.0.0, < 8.2)
|
|
93
93
|
aws-eventstream (1.4.0)
|
|
94
|
-
aws-partitions (1.
|
|
95
|
-
aws-sdk-core (3.
|
|
94
|
+
aws-partitions (1.1293.0)
|
|
95
|
+
aws-sdk-core (3.254.1)
|
|
96
96
|
aws-eventstream (~> 1, >= 1.3.0)
|
|
97
97
|
aws-partitions (~> 1, >= 1.992.0)
|
|
98
98
|
aws-sigv4 (~> 1.9)
|
|
@@ -100,15 +100,15 @@ GEM
|
|
|
100
100
|
bigdecimal
|
|
101
101
|
jmespath (~> 1, >= 1.6.1)
|
|
102
102
|
logger
|
|
103
|
-
aws-sdk-kms (1.
|
|
104
|
-
aws-sdk-core (~> 3, >= 3.
|
|
103
|
+
aws-sdk-kms (1.130.0)
|
|
104
|
+
aws-sdk-core (~> 3, >= 3.254.0)
|
|
105
105
|
aws-sigv4 (~> 1.5)
|
|
106
|
-
aws-sdk-s3 (1.
|
|
107
|
-
aws-sdk-core (~> 3, >= 3.
|
|
106
|
+
aws-sdk-s3 (1.230.0)
|
|
107
|
+
aws-sdk-core (~> 3, >= 3.254.1)
|
|
108
108
|
aws-sdk-kms (~> 1)
|
|
109
109
|
aws-sigv4 (~> 1.5)
|
|
110
|
-
aws-sdk-sqs (1.
|
|
111
|
-
aws-sdk-core (~> 3, >= 3.
|
|
110
|
+
aws-sdk-sqs (1.117.0)
|
|
111
|
+
aws-sdk-core (~> 3, >= 3.254.0)
|
|
112
112
|
aws-sigv4 (~> 1.5)
|
|
113
113
|
aws-sigv4 (1.12.1)
|
|
114
114
|
aws-eventstream (~> 1, >= 1.0.2)
|
|
@@ -122,7 +122,7 @@ GEM
|
|
|
122
122
|
i18n
|
|
123
123
|
bcrypt (3.1.21)
|
|
124
124
|
benchmark (0.3.0)
|
|
125
|
-
bigdecimal (4.1.
|
|
125
|
+
bigdecimal (4.1.3)
|
|
126
126
|
bindex (0.8.1)
|
|
127
127
|
bixby (5.0.2)
|
|
128
128
|
rubocop (= 1.28.2)
|
|
@@ -192,7 +192,7 @@ GEM
|
|
|
192
192
|
thor (>= 0.19.4, < 2.0)
|
|
193
193
|
tins (~> 1.6)
|
|
194
194
|
crass (1.0.6)
|
|
195
|
-
csv (3.3.
|
|
195
|
+
csv (3.3.6)
|
|
196
196
|
database_cleaner (1.99.0)
|
|
197
197
|
date (3.3.4)
|
|
198
198
|
declarative (0.0.20)
|
|
@@ -353,8 +353,8 @@ GEM
|
|
|
353
353
|
logger
|
|
354
354
|
hiredis (0.6.3)
|
|
355
355
|
htmlentities (4.4.2)
|
|
356
|
-
http_logger (1.0.
|
|
357
|
-
httparty (0.24.
|
|
356
|
+
http_logger (1.0.3)
|
|
357
|
+
httparty (0.24.3)
|
|
358
358
|
csv
|
|
359
359
|
mini_mime (>= 1.0.0)
|
|
360
360
|
multi_xml (>= 0.5.2)
|
|
@@ -467,7 +467,7 @@ GEM
|
|
|
467
467
|
activesupport (>= 3.2.18)
|
|
468
468
|
faraday (>= 0.9)
|
|
469
469
|
json
|
|
470
|
-
iiif_manifest (1.
|
|
470
|
+
iiif_manifest (1.7.0)
|
|
471
471
|
activesupport (>= 4)
|
|
472
472
|
jaro_winkler (1.5.6)
|
|
473
473
|
jbuilder (2.12.0)
|
|
@@ -497,7 +497,7 @@ GEM
|
|
|
497
497
|
json-ld-preloaded (3.1.6)
|
|
498
498
|
json-ld (~> 3.1)
|
|
499
499
|
rdf (~> 3.1)
|
|
500
|
-
json-schema (6.2.
|
|
500
|
+
json-schema (6.2.1)
|
|
501
501
|
addressable (~> 2.8)
|
|
502
502
|
bigdecimal (>= 3.1, < 5)
|
|
503
503
|
jwt (2.10.3)
|
|
@@ -589,7 +589,7 @@ GEM
|
|
|
589
589
|
mime-types (3.7.0)
|
|
590
590
|
logger
|
|
591
591
|
mime-types-data (~> 3.2025, >= 3.2025.0507)
|
|
592
|
-
mime-types-data (3.2026.
|
|
592
|
+
mime-types-data (3.2026.1006)
|
|
593
593
|
mini_magick (4.13.2)
|
|
594
594
|
mini_mime (1.1.5)
|
|
595
595
|
mini_portile2 (2.8.5)
|
|
@@ -621,16 +621,17 @@ GEM
|
|
|
621
621
|
nokogiri (1.15.5)
|
|
622
622
|
mini_portile2 (~> 2.8.2)
|
|
623
623
|
racc (~> 1.4)
|
|
624
|
-
oauth (1.1.
|
|
625
|
-
|
|
624
|
+
oauth (1.1.8)
|
|
625
|
+
anonymous_loader (~> 0.1, >= 0.1.3)
|
|
626
|
+
auth-sanitizer (~> 0.2, >= 0.2.3)
|
|
626
627
|
base64 (~> 0.1)
|
|
627
|
-
oauth-tty (~> 1.0, >= 1.0.
|
|
628
|
-
snaky_hash (~> 2.0, >= 2.0.
|
|
629
|
-
version_gem (~> 1.1, >= 1.1.
|
|
630
|
-
oauth-tty (1.0.
|
|
631
|
-
anonymous_loader (~> 0.1, >= 0.1.
|
|
632
|
-
auth-sanitizer (~> 0.2, >= 0.2.
|
|
633
|
-
version_gem (~> 1.1, >= 1.1.
|
|
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)
|
|
634
635
|
oauth2 (1.4.11)
|
|
635
636
|
faraday (>= 0.17.3, < 3.0)
|
|
636
637
|
jwt (>= 1.0, < 3.0)
|
|
@@ -890,9 +891,9 @@ GEM
|
|
|
890
891
|
simplecov-html (~> 0.10.0)
|
|
891
892
|
simplecov-html (0.10.2)
|
|
892
893
|
slop (4.10.1)
|
|
893
|
-
snaky_hash (2.0.
|
|
894
|
+
snaky_hash (2.0.7)
|
|
894
895
|
hashie (>= 0.1.0, < 6)
|
|
895
|
-
version_gem (
|
|
896
|
+
version_gem (~> 1.1, >= 1.1.14)
|
|
896
897
|
solargraph (0.48.0)
|
|
897
898
|
backport (~> 1.2)
|
|
898
899
|
benchmark
|
|
@@ -943,7 +944,7 @@ GEM
|
|
|
943
944
|
matrix (~> 0.4)
|
|
944
945
|
rdf (~> 3.2)
|
|
945
946
|
sync (0.5.0)
|
|
946
|
-
temple (0.10.
|
|
947
|
+
temple (0.10.7)
|
|
947
948
|
term-ansicolor (1.7.1)
|
|
948
949
|
tins (~> 1.0)
|
|
949
950
|
terminal-table (4.0.0)
|
|
@@ -971,7 +972,7 @@ GEM
|
|
|
971
972
|
uglifier (4.2.0)
|
|
972
973
|
execjs (>= 0.3.0, < 3)
|
|
973
974
|
unicode-display_width (2.5.0)
|
|
974
|
-
unicode-types (1.
|
|
975
|
+
unicode-types (1.12.0)
|
|
975
976
|
valkyrie (2.2.0)
|
|
976
977
|
activemodel
|
|
977
978
|
activesupport
|
|
@@ -987,7 +988,7 @@ GEM
|
|
|
987
988
|
rdf-vocab
|
|
988
989
|
reform (~> 2.2)
|
|
989
990
|
reform-rails
|
|
990
|
-
version_gem (1.1.
|
|
991
|
+
version_gem (1.1.15)
|
|
991
992
|
warden (1.2.9)
|
|
992
993
|
rack (>= 2.0.9)
|
|
993
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
|
-
|
|
68
|
+
sanitize_v3_label(hash)
|
|
69
69
|
hash['items'].each do |canvas|
|
|
70
|
-
canvas
|
|
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/
|
|
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.
|
|
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
|
-
|
|
21
|
-
|
|
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
|
|
@@ -24,12 +24,10 @@ module IiifPrint
|
|
|
24
24
|
|
|
25
25
|
# rubocop:enable Naming/PredicateName
|
|
26
26
|
def show_parents_only(solr_parameters)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
@@ -289,9 +289,11 @@ module IiifPrint
|
|
|
289
289
|
##
|
|
290
290
|
# This is the default sorter for the metadata. It will sort by the order of the keys specificied.
|
|
291
291
|
# By default, this is turned off as it returns nil. If you want to turn it on, you can set this
|
|
292
|
-
#
|
|
292
|
+
# to an array of symbols for the properties on the work. Properties not listed follow the listed
|
|
293
|
+
# ones, or take the place of `:remaining` when it is listed.
|
|
293
294
|
#
|
|
294
295
|
# @example [:title, :description, :date_created]
|
|
296
|
+
# @example [:title, :collection, :remaining, :rights_statement]
|
|
295
297
|
# @return [Array<Symbol>]
|
|
296
298
|
def iiif_metadata_field_presentation_order
|
|
297
299
|
@iiif_metadata_field_presentation_order || nil
|
|
@@ -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
|
data/lib/iiif_print/engine.rb
CHANGED
|
@@ -42,11 +42,10 @@ module IiifPrint
|
|
|
42
42
|
# To configure specific services, inject each service, in desired order
|
|
43
43
|
# to IiifPrint::PluggableDerivativeService.plugins array.
|
|
44
44
|
|
|
45
|
-
Hyrax::DerivativeService.services
|
|
46
|
-
|
|
47
|
-
)
|
|
45
|
+
derivative_services = Hyrax::DerivativeService.services
|
|
46
|
+
derivative_services.unshift(IiifPrint::PluggableDerivativeService) unless derivative_services.include?(IiifPrint::PluggableDerivativeService)
|
|
48
47
|
|
|
49
|
-
|
|
48
|
+
IiifPrint.subscribe_listener if Hyrax.respond_to?(:publisher)
|
|
50
49
|
|
|
51
50
|
# Load locales early so decorators can use them during initialization
|
|
52
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
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module IiifPrint
|
|
4
|
+
module Flexibility
|
|
5
|
+
##
|
|
6
|
+
# The fields a flexible record's show page lists, from its M3 profile: those an anonymous visitor may see and
|
|
7
|
+
# its show presenter can render, labeled and ordered as the profile says, plus its collections. Each carries
|
|
8
|
+
# its view options, which {IiifPrint::Flexibility::Values} renders it by.
|
|
9
|
+
class Fields
|
|
10
|
+
# The show page leads with the title and description, then the work's collections.
|
|
11
|
+
DEFAULT_ORDER = %i[title description abstract collection remaining].freeze
|
|
12
|
+
|
|
13
|
+
# @param document [SolrDocument]
|
|
14
|
+
# @param ability [Ability]
|
|
15
|
+
# @param sort_order [Array<Symbol>] see {IiifPrint::Configuration#iiif_metadata_field_presentation_order};
|
|
16
|
+
# {DEFAULT_ORDER} when unset
|
|
17
|
+
def initialize(document, ability, sort_order: IiifPrint.config.iiif_metadata_field_presentation_order)
|
|
18
|
+
@document = document
|
|
19
|
+
@ability = ability
|
|
20
|
+
@sort_order = sort_order || DEFAULT_ORDER
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [Array<IiifPrint::Field>]
|
|
24
|
+
def to_a
|
|
25
|
+
presenter = Flexibility.presenter_for(@document, @ability)
|
|
26
|
+
definitions = Flexibility.view_definitions_for(@document)
|
|
27
|
+
fields = definitions.filter_map do |name, options|
|
|
28
|
+
options = options.to_h.symbolize_keys
|
|
29
|
+
term = (options[:render_term] || name).to_sym
|
|
30
|
+
next unless public?(name, options) && (term == :collection || presenter.respond_to?(term))
|
|
31
|
+
|
|
32
|
+
Field.new(name: term, label: label(term, options), options: options)
|
|
33
|
+
end
|
|
34
|
+
fields << collection_field unless collection_defined?(definitions)
|
|
35
|
+
IiifPrint.sort_af_fields!(fields, sort_order: @sort_order)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def collection_field
|
|
41
|
+
Field.new(name: :collection, label: Hyrax::Renderers::AttributeRenderer.new(:collection, nil).label)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def collection_defined?(definitions)
|
|
45
|
+
definitions.any? { |name, options| [name.to_s, options.to_h.symbolize_keys[:render_term].to_s].include?('collection') }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The show page lists an admin note for editors only, whatever the profile says.
|
|
49
|
+
def public?(name, options)
|
|
50
|
+
return false if name.to_s == 'admin_note'
|
|
51
|
+
|
|
52
|
+
!(options[:admin_only] || options[:editor_only] || options[:show_page] == false)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Mirrors Hyrax's AttributesHelper#conform_options, so a field is labeled as its show page labels it.
|
|
56
|
+
def label(name, options)
|
|
57
|
+
labels = options[:display_label] || {}
|
|
58
|
+
label = (labels[I18n.locale.to_s] || labels['default'] || name).to_s
|
|
59
|
+
translated = I18n.t(label, default: label) if label.include?('.')
|
|
60
|
+
return translated if translated && translated != label
|
|
61
|
+
|
|
62
|
+
I18n.t(:"blacklight.search.fields.index.#{label}",
|
|
63
|
+
default: [:"blacklight.search.fields.show.#{label}", :"blacklight.search.fields.#{label}", label.humanize])
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module IiifPrint
|
|
4
|
+
module Flexibility
|
|
5
|
+
##
|
|
6
|
+
# A flexible field's values, read from the show presenter and rendered the way its profile's `render_as` renders
|
|
7
|
+
# them on the show page.
|
|
8
|
+
class Values
|
|
9
|
+
# What IIIF Presentation 3.0 allows in a metadata value (section 4.5).
|
|
10
|
+
IIIF_TAGS = %w[a b br i img p small span sub sup].freeze
|
|
11
|
+
IIIF_ATTRIBUTES = %w[href src alt].freeze
|
|
12
|
+
RIGHTS_SERVICES = %w[rights_statement license].freeze
|
|
13
|
+
|
|
14
|
+
# @param work [SolrDocument, SolrHit]
|
|
15
|
+
# @param presenter [#display_values_for] the record's show presenter
|
|
16
|
+
# @param base_url [String]
|
|
17
|
+
# @param autolink [#call] turns URLs in plain text into links
|
|
18
|
+
def initialize(work:, presenter:, base_url:, autolink:)
|
|
19
|
+
@work = work
|
|
20
|
+
@presenter = presenter
|
|
21
|
+
@base_url = base_url
|
|
22
|
+
@autolink = autolink
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @return [Array] the field's values as stored, blanks and all-blank compound entries dropped
|
|
26
|
+
def raw(field_name)
|
|
27
|
+
Array(@presenter.try(field_name)).reject { |value| CompoundValues.blank_entry?(value) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @return [Array<String>] the field's values as html
|
|
31
|
+
def rendered(field_name, options)
|
|
32
|
+
options ||= {}
|
|
33
|
+
values = Array(@presenter.try(field_name)).reject(&:blank?)
|
|
34
|
+
return [compound_values(field_name, values)] if options[:render_as].to_s == 'compound'
|
|
35
|
+
|
|
36
|
+
labels = labels_for(field_name, values)
|
|
37
|
+
values.map { |value| value(field_name, value.to_s, labels[value.to_s], options) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
# Keyed by value, as the show page's renderers get them in `options[:labels]`, and like them treating a blank
|
|
43
|
+
# label, or one equal to its value, as none.
|
|
44
|
+
def labels_for(field_name, values)
|
|
45
|
+
labels = @presenter.try(:controlled_labels_for, field_name, values) || display_labels(field_name, values)
|
|
46
|
+
labels.to_h.transform_keys(&:to_s).reject { |value, label| label.blank? || label.to_s == value }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# TODO: drop once Hyrax's PresentsAttributes#controlled_labels_for is public
|
|
50
|
+
def display_labels(field_name, values)
|
|
51
|
+
display = Array(@presenter.try(:display_values_for, field_name)).reject(&:blank?)
|
|
52
|
+
return {} unless display.size == values.size
|
|
53
|
+
|
|
54
|
+
values.map(&:to_s).zip(display.map(&:to_s)).to_h
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def value(field_name, value, label, options)
|
|
58
|
+
return search_value(options[:search_field] || field_name, value, label, options[:render_as].to_s) if %w[faceted linked].include?(options[:render_as].to_s)
|
|
59
|
+
|
|
60
|
+
label = rights_label(field_name, options[:render_as].to_s, value, label)
|
|
61
|
+
case options[:render_as].to_s
|
|
62
|
+
when 'redirects_label' then link(File.join(@base_url, value), File.join(@base_url, value))
|
|
63
|
+
when 'html' then sanitize_html(value)
|
|
64
|
+
when 'date' then ERB::Util.h(formatted_date(value))
|
|
65
|
+
else labeled_value(value, label)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Like Hyrax's faceted and linked renderers: a controlled term links by its label, through the label facet
|
|
70
|
+
# when the catalog registers one.
|
|
71
|
+
def search_value(field, value, label, render_as)
|
|
72
|
+
return search_link({ search_field: field, q: label || value }, label || value) if render_as == 'linked'
|
|
73
|
+
|
|
74
|
+
by_label = label.present? && label_facet?(field)
|
|
75
|
+
search_link({ "f[#{field}#{'_label' if by_label}_sim][]": by_label ? label : value }, label || value)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def label_facet?(field)
|
|
79
|
+
config = 'CatalogController'.safe_constantize.try(:blacklight_config)
|
|
80
|
+
config.respond_to?(:facet_fields) && config.facet_fields.key?("#{field}_label_sim")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def search_link(params, text)
|
|
84
|
+
params = params.merge(include_child_works: true) if @work["is_child_bsi"] == true
|
|
85
|
+
link(File.join(@base_url, Rails.application.routes.url_helpers.search_catalog_path(**params, locale: I18n.locale)), text)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def labeled_value(value, label)
|
|
89
|
+
return @autolink.call([ERB::Util.h(value)]).first if label.blank?
|
|
90
|
+
return ERB::Util.h(label) unless value.match?(%r{\Ahttps?://\S+\z})
|
|
91
|
+
|
|
92
|
+
link(value, label)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def formatted_date(value)
|
|
96
|
+
Date.parse(value).to_formatted_s(:standard)
|
|
97
|
+
rescue ArgumentError
|
|
98
|
+
value
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# A rights statement or license rendered as such takes its label from its Hyrax service, as the show page's
|
|
102
|
+
# renderers do; one rendered otherwise falls back to the service only when no label was indexed.
|
|
103
|
+
def rights_label(field_name, render_as, value, label)
|
|
104
|
+
return service_label(render_as, value) || label if RIGHTS_SERVICES.include?(render_as)
|
|
105
|
+
return label || service_label(field_name.to_s, value) if RIGHTS_SERVICES.include?(field_name.to_s)
|
|
106
|
+
|
|
107
|
+
label
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def service_label(service, value)
|
|
111
|
+
Hyrax.config.try("#{service}_service_class")&.new&.label(value) { nil }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def sanitize_html(value)
|
|
115
|
+
fragment = Nokogiri::HTML.fragment(value.to_s)
|
|
116
|
+
{ 'a' => 'href', 'img' => 'src' }.each do |tag, attribute|
|
|
117
|
+
fragment.css("#{tag}[#{attribute}]").each { |node| node[attribute] = absolute_url(node[attribute]) }
|
|
118
|
+
end
|
|
119
|
+
Rails::Html::SafeListSanitizer.new.sanitize(fragment.to_html, tags: IIIF_TAGS, attributes: IIIF_ATTRIBUTES).strip
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def absolute_url(url)
|
|
123
|
+
return url if url.blank? || url.match?(/\A[a-z][a-z0-9+.-]*:/i)
|
|
124
|
+
|
|
125
|
+
URI.join("#{@base_url.to_s.chomp('/')}/", url).to_s
|
|
126
|
+
rescue URI::Error
|
|
127
|
+
url
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def compound_values(field_name, entries)
|
|
131
|
+
CompoundValues.new(field_name, presenter: @presenter, sanitize: method(:sanitize_html)).to_html(entries)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def link(href, text)
|
|
135
|
+
%(<a href="#{ERB::Util.h(href)}">#{ERB::Util.h(text)}</a>)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "iiif_print/flexibility/fields"
|
|
4
|
+
require "iiif_print/flexibility/compound_values"
|
|
5
|
+
require "iiif_print/flexibility/values"
|
|
6
|
+
|
|
7
|
+
module IiifPrint
|
|
8
|
+
##
|
|
9
|
+
# IIIF manifest metadata for records indexed under an M3 profile, built the way their show page is: which fields
|
|
10
|
+
# from {IiifPrint::Flexibility::Fields}, and how each value renders from {IiifPrint::Flexibility::Values}.
|
|
11
|
+
module Flexibility
|
|
12
|
+
class << self
|
|
13
|
+
##
|
|
14
|
+
# @return [Boolean] whether the record's metadata comes from its M3 profile
|
|
15
|
+
def applies_to?(document)
|
|
16
|
+
Hyrax.config.try(:flexible?) && document.try(:flexible?)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
##
|
|
20
|
+
# One per request for each record and ability, since both the fields and their values read from it.
|
|
21
|
+
def presenter_for(document, ability)
|
|
22
|
+
presenters = Current.presenters ||= {}
|
|
23
|
+
presenters[[document.id, ability]] ||= build_presenter(document, ability)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# The profile's view definitions for the record, plus its title, description and abstract when the profile
|
|
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
|
|
30
|
+
# for each tenant, schema, version and context; each tenant numbers its own profile versions.
|
|
31
|
+
def view_definitions_for(document)
|
|
32
|
+
cache = Current.view_definitions ||= {}
|
|
33
|
+
version = document.try(:schema_version)
|
|
34
|
+
contexts = document.try(:contexts)
|
|
35
|
+
tenant = Apartment::Tenant.current if defined?(Apartment::Tenant)
|
|
36
|
+
cache[[tenant, schema_name(document), version, contexts]] ||= begin
|
|
37
|
+
definitions = Hyrax::Schema.m3_schema_loader.view_definitions_for(schema: schema_name(document), version: version, contexts: contexts)
|
|
38
|
+
lead_definitions(document, definitions, contexts).merge(definitions.to_h.symbolize_keys)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
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
|
+
|
|
51
|
+
LEAD_FIELDS = %i[title description abstract].freeze
|
|
52
|
+
|
|
53
|
+
def lead_definitions(document, definitions, contexts)
|
|
54
|
+
attributes = profile_attributes(document)
|
|
55
|
+
(LEAD_FIELDS - definitions.to_h.keys.map(&:to_sym)).each_with_object({}) do |name, lead|
|
|
56
|
+
config = attributes[name.to_s]
|
|
57
|
+
next unless config && in_context?(config, contexts)
|
|
58
|
+
|
|
59
|
+
lead[name] = Hyrax::SchemaLoader::AttributeDefinition.new(name.to_s, config).view_options
|
|
60
|
+
end
|
|
61
|
+
end
|
|
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
|
+
|
|
70
|
+
def profile_attributes(document)
|
|
71
|
+
schema = Hyrax::FlexibleSchema.find_by(id: document.try(:schema_version)) || Hyrax::FlexibleSchema.order(:created_at).last
|
|
72
|
+
schema.try(:attributes_for, schema_name(document)) || {}
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def schema_name(document)
|
|
76
|
+
Valkyrie.config.resource_class_resolver.call(document.hydra_model_name).to_s
|
|
77
|
+
rescue NameError
|
|
78
|
+
document.hydra_model.to_s
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
data/lib/iiif_print/metadata.rb
CHANGED
|
@@ -38,13 +38,13 @@ module IiifPrint
|
|
|
38
38
|
if version == 2
|
|
39
39
|
case property
|
|
40
40
|
when :label then field.label
|
|
41
|
-
when :value then
|
|
41
|
+
when :value then rendered_values_for(field)
|
|
42
42
|
when :collection then make_collection_link(viewable_collections)
|
|
43
43
|
end
|
|
44
44
|
elsif version == 3
|
|
45
45
|
case property
|
|
46
46
|
when :label then { I18n.locale.to_s => [field.label] }
|
|
47
|
-
when :value then { 'none' =>
|
|
47
|
+
when :value then { 'none' => rendered_values_for(field) }
|
|
48
48
|
when :collection then { 'none' => make_collection_link(viewable_collections) }
|
|
49
49
|
end
|
|
50
50
|
end
|
|
@@ -64,6 +64,12 @@ module IiifPrint
|
|
|
64
64
|
Loofah.fragment(value).scrub!(:whitewash).to_s
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
def rendered_values_for(field)
|
|
68
|
+
return flexible_values.rendered(field.name, field.options) if flexible?
|
|
69
|
+
|
|
70
|
+
cast_to_value(field_name: field.name, options: field.options)
|
|
71
|
+
end
|
|
72
|
+
|
|
67
73
|
def cast_to_value(field_name:, options:)
|
|
68
74
|
if options&.[](:render_as) == :faceted
|
|
69
75
|
faceted_values_for(field_name: field_name)
|
|
@@ -98,14 +104,25 @@ module IiifPrint
|
|
|
98
104
|
end
|
|
99
105
|
|
|
100
106
|
def values_for(field_name:)
|
|
107
|
+
return flexible_values.raw(field_name.try(:name) || field_name) if flexible?
|
|
108
|
+
|
|
101
109
|
field_name = field_name.try(:name) || field_name
|
|
102
110
|
# TODO: we are assuming tesim or dtsi (for dates), might want to account for other suffixes in the future
|
|
103
111
|
Array(work["#{field_name}_tesim"] || work["#{field_name}_dtsi"]&.to_date.try(:to_formatted_s, :standard))
|
|
104
112
|
end
|
|
105
113
|
|
|
114
|
+
def flexible?
|
|
115
|
+
Flexibility.applies_to?(work)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def flexible_values
|
|
119
|
+
@flexible_values ||= Flexibility::Values.new(work: work, presenter: Flexibility.presenter_for(work, current_ability),
|
|
120
|
+
base_url: @base_url, autolink: method(:make_link))
|
|
121
|
+
end
|
|
122
|
+
|
|
106
123
|
def make_collection_link(collection_documents)
|
|
107
124
|
collection_documents.map do |collection|
|
|
108
|
-
"<a href='#{File.join(@base_url, 'collections', collection.id)}'>#{collection.title.first}</a>"
|
|
125
|
+
"<a href='#{File.join(@base_url, 'collections', collection.id)}'>#{ERB::Util.h(collection.title.first)}</a>"
|
|
109
126
|
end
|
|
110
127
|
end
|
|
111
128
|
|
|
@@ -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
|
|
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
|
|
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
|
|
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?
|
data/lib/iiif_print/version.rb
CHANGED
data/lib/iiif_print.rb
CHANGED
|
@@ -13,6 +13,9 @@ 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)
|
|
18
|
+
require "iiif_print/flexibility"
|
|
16
19
|
require "iiif_print/split_pdfs/base_splitter"
|
|
17
20
|
require "iiif_print/split_pdfs/child_work_creation_from_pdf_service"
|
|
18
21
|
require "iiif_print/split_pdfs/derivative_rodeo_splitter"
|
|
@@ -41,6 +44,16 @@ module IiifPrint
|
|
|
41
44
|
@config
|
|
42
45
|
end
|
|
43
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
|
+
|
|
44
57
|
class << self
|
|
45
58
|
delegate(
|
|
46
59
|
:persistence_adapter,
|
|
@@ -203,8 +216,27 @@ module IiifPrint
|
|
|
203
216
|
def self.manifest_metadata_from(work:, presenter:)
|
|
204
217
|
current_ability = presenter.try(:ability) || presenter.try(:current_ability)
|
|
205
218
|
base_url = presenter.try(:base_url) || presenter.try(:request)&.base_url
|
|
219
|
+
document = work.is_a?(::SolrDocument) ? work : ::SolrDocument.new(work)
|
|
220
|
+
return flexible_manifest_metadata_for(document, base_url: base_url) if Flexibility.applies_to?(document)
|
|
221
|
+
|
|
206
222
|
IiifPrint.manifest_metadata_for(work: work, current_ability: current_ability, base_url: base_url)
|
|
207
223
|
end
|
|
224
|
+
|
|
225
|
+
# @api private
|
|
226
|
+
#
|
|
227
|
+
# A flexible work's metadata lists the fields an anonymous visitor would see on its show page, whoever asked
|
|
228
|
+
# and whether or not the work itself is public.
|
|
229
|
+
def self.flexible_manifest_metadata_for(document, base_url:)
|
|
230
|
+
ability = anonymous_ability
|
|
231
|
+
manifest_metadata_for(work: document, fields: Flexibility::Fields.new(document, ability).to_a, current_ability: ability, base_url: base_url)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# One per request: an Ability is costly to build, and a manifest can ask once for every page.
|
|
235
|
+
def self.anonymous_ability
|
|
236
|
+
Current.anonymous_ability ||= ::Ability.new(nil)
|
|
237
|
+
end
|
|
238
|
+
private_class_method :flexible_manifest_metadata_for, :anonymous_ability
|
|
239
|
+
|
|
208
240
|
# Hash is an arbitrary attribute key/value pairs
|
|
209
241
|
# Struct is a defined set of attribute "keys". When we favor defined values,
|
|
210
242
|
# then we are naming the concept and defining the range of potential values.
|
|
@@ -273,10 +305,10 @@ module IiifPrint
|
|
|
273
305
|
def self.sort_af_fields!(fields, sort_order:)
|
|
274
306
|
return fields if sort_order.blank?
|
|
275
307
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
end
|
|
308
|
+
remaining = sort_order.index(:remaining) || sort_order.length
|
|
309
|
+
fields.each_with_index.sort_by do |field, index|
|
|
310
|
+
[sort_order.index(field.name.to_sym) || remaining, index]
|
|
311
|
+
end.map(&:first)
|
|
280
312
|
end
|
|
281
313
|
|
|
282
314
|
##
|
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.1
|
|
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:
|
|
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
|
|
@@ -448,6 +449,7 @@ files:
|
|
|
448
449
|
- lib/iiif_print/blacklight_iiif_search/annotation_decorator.rb
|
|
449
450
|
- lib/iiif_print/catalog_search_builder.rb
|
|
450
451
|
- lib/iiif_print/configuration.rb
|
|
452
|
+
- lib/iiif_print/current.rb
|
|
451
453
|
- lib/iiif_print/data.rb
|
|
452
454
|
- lib/iiif_print/data/fileset_helper.rb
|
|
453
455
|
- lib/iiif_print/data/path_helper.rb
|
|
@@ -456,6 +458,10 @@ files:
|
|
|
456
458
|
- lib/iiif_print/data/work_files.rb
|
|
457
459
|
- lib/iiif_print/engine.rb
|
|
458
460
|
- lib/iiif_print/errors.rb
|
|
461
|
+
- lib/iiif_print/flexibility.rb
|
|
462
|
+
- lib/iiif_print/flexibility/compound_values.rb
|
|
463
|
+
- lib/iiif_print/flexibility/fields.rb
|
|
464
|
+
- lib/iiif_print/flexibility/values.rb
|
|
459
465
|
- lib/iiif_print/homepage_search_builder.rb
|
|
460
466
|
- lib/iiif_print/image_tool.rb
|
|
461
467
|
- lib/iiif_print/jp2_derivative_service.rb
|
|
@@ -496,6 +502,7 @@ homepage: https://github.com/notch8/iiif_print/
|
|
|
496
502
|
licenses:
|
|
497
503
|
- Apache-2.0
|
|
498
504
|
metadata: {}
|
|
505
|
+
post_install_message:
|
|
499
506
|
rdoc_options: []
|
|
500
507
|
require_paths:
|
|
501
508
|
- lib
|
|
@@ -510,7 +517,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
510
517
|
- !ruby/object:Gem::Version
|
|
511
518
|
version: '0'
|
|
512
519
|
requirements: []
|
|
513
|
-
rubygems_version: 3.
|
|
520
|
+
rubygems_version: 3.3.20
|
|
521
|
+
signing_key:
|
|
514
522
|
specification_version: 4
|
|
515
523
|
summary: IiifPrint is a gem (Rails "engine") for Hyrax-based digital repository applications
|
|
516
524
|
to support displaying parent/child works in the same viewer (Universal Viewer) and
|