iiif_print 3.1.1 → 3.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -1
- data/app/models/iiif_print/iiif_search_decorator.rb +1 -1
- data/iiif_print.gemspec +1 -0
- data/lib/iiif_print/blacklight_iiif_search/annotation_decorator.rb +8 -7
- data/lib/iiif_print/configuration.rb +3 -1
- data/lib/iiif_print/engine.rb +1 -0
- data/lib/iiif_print/flexibility/fields.rb +66 -0
- data/lib/iiif_print/flexibility/values.rb +148 -0
- data/lib/iiif_print/flexibility.rb +64 -0
- data/lib/iiif_print/metadata.rb +20 -3
- data/lib/iiif_print/persistence_layer/valkyrie_adapter.rb +1 -1
- data/lib/iiif_print/version.rb +1 -1
- data/lib/iiif_print.rb +24 -4
- metadata +20 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 01fd18bb505838305103b589e56d196fbc346eb00390de671c53bb15083a39d6
|
|
4
|
+
data.tar.gz: c7bf87ae11b5ae00b597076254d897c9519fc2bf9facd777763861dcca511adf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3eee1a13ecdf5693e0dd987a9331d527fcf884d0c8dc10ea12b14f7b99165a3f13b712ac5cfddb6d1579fbd33ce6fc0945c1bc2c3195ff1aff7c56637e3aee46
|
|
7
|
+
data.tar.gz: 1a85675fa01612c0fc9115e2f1cf524fae745d6a2c4b8b897b283c4272bd1583ef3c40e5e9b605591cc36a641a4f8aedb8c5b022a90534bbb31f725aa670e8be
|
data/Gemfile.lock
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: /app/samvera/hyrax-webapp
|
|
3
3
|
specs:
|
|
4
|
-
iiif_print (3.
|
|
4
|
+
iiif_print (3.2.0)
|
|
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)
|
|
10
11
|
|
|
11
12
|
GEM
|
|
12
13
|
remote: https://rubygems.org/
|
|
@@ -26,7 +26,7 @@ module IiifPrint
|
|
|
26
26
|
return { q: 'nil:nil' } unless q
|
|
27
27
|
|
|
28
28
|
{
|
|
29
|
-
q: "#{q} AND has_model_ssim:FileSet AND (#{iiif_config[:object_relation_field]}:\"#{parent_document.id}\" OR id:\"#{parent_document.id}\")",
|
|
29
|
+
q: "(#{q}) AND has_model_ssim:FileSet AND (#{iiif_config[:object_relation_field]}:\"#{parent_document.id}\" OR id:\"#{parent_document.id}\")",
|
|
30
30
|
rows: rows,
|
|
31
31
|
page: page
|
|
32
32
|
}
|
data/iiif_print.gemspec
CHANGED
|
@@ -25,6 +25,7 @@ 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'
|
|
28
29
|
|
|
29
30
|
spec.add_development_dependency 'bixby'
|
|
30
31
|
spec.add_development_dependency 'database_cleaner', '~> 1.3'
|
|
@@ -36,8 +36,9 @@ module IiifPrint
|
|
|
36
36
|
|
|
37
37
|
query_terms = sanitized_query.split(' ')
|
|
38
38
|
|
|
39
|
+
terms_pattern = Regexp.union(query_terms)
|
|
39
40
|
matches = coords_json['coords'].select do |k, _v|
|
|
40
|
-
k.downcase
|
|
41
|
+
k.downcase.match?(terms_pattern)
|
|
41
42
|
end
|
|
42
43
|
return default_coords if matches.blank?
|
|
43
44
|
|
|
@@ -113,16 +114,16 @@ module IiifPrint
|
|
|
113
114
|
|
|
114
115
|
##
|
|
115
116
|
# This method is a workaround to compensate for overriding the solr_params method in
|
|
116
|
-
# BlacklightIiifSearch::IiifSearch. In the override, the solr_params method
|
|
117
|
-
#
|
|
118
|
-
#
|
|
117
|
+
# BlacklightIiifSearch::IiifSearch. In the override, the solr_params method wraps the user's query in
|
|
118
|
+
# parentheses and adds filters for the FileSet model and the object_relation_field OR the parent document's id.
|
|
119
|
+
# This extracts the user's query from inside those parentheses so the filters are not treated as search terms.
|
|
119
120
|
#
|
|
120
121
|
# @see IiifPrint::IiifSearchDecorator#solr_params
|
|
121
|
-
# @return [Regexp] A regular expression
|
|
122
|
+
# @return [Regexp] A regular expression capturing the user's query
|
|
122
123
|
# @example
|
|
123
|
-
# 'foo AND (is_page_of_ssim:\"123123\" OR id:\"123123\")' #=> 'foo'
|
|
124
|
+
# '(foo) AND has_model_ssim:FileSet AND (is_page_of_ssim:\"123123\" OR id:\"123123\")' #=> 'foo'
|
|
124
125
|
def additional_query_terms_regex
|
|
125
|
-
|
|
126
|
+
/\A\((.*)\) AND has_model_ssim/m
|
|
126
127
|
end
|
|
127
128
|
|
|
128
129
|
##
|
|
@@ -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
|
data/lib/iiif_print/engine.rb
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
def label(name, options)
|
|
56
|
+
labels = options[:display_label] || {}
|
|
57
|
+
label = (labels[I18n.locale.to_s] || labels['default'] || name).to_s
|
|
58
|
+
translated = I18n.t(label, default: label) if label.include?('.')
|
|
59
|
+
return translated if translated && translated != label
|
|
60
|
+
|
|
61
|
+
I18n.t(:"blacklight.search.fields.index.#{label}",
|
|
62
|
+
default: [:"blacklight.search.fields.show.#{label}", :"blacklight.search.fields.#{label}", label.humanize])
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
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 dropped
|
|
26
|
+
def raw(field_name)
|
|
27
|
+
Array(@presenter.try(field_name)).reject(&:blank?)
|
|
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 [values.map { |entry| "<p>#{compound_value(field_name, entry)}</p>" }.join] 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
|
+
# 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)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def link(href, text)
|
|
144
|
+
%(<a href="#{ERB::Util.h(href)}">#{ERB::Util.h(text)}</a>)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "iiif_print/flexibility/fields"
|
|
4
|
+
require "iiif_print/flexibility/values"
|
|
5
|
+
|
|
6
|
+
module IiifPrint
|
|
7
|
+
##
|
|
8
|
+
# IIIF manifest metadata for records indexed under an M3 profile, built the way their show page is: which fields
|
|
9
|
+
# from {IiifPrint::Flexibility::Fields}, and how each value renders from {IiifPrint::Flexibility::Values}.
|
|
10
|
+
module Flexibility
|
|
11
|
+
class << self
|
|
12
|
+
##
|
|
13
|
+
# @return [Boolean] whether the record's metadata comes from its M3 profile
|
|
14
|
+
def applies_to?(document)
|
|
15
|
+
Hyrax.config.try(:flexible?) && document.try(:flexible?)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
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)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
# 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
|
+
# for each tenant, schema, version and context; each tenant numbers its own profile versions.
|
|
29
|
+
def view_definitions_for(document)
|
|
30
|
+
cache = RequestStore.store[:iiif_print_view_definitions] ||= {}
|
|
31
|
+
version = document.try(:schema_version)
|
|
32
|
+
contexts = document.try(:contexts)
|
|
33
|
+
tenant = Apartment::Tenant.current if defined?(Apartment::Tenant)
|
|
34
|
+
cache[[tenant, schema_name(document), version, contexts]] ||= begin
|
|
35
|
+
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)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
LEAD_FIELDS = %i[title description abstract].freeze
|
|
43
|
+
|
|
44
|
+
def lead_definitions(document, definitions)
|
|
45
|
+
attributes = profile_attributes(document)
|
|
46
|
+
(LEAD_FIELDS - definitions.to_h.keys.map(&:to_sym)).each_with_object({}) do |name, lead|
|
|
47
|
+
config = attributes[name.to_s]
|
|
48
|
+
lead[name] = Hyrax::SchemaLoader::AttributeDefinition.new(name.to_s, config).view_options if config
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def profile_attributes(document)
|
|
53
|
+
schema = Hyrax::FlexibleSchema.find_by(id: document.try(:schema_version)) || Hyrax::FlexibleSchema.order(:created_at).last
|
|
54
|
+
schema.try(:attributes_for, schema_name(document)) || {}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def schema_name(document)
|
|
58
|
+
Valkyrie.config.resource_class_resolver.call(document.hydra_model_name).to_s
|
|
59
|
+
rescue NameError
|
|
60
|
+
document.hydra_model.to_s
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
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
|
|
|
@@ -96,7 +96,7 @@ module IiifPrint
|
|
|
96
96
|
# of finding child works by title.
|
|
97
97
|
# rubocop:disable Lint/UnusedMethodArgument, Metrics/AbcSize, Metrics/MethodLength
|
|
98
98
|
def self.destroy_children_split_from(file_set:, work:, model:, user:)
|
|
99
|
-
all_child_works = Hyrax.custom_queries.find_child_works(resource: work)
|
|
99
|
+
all_child_works = Hyrax.custom_queries.find_child_works(resource: work).to_a
|
|
100
100
|
return if all_child_works.blank?
|
|
101
101
|
# look first for children by the file set id they were split from
|
|
102
102
|
children = all_child_works.select { |m| m.split_from_pdf_id == file_set.id }
|
data/lib/iiif_print/version.rb
CHANGED
data/lib/iiif_print.rb
CHANGED
|
@@ -13,6 +13,7 @@ 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
|
+
require "iiif_print/flexibility"
|
|
16
17
|
require "iiif_print/split_pdfs/base_splitter"
|
|
17
18
|
require "iiif_print/split_pdfs/child_work_creation_from_pdf_service"
|
|
18
19
|
require "iiif_print/split_pdfs/derivative_rodeo_splitter"
|
|
@@ -203,8 +204,27 @@ module IiifPrint
|
|
|
203
204
|
def self.manifest_metadata_from(work:, presenter:)
|
|
204
205
|
current_ability = presenter.try(:ability) || presenter.try(:current_ability)
|
|
205
206
|
base_url = presenter.try(:base_url) || presenter.try(:request)&.base_url
|
|
207
|
+
document = work.is_a?(::SolrDocument) ? work : ::SolrDocument.new(work)
|
|
208
|
+
return flexible_manifest_metadata_for(document, base_url: base_url) if Flexibility.applies_to?(document)
|
|
209
|
+
|
|
206
210
|
IiifPrint.manifest_metadata_for(work: work, current_ability: current_ability, base_url: base_url)
|
|
207
211
|
end
|
|
212
|
+
|
|
213
|
+
# @api private
|
|
214
|
+
#
|
|
215
|
+
# A flexible work's metadata lists the fields an anonymous visitor would see on its show page, whoever asked
|
|
216
|
+
# and whether or not the work itself is public.
|
|
217
|
+
def self.flexible_manifest_metadata_for(document, base_url:)
|
|
218
|
+
ability = anonymous_ability
|
|
219
|
+
manifest_metadata_for(work: document, fields: Flexibility::Fields.new(document, ability).to_a, current_ability: ability, base_url: base_url)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# One per request: an Ability is costly to build, and a manifest can ask once for every page.
|
|
223
|
+
def self.anonymous_ability
|
|
224
|
+
RequestStore.store[:iiif_print_anonymous_ability] ||= ::Ability.new(nil)
|
|
225
|
+
end
|
|
226
|
+
private_class_method :flexible_manifest_metadata_for, :anonymous_ability
|
|
227
|
+
|
|
208
228
|
# Hash is an arbitrary attribute key/value pairs
|
|
209
229
|
# Struct is a defined set of attribute "keys". When we favor defined values,
|
|
210
230
|
# then we are naming the concept and defining the range of potential values.
|
|
@@ -273,10 +293,10 @@ module IiifPrint
|
|
|
273
293
|
def self.sort_af_fields!(fields, sort_order:)
|
|
274
294
|
return fields if sort_order.blank?
|
|
275
295
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
end
|
|
296
|
+
remaining = sort_order.index(:remaining) || sort_order.length
|
|
297
|
+
fields.each_with_index.sort_by do |field, index|
|
|
298
|
+
[sort_order.index(field.name.to_sym) || remaining, index]
|
|
299
|
+
end.map(&:first)
|
|
280
300
|
end
|
|
281
301
|
|
|
282
302
|
##
|
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.
|
|
4
|
+
version: 3.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sean Upton
|
|
@@ -11,10 +11,9 @@ authors:
|
|
|
11
11
|
- Kirk Wang
|
|
12
12
|
- LaRita Robinson
|
|
13
13
|
- Jeremy Friesen
|
|
14
|
-
autorequire:
|
|
15
14
|
bindir: bin
|
|
16
15
|
cert_chain: []
|
|
17
|
-
date:
|
|
16
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
18
17
|
dependencies:
|
|
19
18
|
- !ruby/object:Gem::Dependency
|
|
20
19
|
name: blacklight_iiif_search
|
|
@@ -98,6 +97,20 @@ dependencies:
|
|
|
98
97
|
- - "~>"
|
|
99
98
|
- !ruby/object:Gem::Version
|
|
100
99
|
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'
|
|
101
114
|
- !ruby/object:Gem::Dependency
|
|
102
115
|
name: bixby
|
|
103
116
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -457,6 +470,9 @@ files:
|
|
|
457
470
|
- lib/iiif_print/data/work_files.rb
|
|
458
471
|
- lib/iiif_print/engine.rb
|
|
459
472
|
- lib/iiif_print/errors.rb
|
|
473
|
+
- lib/iiif_print/flexibility.rb
|
|
474
|
+
- lib/iiif_print/flexibility/fields.rb
|
|
475
|
+
- lib/iiif_print/flexibility/values.rb
|
|
460
476
|
- lib/iiif_print/homepage_search_builder.rb
|
|
461
477
|
- lib/iiif_print/image_tool.rb
|
|
462
478
|
- lib/iiif_print/jp2_derivative_service.rb
|
|
@@ -497,7 +513,6 @@ homepage: https://github.com/notch8/iiif_print/
|
|
|
497
513
|
licenses:
|
|
498
514
|
- Apache-2.0
|
|
499
515
|
metadata: {}
|
|
500
|
-
post_install_message:
|
|
501
516
|
rdoc_options: []
|
|
502
517
|
require_paths:
|
|
503
518
|
- lib
|
|
@@ -512,8 +527,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
512
527
|
- !ruby/object:Gem::Version
|
|
513
528
|
version: '0'
|
|
514
529
|
requirements: []
|
|
515
|
-
rubygems_version: 3.
|
|
516
|
-
signing_key:
|
|
530
|
+
rubygems_version: 3.6.7
|
|
517
531
|
specification_version: 4
|
|
518
532
|
summary: IiifPrint is a gem (Rails "engine") for Hyrax-based digital repository applications
|
|
519
533
|
to support displaying parent/child works in the same viewer (Universal Viewer) and
|