blacklight-spotlight 0.32.0 → 0.33.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +8 -0
- data/app/assets/images/spotlight/default_browse_thumbnail.jpg +0 -0
- data/app/assets/javascripts/spotlight/application.js +6 -1
- data/app/assets/javascripts/spotlight/blocks/pages_block.js +1 -1
- data/app/assets/javascripts/spotlight/blocks/resources_block.js +7 -4
- data/app/assets/javascripts/spotlight/blocks/solr_documents_base_block.js +108 -0
- data/app/assets/javascripts/spotlight/blocks/solr_documents_block.js +12 -56
- data/app/assets/javascripts/spotlight/blocks/solr_documents_carousel_block.js +3 -3
- data/app/assets/javascripts/spotlight/blocks/solr_documents_embed_block.js +3 -3
- data/app/assets/javascripts/spotlight/blocks/solr_documents_features_block.js +3 -3
- data/app/assets/javascripts/spotlight/blocks/solr_documents_grid_block.js +3 -3
- data/app/assets/javascripts/spotlight/crop.es6 +205 -0
- data/app/assets/javascripts/spotlight/croppable.js +7 -104
- data/app/assets/javascripts/spotlight/iiif.es6 +54 -0
- data/app/assets/javascripts/spotlight/multi_image_selector.js +34 -16
- data/app/assets/javascripts/spotlight/pages.js.erb +1 -1
- data/app/assets/javascripts/spotlight/reindex_monitor.js +5 -1
- data/app/assets/javascripts/spotlight/search_typeahead.js +33 -47
- data/app/assets/javascripts/spotlight/sir-trevor/locales.js +5 -2
- data/app/assets/javascripts/spotlight/zpr_links.js.erb +30 -0
- data/app/assets/stylesheets/spotlight/_croppable.scss +8 -13
- data/app/assets/stylesheets/spotlight/_multi_image_selector.scss +1 -1
- data/app/assets/stylesheets/spotlight/_pages.scss +5 -0
- data/app/assets/stylesheets/spotlight/typeahead.css +0 -1
- data/app/controllers/concerns/spotlight/base.rb +3 -5
- data/app/controllers/spotlight/appearances_controller.rb +6 -4
- data/app/controllers/spotlight/catalog_controller.rb +10 -0
- data/app/controllers/spotlight/contacts_controller.rb +2 -6
- data/app/controllers/spotlight/featured_images_controller.rb +26 -0
- data/app/controllers/spotlight/pages_controller.rb +3 -2
- data/app/controllers/spotlight/resources/iiif_harvester_controller.rb +10 -0
- data/app/controllers/spotlight/resources/upload_controller.rb +4 -3
- data/app/controllers/spotlight/searches_controller.rb +20 -6
- data/app/controllers/spotlight/sites_controller.rb +2 -5
- data/app/helpers/spotlight/application_helper.rb +14 -1
- data/app/helpers/spotlight/crop_helper.rb +37 -0
- data/app/helpers/spotlight/main_app_helpers.rb +13 -0
- data/app/helpers/spotlight/meta_helper.rb +10 -20
- data/app/models/concerns/spotlight/solr_document.rb +1 -2
- data/app/models/concerns/spotlight/solr_document/uploaded_resource.rb +1 -23
- data/app/models/sir_trevor_rails/blocks/browse_block.rb +10 -0
- data/app/models/sir_trevor_rails/blocks/featured_pages_block.rb +10 -0
- data/app/models/sir_trevor_rails/blocks/solr_documents_block.rb +5 -0
- data/app/models/spotlight/analytics/ga.rb +1 -1
- data/app/models/spotlight/blacklight_configuration.rb +16 -8
- data/app/models/spotlight/contact.rb +2 -13
- data/app/models/spotlight/contact_image.rb +11 -0
- data/app/models/spotlight/exhibit.rb +11 -8
- data/app/models/spotlight/exhibit_thumbnail.rb +12 -0
- data/app/models/spotlight/feature_page.rb +3 -5
- data/app/models/spotlight/featured_image.rb +28 -9
- data/app/models/spotlight/home_page.rb +2 -0
- data/app/models/spotlight/masthead.rb +5 -11
- data/app/models/spotlight/page.rb +5 -0
- data/app/models/spotlight/reindex_progress.rb +10 -18
- data/app/models/spotlight/reindexing_log_entry.rb +1 -0
- data/app/models/spotlight/resources/iiif_harvester.rb +33 -0
- data/app/models/spotlight/resources/iiif_manifest.rb +211 -0
- data/app/models/spotlight/resources/iiif_service.rb +93 -0
- data/app/models/spotlight/resources/upload.rb +1 -2
- data/app/models/spotlight/search.rb +5 -34
- data/app/presenters/spotlight/iiif_manifest_presenter.rb +79 -0
- data/app/serializers/spotlight/exhibit_export_serializer.rb +9 -41
- data/app/services/spotlight/carrierwave_file_resolver.rb +3 -1
- data/app/services/spotlight/iiif_resource_resolver.rb +73 -0
- data/app/services/spotlight/resources/iiif_builder.rb +17 -0
- data/app/services/spotlight/upload_solr_document_builder.rb +23 -23
- data/app/uploaders/spotlight/attachment_uploader.rb +0 -48
- data/app/uploaders/spotlight/featured_image_uploader.rb +2 -16
- data/app/views/_user_util_links.html.erb +8 -5
- data/app/views/catalog/_save_search.html.erb +4 -2
- data/app/views/layouts/spotlight/spotlight.html.erb +5 -1
- data/app/views/shared/_masthead.html.erb +1 -1
- data/app/views/spotlight/about_pages/_contact_properties.html.erb +1 -1
- data/app/views/spotlight/appearances/edit.html.erb +26 -6
- data/app/views/spotlight/browse/_search.html.erb +1 -1
- data/app/views/spotlight/contacts/_form.html.erb +12 -7
- data/app/views/spotlight/dashboards/_reindexing_activity.html.erb +1 -1
- data/app/views/spotlight/exhibits/_exhibit_card_front.html.erb +2 -2
- data/app/views/spotlight/featured_images/_form.html.erb +12 -15
- data/app/views/spotlight/featured_images/_upload_form.html.erb +6 -12
- data/app/views/spotlight/metadata_configurations/_metadata_field.html.erb +3 -3
- data/app/views/spotlight/pages/_form.html.erb +3 -3
- data/app/views/spotlight/pages/edit.html.erb +4 -2
- data/app/views/spotlight/resources/iiif/_form.html.erb +9 -0
- data/app/views/spotlight/searches/_form.html.erb +3 -3
- data/app/views/spotlight/searches/_search.html.erb +4 -2
- data/app/views/spotlight/sir_trevor/blocks/_browse_block.html.erb +2 -2
- data/app/views/spotlight/sir_trevor/blocks/_featured_pages_block.html.erb +1 -1
- data/app/views/spotlight/sir_trevor/blocks/_solr_documents_block.html.erb +7 -2
- data/app/views/spotlight/sir_trevor/blocks/_solr_documents_carousel_block.html.erb +4 -2
- data/app/views/spotlight/sir_trevor/blocks/_solr_documents_features_block.html.erb +4 -2
- data/app/views/spotlight/sir_trevor/blocks/_solr_documents_grid_block.html.erb +4 -2
- data/app/views/spotlight/sites/edit.html.erb +1 -1
- data/config/locales/spotlight.en.yml +31 -6
- data/config/routes.rb +9 -0
- data/db/migrate/20160714144125_add_iiif_urls_to_featured_image.rb +9 -0
- data/db/migrate/20160718194010_add_iiif_url_to_contact.rb +6 -0
- data/db/migrate/20160805143841_add_upload_id_to_resources.rb +6 -0
- data/db/migrate/20170204091234_add_theme_to_spotlight_exhibits.rb +5 -0
- data/lib/generators/spotlight/install_generator.rb +5 -2
- data/lib/generators/spotlight/templates/config/initializers/spotlight_initializer.rb +0 -1
- data/lib/generators/spotlight/templates/spotlight.scss +3 -2
- data/lib/migration/iiif.rb +82 -0
- data/lib/spotlight/engine.rb +22 -10
- data/lib/spotlight/version.rb +1 -1
- data/lib/tasks/spotlight_tasks.rake +10 -0
- data/spec/controllers/spotlight/about_pages_controller_spec.rb +1 -1
- data/spec/controllers/spotlight/appearances_controller_spec.rb +31 -18
- data/spec/controllers/spotlight/catalog_controller_spec.rb +40 -0
- data/spec/controllers/spotlight/contacts_controller_spec.rb +20 -1
- data/spec/controllers/spotlight/feature_pages_controller_spec.rb +2 -6
- data/spec/controllers/spotlight/featured_images_controller_spec.rb +74 -0
- data/spec/controllers/spotlight/home_pages_controller_spec.rb +1 -1
- data/spec/controllers/spotlight/searches_controller_spec.rb +3 -1
- data/spec/controllers/spotlight/sites_controller_spec.rb +6 -1
- data/spec/examples.txt +1118 -1059
- data/spec/factories/contact_images.rb +6 -0
- data/spec/factories/contacts.rb +4 -1
- data/spec/factories/exhibit_thumbnails.rb +6 -0
- data/spec/factories/exhibits.rb +4 -0
- data/spec/factories/featured_images.rb +1 -0
- data/spec/factories/resources.rb +2 -1
- data/spec/features/add_contacts_spec.rb +5 -5
- data/spec/features/add_iiif_manifest_spec.rb +41 -0
- data/spec/features/add_items_spec.rb +2 -2
- data/spec/features/autocomplete_typeahead_spec.rb +86 -0
- data/spec/features/browse_category_admin_spec.rb +27 -6
- data/spec/features/browse_category_spec.rb +2 -2
- data/spec/features/create_exhibit_spec.rb +3 -3
- data/spec/features/exhibit_masthead_spec.rb +20 -9
- data/spec/features/exhibit_themes_spec.rb +25 -0
- data/spec/features/home_page_spec.rb +1 -1
- data/spec/features/javascript/blocks/solr_documents_block_spec.rb +42 -0
- data/spec/features/javascript/blocks/uploaded_items_block_spec.rb +5 -3
- data/spec/features/javascript/feature_page_admin_spec.rb +1 -1
- data/spec/features/javascript/multi_image_select_spec.rb +5 -6
- data/spec/features/javascript/search_config_admin_spec.rb +1 -1
- data/spec/features/site_masthead_spec.rb +14 -4
- data/spec/fixtures/gk446cj2442-manifest.json +58 -0
- data/spec/fixtures/iiif_responses.rb +274 -0
- data/spec/fixtures/sample_solr_documents.yml +106 -0
- data/spec/helpers/spotlight/crop_helper_spec.rb +9 -0
- data/spec/helpers/spotlight/main_app_helpers_spec.rb +45 -0
- data/spec/helpers/spotlight/meta_helper_spec.rb +2 -15
- data/spec/lib/migration/iiif_spec.rb +70 -0
- data/spec/models/spotlight/blacklight_configuration_spec.rb +17 -5
- data/spec/models/spotlight/contact_image_spec.rb +9 -0
- data/spec/models/spotlight/exhibit_spec.rb +17 -20
- data/spec/models/spotlight/exhibit_thumbnail_spec.rb +8 -0
- data/spec/models/spotlight/featured_image_spec.rb +59 -10
- data/spec/models/spotlight/masthead_spec.rb +33 -17
- data/spec/models/spotlight/page_spec.rb +14 -0
- data/spec/models/spotlight/reindex_progress_spec.rb +22 -73
- data/spec/models/spotlight/resources/iiif_harvester_spec.rb +30 -0
- data/spec/models/spotlight/resources/iiif_manifest_spec.rb +107 -0
- data/spec/models/spotlight/resources/iiif_service_spec.rb +52 -0
- data/spec/models/spotlight/resources/upload_spec.rb +7 -3
- data/spec/models/spotlight/search_spec.rb +0 -45
- data/spec/models/spotlight/solr_document/uploaded_resource_spec.rb +11 -29
- data/spec/presenters/spotlight/iiif_manifest_presenter_spec.rb +123 -0
- data/spec/routing/spotlight/exhibit_catalog_spec.rb +4 -0
- data/spec/routing/spotlight/featured_images_spec.rb +21 -0
- data/spec/serializers/spotlight/exhibit_export_serializer_spec.rb +15 -18
- data/spec/services/spotlight/iiif_resource_resolver_spec.rb +90 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/features/test_features_helpers.rb +3 -2
- data/spec/support/stub_iiif_response.rb +24 -0
- data/spec/support/views/test_view_helpers.rb +1 -0
- data/spec/test_app_templates/Gemfile.extra +0 -1
- data/spec/uploaders/spotlight/attachment_uploader_spec.rb +24 -0
- data/spec/uploaders/spotlight/featured_image_uploader_spec.rb +30 -0
- data/spec/views/_user_util_links.html.erb_spec.rb +9 -5
- data/spec/views/shared/_masthead.html.erb_spec.rb +5 -2
- data/spec/views/spotlight/browse/_search.html.erb_spec.rb +2 -2
- data/spec/views/spotlight/contacts/edit.html.erb_spec.rb +4 -7
- data/spec/views/spotlight/metadata_configurations/_metadata_field.html.erb_spec.rb +7 -2
- data/spec/views/spotlight/pages/edit.html.erb_spec.rb +3 -1
- data/spec/views/spotlight/pages/new.html.erb_spec.rb +3 -1
- data/spec/views/spotlight/searches/_search.html.erb_spec.rb +3 -2
- data/spec/views/spotlight/searches/edit.html.erb_spec.rb +3 -2
- data/vendor/assets/javascripts/leaflet-areaselect.js +184 -0
- data/vendor/assets/javascripts/leaflet-iiif.js +230 -0
- data/vendor/assets/javascripts/leaflet.js +9 -0
- data/vendor/assets/javascripts/polyfill.min.js +4 -0
- data/vendor/assets/stylesheets/leaflet-areaselect.css +15 -0
- data/vendor/assets/stylesheets/leaflet.css +624 -0
- metadata +136 -62
- data/app/assets/javascripts/spotlight/jcrop.js +0 -1696
- data/app/helpers/spotlight/jcrop_helper.rb +0 -37
- data/app/models/concerns/spotlight/default_thumbnailable.rb +0 -25
- data/app/models/concerns/spotlight/image_derivatives.rb +0 -58
- data/app/models/concerns/spotlight/solr_document/spotlight_images.rb +0 -55
- data/app/uploaders/spotlight/avatar_uploader.rb +0 -24
- data/app/uploaders/spotlight/item_uploader.rb +0 -25
- data/app/uploaders/spotlight/masthead_uploader.rb +0 -22
- data/spec/helpers/spotlight/jcrop_helper_spec.rb +0 -33
- data/spec/models/spotlight/default_thumbnailable_concern_spec.rb +0 -16
- data/spec/models/spotlight/image_derivatives_spec.rb +0 -15
- data/spec/models/spotlight/solr_document/spotlight_images_spec.rb +0 -42
- data/spec/uploaders/spotlight/item_uploader_spec.rb +0 -67
@@ -10,6 +10,7 @@ module Spotlight
|
|
10
10
|
# null start times sort to the top, to more easily surface pending reindexing
|
11
11
|
default_scope { order('start_time IS NOT NULL, start_time DESC') }
|
12
12
|
scope :recent, -> { limit(5) }
|
13
|
+
scope :started_or_completed, -> { where.not(job_status: 'unstarted') }
|
13
14
|
|
14
15
|
def duration
|
15
16
|
end_time - start_time if end_time
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'iiif/presentation'
|
2
|
+
|
3
|
+
module Spotlight
|
4
|
+
module Resources
|
5
|
+
# harvest Images from IIIF Manifest and turn them into a Spotlight::Resource
|
6
|
+
# Note: IIIF API : http://iiif.io/api/presentation/2.0
|
7
|
+
class IiifHarvester < Spotlight::Resource
|
8
|
+
self.document_builder_class = Spotlight::Resources::IiifBuilder
|
9
|
+
self.weight = -5000
|
10
|
+
|
11
|
+
validate :valid_url?
|
12
|
+
|
13
|
+
def iiif_manifests
|
14
|
+
@iiif_manifests ||= IiifService.parse(url)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def valid_url?
|
20
|
+
errors.add(:url, 'Invalid IIIF URL') unless url_is_iiif?(url)
|
21
|
+
end
|
22
|
+
|
23
|
+
def url_is_iiif?(url)
|
24
|
+
valid_content_types = ['application/json', 'application/ld+json']
|
25
|
+
req = Faraday.head(url)
|
26
|
+
return unless req.success?
|
27
|
+
valid_content_types.any? do |valid_type|
|
28
|
+
req.headers['content-type'].include?(valid_type)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
module Spotlight
|
2
|
+
module Resources
|
3
|
+
##
|
4
|
+
# A PORO to construct a solr hash for a given IiifManifest
|
5
|
+
class IiifManifest
|
6
|
+
attr_reader :collection
|
7
|
+
def initialize(attrs = {})
|
8
|
+
@url = attrs[:url]
|
9
|
+
@manifest = attrs[:manifest]
|
10
|
+
@collection = attrs[:collection]
|
11
|
+
@solr_hash = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_solr
|
15
|
+
add_document_id
|
16
|
+
add_label
|
17
|
+
add_thumbnail_url
|
18
|
+
add_full_image_urls
|
19
|
+
add_manifest_url
|
20
|
+
add_image_urls
|
21
|
+
add_metadata
|
22
|
+
add_collection_id
|
23
|
+
solr_hash
|
24
|
+
end
|
25
|
+
|
26
|
+
def with_exhibit(e)
|
27
|
+
@exhibit = e
|
28
|
+
end
|
29
|
+
|
30
|
+
def compound_id
|
31
|
+
Digest::MD5.hexdigest("#{exhibit.id}-#{url}")
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :url, :manifest, :exhibit, :solr_hash
|
37
|
+
delegate :blacklight_config, to: :exhibit
|
38
|
+
|
39
|
+
def add_document_id
|
40
|
+
solr_hash[exhibit.blacklight_config.document_model.unique_key.to_sym] = compound_id
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_collection_id
|
44
|
+
solr_hash[collection_id_field] = [collection.compound_id] if collection
|
45
|
+
end
|
46
|
+
|
47
|
+
def collection_id_field
|
48
|
+
Spotlight::Engine.config.iiif_collection_id_field
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_manifest_url
|
52
|
+
solr_hash[Spotlight::Engine.config.iiif_manifest_field] = url
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_thumbnail_url
|
56
|
+
return unless thumbnail_field && manifest['thumbnail'].present?
|
57
|
+
solr_hash[thumbnail_field] = manifest['thumbnail']['@id']
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_full_image_urls
|
61
|
+
return unless full_image_field && full_image_url
|
62
|
+
solr_hash[full_image_field] = full_image_url
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_label
|
66
|
+
return unless title_field && manifest.try(:label)
|
67
|
+
solr_hash[title_field] = manifest.label
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_image_urls
|
71
|
+
solr_hash[tile_source_field] = image_urls
|
72
|
+
end
|
73
|
+
|
74
|
+
def add_metadata
|
75
|
+
solr_hash.merge!(manifest_metadata)
|
76
|
+
sidecar.update(data: sidecar.data.merge(manifest_metadata))
|
77
|
+
end
|
78
|
+
|
79
|
+
def manifest_metadata
|
80
|
+
metadata = metadata_class.new(manifest).to_solr
|
81
|
+
return {} unless metadata.present?
|
82
|
+
create_sidecars_for(*metadata.keys)
|
83
|
+
|
84
|
+
metadata.each_with_object({}) do |(key, value), hash|
|
85
|
+
next unless (field = exhibit_custom_fields[key])
|
86
|
+
hash[field.field] = value
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def create_sidecars_for(*keys)
|
91
|
+
missing_keys(keys).each do |k|
|
92
|
+
exhibit.custom_fields.create! label: k, readonly_field: true
|
93
|
+
end
|
94
|
+
@exhibit_custom_fields = nil
|
95
|
+
end
|
96
|
+
|
97
|
+
def missing_keys(keys)
|
98
|
+
custom_field_keys = exhibit_custom_fields.keys.map(&:downcase)
|
99
|
+
keys.reject do |key|
|
100
|
+
custom_field_keys.include?(key.downcase)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def exhibit_custom_fields
|
105
|
+
@exhibit_custom_fields ||= exhibit.custom_fields.each_with_object({}) do |value, hash|
|
106
|
+
hash[value.label] = value
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def image_urls
|
111
|
+
@image_urls ||= resources.map do |resource|
|
112
|
+
next unless resource && !resource.service.empty?
|
113
|
+
image_url = resource.service['@id']
|
114
|
+
image_url << '/info.json' unless image_url.downcase.ends_with?('/info.json')
|
115
|
+
image_url
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def full_image_url
|
120
|
+
resources.first.try(:[], '@id')
|
121
|
+
end
|
122
|
+
|
123
|
+
def resources
|
124
|
+
@resources ||= sequences
|
125
|
+
.flat_map(&:canvases)
|
126
|
+
.flat_map(&:images)
|
127
|
+
.flat_map(&:resource)
|
128
|
+
end
|
129
|
+
|
130
|
+
def sequences
|
131
|
+
manifest.try(:sequences) || []
|
132
|
+
end
|
133
|
+
|
134
|
+
def thumbnail_field
|
135
|
+
blacklight_config.index.try(:thumbnail_field)
|
136
|
+
end
|
137
|
+
|
138
|
+
def full_image_field
|
139
|
+
Spotlight::Engine.config.full_image_field
|
140
|
+
end
|
141
|
+
|
142
|
+
def tile_source_field
|
143
|
+
blacklight_config.show.try(:tile_source_field)
|
144
|
+
end
|
145
|
+
|
146
|
+
def title_field
|
147
|
+
blacklight_config.index.try(:title_field)
|
148
|
+
end
|
149
|
+
|
150
|
+
def sidecar
|
151
|
+
@sidecar ||= document_model.new(id: compound_id).sidecar(exhibit)
|
152
|
+
end
|
153
|
+
|
154
|
+
def document_model
|
155
|
+
exhibit.blacklight_config.document_model
|
156
|
+
end
|
157
|
+
|
158
|
+
def metadata_class
|
159
|
+
Spotlight::Engine.config.iiif_metadata_class.call
|
160
|
+
end
|
161
|
+
|
162
|
+
###
|
163
|
+
# A simple class to map the metadata field
|
164
|
+
# in a IIIF document to label/value pairs
|
165
|
+
# This is intended to be overriden by an
|
166
|
+
# application if a different metadata
|
167
|
+
# strucure is used by the consumer
|
168
|
+
class Metadata
|
169
|
+
def initialize(manifest)
|
170
|
+
@manifest = manifest
|
171
|
+
end
|
172
|
+
|
173
|
+
def to_solr
|
174
|
+
metadata_hash.merge(manifest_level_metadata)
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
attr_reader :manifest
|
180
|
+
|
181
|
+
def metadata
|
182
|
+
manifest.try(:metadata) || []
|
183
|
+
end
|
184
|
+
|
185
|
+
def metadata_hash
|
186
|
+
return {} unless metadata.present?
|
187
|
+
return {} unless metadata.is_a?(Array)
|
188
|
+
|
189
|
+
metadata.each_with_object({}) do |md, hash|
|
190
|
+
next unless md['label'] && md['value']
|
191
|
+
hash[md['label']] ||= []
|
192
|
+
hash[md['label']] += Array(md['value'])
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def manifest_level_metadata
|
197
|
+
manifest_fields.each_with_object({}) do |field, hash|
|
198
|
+
next unless manifest.respond_to?(field) &&
|
199
|
+
manifest.send(field).present?
|
200
|
+
hash[field.capitalize] ||= []
|
201
|
+
hash[field.capitalize] += Array(manifest.send(field))
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def manifest_fields
|
206
|
+
%w(attribution description license)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'iiif/presentation'
|
2
|
+
module Spotlight
|
3
|
+
module Resources
|
4
|
+
###
|
5
|
+
# Wrapper around IIIF-Presentation's IIIF::Service that provides the
|
6
|
+
# ability to recursively traverse through all collections and manifests
|
7
|
+
class IiifService
|
8
|
+
def initialize(url)
|
9
|
+
@url = url
|
10
|
+
end
|
11
|
+
|
12
|
+
def collections
|
13
|
+
@collections ||= (object.try(:collections) || []).map do |collection|
|
14
|
+
self.class.new(collection['@id'])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def manifests
|
19
|
+
@manifests ||= if manifest?
|
20
|
+
[create_iiif_manifest(object)]
|
21
|
+
else
|
22
|
+
build_collection_manifest.to_a
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.parse(url)
|
27
|
+
recursive_manifests(new(url))
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def object
|
33
|
+
@object ||= IIIF::Service.parse(response)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :url
|
39
|
+
|
40
|
+
class << self
|
41
|
+
def iiif_response(url)
|
42
|
+
Faraday.get(url).body
|
43
|
+
rescue Faraday::Error::ConnectionFailed, Faraday::TimeoutError => e
|
44
|
+
Rails.logger.warn("HTTP GET for #{url} failed with #{e}")
|
45
|
+
{}.to_json
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def recursive_manifests(thing, &block)
|
51
|
+
return to_enum(:recursive_manifests, thing) unless block_given?
|
52
|
+
|
53
|
+
thing.manifests.each(&block)
|
54
|
+
|
55
|
+
return unless thing.collections.present?
|
56
|
+
|
57
|
+
thing.collections.each do |collection|
|
58
|
+
recursive_manifests(collection, &block)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def create_iiif_manifest(manifest, collection = nil)
|
64
|
+
IiifManifest.new(url: manifest['@id'], manifest: manifest, collection: collection)
|
65
|
+
end
|
66
|
+
|
67
|
+
def manifest?
|
68
|
+
object.is_a?(IIIF::Presentation::Manifest)
|
69
|
+
end
|
70
|
+
|
71
|
+
def collection?
|
72
|
+
object.is_a?(IIIF::Presentation::Collection)
|
73
|
+
end
|
74
|
+
|
75
|
+
def response
|
76
|
+
@response ||= self.class.iiif_response(url)
|
77
|
+
end
|
78
|
+
|
79
|
+
def build_collection_manifest
|
80
|
+
return to_enum(:build_collection_manifest) unless block_given?
|
81
|
+
|
82
|
+
if collection?
|
83
|
+
self_manifest = create_iiif_manifest(object)
|
84
|
+
yield self_manifest
|
85
|
+
end
|
86
|
+
|
87
|
+
(object.try(:manifests) || []).each do |manifest|
|
88
|
+
yield create_iiif_manifest(self.class.new(manifest['@id']).object, self_manifest)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -4,8 +4,7 @@ module Spotlight
|
|
4
4
|
##
|
5
5
|
# Exhibit-specific resources, created using uploaded and custom fields
|
6
6
|
class Upload < Spotlight::Resource
|
7
|
-
|
8
|
-
include Spotlight::ImageDerivatives
|
7
|
+
belongs_to :upload, class_name: 'Spotlight::FeaturedImage'
|
9
8
|
|
10
9
|
# we want to do this before reindexing
|
11
10
|
after_create :update_document_sidecar
|
@@ -2,7 +2,6 @@ module Spotlight
|
|
2
2
|
##
|
3
3
|
# Exhibit saved searches
|
4
4
|
class Search < ActiveRecord::Base
|
5
|
-
include DefaultThumbnailable
|
6
5
|
extend FriendlyId
|
7
6
|
friendly_id :title, use: [:slugged, :scoped, :finders, :history], scope: :exhibit
|
8
7
|
|
@@ -16,23 +15,12 @@ module Spotlight
|
|
16
15
|
|
17
16
|
belongs_to :masthead, dependent: :destroy
|
18
17
|
belongs_to :thumbnail, class_name: 'Spotlight::FeaturedImage', dependent: :destroy
|
19
|
-
accepts_nested_attributes_for :thumbnail, update_only: true
|
20
|
-
accepts_nested_attributes_for :masthead, update_only: true
|
18
|
+
accepts_nested_attributes_for :thumbnail, update_only: true, reject_if: proc { |attr| attr['iiif_tilesource'].blank? }
|
19
|
+
accepts_nested_attributes_for :masthead, update_only: true, reject_if: proc { |attr| attr['iiif_tilesource'].blank? }
|
21
20
|
|
22
21
|
def thumbnail_image_url
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
def images
|
27
|
-
return enum_for(:images) { documents.size } unless block_given?
|
28
|
-
|
29
|
-
documents.each do |doc|
|
30
|
-
yield [
|
31
|
-
doc.first(blacklight_config.document_model.unique_key),
|
32
|
-
doc.first(blacklight_config.index.title_field),
|
33
|
-
doc.first(blacklight_config.index.thumbnail_field)
|
34
|
-
]
|
35
|
-
end
|
22
|
+
return unless thumbnail && thumbnail.iiif_url
|
23
|
+
thumbnail.iiif_url
|
36
24
|
end
|
37
25
|
|
38
26
|
def documents
|
@@ -58,22 +46,6 @@ module Spotlight
|
|
58
46
|
masthead && masthead.display?
|
59
47
|
end
|
60
48
|
|
61
|
-
# rubocop:disable Metrics/MethodLength
|
62
|
-
def set_default_thumbnail
|
63
|
-
self.thumbnail ||= begin
|
64
|
-
return unless Spotlight::Engine.config.full_image_field
|
65
|
-
doc = documents.detect { |x| x.first(Spotlight::Engine.config.full_image_field) }
|
66
|
-
if doc
|
67
|
-
create_thumbnail(
|
68
|
-
source: 'exhibit',
|
69
|
-
document_global_id: doc.to_global_id.to_s,
|
70
|
-
remote_image_url: doc.first(Spotlight::Engine.config.full_image_field)
|
71
|
-
)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
# rubocop:enable Metrics/MethodLength
|
76
|
-
|
77
49
|
def search_params
|
78
50
|
search_builder.with(query_params.with_indifferent_access).merge(facet: false, fl: default_search_fields)
|
79
51
|
end
|
@@ -102,8 +74,7 @@ module Spotlight
|
|
102
74
|
[
|
103
75
|
blacklight_config.document_model.unique_key,
|
104
76
|
blacklight_config.index.title_field,
|
105
|
-
blacklight_config.index.thumbnail_field
|
106
|
-
Spotlight::Engine.config.full_image_field
|
77
|
+
blacklight_config.index.thumbnail_field
|
107
78
|
].compact
|
108
79
|
end
|
109
80
|
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Spotlight
|
2
|
+
##
|
3
|
+
# A presenter class that provides the methods that IIIFManifest expects, as well as convenience methods
|
4
|
+
# that will generate a IIIFManifest object, and the actual JSON manifest from the IIIFManifest object.
|
5
|
+
# Instances of this class represent IIIF leaf nodes. We do not currently generate manifests with interstitial
|
6
|
+
# nodes.
|
7
|
+
#
|
8
|
+
# IIIFManifest expects the following methods: #file_set_presenters, #work_presenters, #manifest_url, #description.
|
9
|
+
# see: https://github.com/projecthydra-labs/iiif_manifest/blob/master/README.md
|
10
|
+
class IiifManifestPresenter
|
11
|
+
require 'iiif_manifest'
|
12
|
+
|
13
|
+
# The class that represents the leaf nodes must implement #id (here implemented
|
14
|
+
# via delegation to the resource, since this class represents leaf nodes).
|
15
|
+
delegate :id, :uploaded_resource, to: :resource
|
16
|
+
delegate :blacklight_config, to: :controller
|
17
|
+
|
18
|
+
attr_accessor :resource, :controller
|
19
|
+
|
20
|
+
def initialize(resource, controller)
|
21
|
+
@resource = resource
|
22
|
+
@controller = controller
|
23
|
+
end
|
24
|
+
|
25
|
+
# IIIFManifest expects leaf nodes to implement #display_image, which returns an instance of IIIFManifest::DisplayImage.
|
26
|
+
def display_image
|
27
|
+
IIIFManifest::DisplayImage.new(id,
|
28
|
+
width: resource.first(:spotlight_full_image_width_ssm),
|
29
|
+
height: resource.first(:spotlight_full_image_height_ssm),
|
30
|
+
format: 'image/jpeg',
|
31
|
+
iiif_endpoint: endpoint)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns an array of leaf nodes. Currently, this is a single element array containing this presenter
|
35
|
+
# instance, since we're only building a single-image manifest for the given resource.
|
36
|
+
def file_set_presenters
|
37
|
+
[self]
|
38
|
+
end
|
39
|
+
|
40
|
+
# This is an empty array, since we're not building manifests for works at the moment.
|
41
|
+
def work_presenters
|
42
|
+
[]
|
43
|
+
end
|
44
|
+
|
45
|
+
# where this manifest can be found
|
46
|
+
def manifest_url
|
47
|
+
controller.spotlight.manifest_exhibit_solr_document_url(uploaded_resource.exhibit, resource)
|
48
|
+
end
|
49
|
+
|
50
|
+
# a description of the manifest
|
51
|
+
def description
|
52
|
+
resource.first(Spotlight::Engine.config.upload_description_field)
|
53
|
+
end
|
54
|
+
|
55
|
+
# IIIFManifest will call #to_s on each leaf node to get its respective label (not called out in README).
|
56
|
+
def to_s
|
57
|
+
resource.first(blacklight_config.view_config(:show).title_field)
|
58
|
+
end
|
59
|
+
|
60
|
+
def iiif_manifest
|
61
|
+
IIIFManifest::ManifestFactory.new(self)
|
62
|
+
end
|
63
|
+
|
64
|
+
def iiif_manifest_json
|
65
|
+
iiif_manifest.to_h.to_json
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def endpoint
|
71
|
+
IIIFManifest::IIIFEndpoint.new(iiif_url, profile: 'http://iiif.io/api/image/2/level2.json')
|
72
|
+
end
|
73
|
+
|
74
|
+
def iiif_url
|
75
|
+
# yes this is hacky, and we are appropriately ashamed.
|
76
|
+
controller.riiif.info_url.sub(%r{/info\.json\Z}, '')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|