blacklight-spotlight 5.3.2 → 6.0.0.rc2
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/README.md +41 -4
- data/Rakefile +1 -2
- data/app/assets/javascripts/spotlight/spotlight.esm.js +2168 -4560
- data/app/assets/javascripts/spotlight/spotlight.esm.js.map +1 -1
- data/app/assets/javascripts/spotlight/spotlight.js +2167 -4559
- data/app/assets/javascripts/spotlight/spotlight.js.map +1 -1
- data/app/assets/stylesheets/spotlight/_blacklight_overrides.scss +4 -0
- data/app/assets/stylesheets/spotlight/_browse.scss +0 -4
- data/app/assets/stylesheets/spotlight/{_collapse_toggle.scss → _collapse_toggle.css} +2 -2
- data/app/assets/stylesheets/spotlight/_exhibit_admin.scss +8 -0
- data/app/assets/stylesheets/spotlight/_exhibits_index.scss +2 -1
- data/app/assets/stylesheets/spotlight/_footer.scss +2 -2
- data/app/assets/stylesheets/spotlight/_header.scss +8 -8
- data/app/assets/stylesheets/spotlight/_pages.scss +1 -1
- data/app/assets/stylesheets/spotlight/_slideshow_block.scss +15 -0
- data/app/assets/stylesheets/spotlight/_spotlight.scss +1 -1
- data/app/assets/stylesheets/spotlight/_translations.scss +3 -3
- data/app/assets/stylesheets/spotlight/_uploaded_items_block.css +7 -0
- data/app/assets/stylesheets/spotlight/_variables.css +15 -0
- data/app/assets/stylesheets/spotlight/browse_group_categories_block.scss +0 -76
- data/app/components/blacklight/icons/pause_component.rb +15 -0
- data/app/components/blacklight/icons/play_arrow_component.rb +15 -0
- data/app/components/spotlight/analytics/dashboard_component.html.erb +1 -1
- data/app/components/spotlight/custom_field_form_group_component.rb +40 -0
- data/app/components/spotlight/document_admin_table_component.html.erb +2 -2
- data/app/components/spotlight/edit_sidecar_component.html.erb +18 -0
- data/app/components/spotlight/edit_sidecar_component.rb +41 -0
- data/app/components/spotlight/masthead_component.html.erb +18 -0
- data/app/components/spotlight/masthead_component.rb +46 -0
- data/app/components/spotlight/save_search_component.html.erb +1 -1
- data/app/components/spotlight/sidecar_form_field_component.html.erb +18 -0
- data/app/components/spotlight/sidecar_form_field_component.rb +71 -0
- data/app/components/spotlight/solr_document_legacy_embed_component.html.erb +0 -4
- data/app/components/spotlight/solr_document_legacy_embed_component.rb +2 -2
- data/app/components/spotlight/tag_selector_component.html.erb +1 -1
- data/app/components/spotlight/tag_selector_component.rb +3 -2
- data/app/components/spotlight/upload_field_form_group_component.rb +33 -0
- data/app/controllers/concerns/spotlight/controller.rb +15 -14
- data/app/controllers/concerns/spotlight/search_helper.rb +2 -5
- data/app/controllers/spotlight/browse_controller.rb +1 -6
- data/app/controllers/spotlight/catalog_controller.rb +2 -3
- data/app/controllers/spotlight/concerns/application_controller.rb +4 -6
- data/app/controllers/spotlight/exhibits_controller.rb +22 -10
- data/app/controllers/spotlight/lock_controller.rb +1 -1
- data/app/controllers/spotlight/pages_controller.rb +11 -11
- data/app/controllers/spotlight/resources/upload_controller.rb +1 -1
- data/app/controllers/spotlight/resources_controller.rb +1 -1
- data/app/controllers/spotlight/solr_controller.rb +5 -1
- data/app/helpers/spotlight/application_helper.rb +14 -26
- data/app/helpers/spotlight/crud_link_helpers.rb +0 -12
- data/app/helpers/spotlight/main_app_helpers.rb +2 -2
- data/app/javascript/spotlight/admin/add_another.js +37 -25
- data/app/javascript/spotlight/admin/add_image_selector.js +43 -26
- data/app/javascript/spotlight/admin/add_new_button.js +51 -36
- data/app/javascript/spotlight/admin/blacklight_configuration.js +76 -54
- data/app/javascript/spotlight/admin/block_mixins/autocompleteable.js +102 -101
- data/app/javascript/spotlight/admin/block_mixins/formable.js +91 -75
- data/app/javascript/spotlight/admin/block_mixins/plustextable.js +46 -48
- data/app/javascript/spotlight/admin/blocks/block.js +27 -28
- data/app/javascript/spotlight/admin/blocks/browse_block.js +14 -10
- data/app/javascript/spotlight/admin/blocks/browse_group_categories_block.js +14 -10
- data/app/javascript/spotlight/admin/blocks/pages_block.js +5 -4
- data/app/javascript/spotlight/admin/blocks/resources_block.js +61 -41
- data/app/javascript/spotlight/admin/blocks/search_result_block.js +24 -7
- data/app/javascript/spotlight/admin/blocks/solr_documents_base_block.js +103 -50
- data/app/javascript/spotlight/admin/blocks/solr_documents_carousel_block.js +50 -26
- data/app/javascript/spotlight/admin/blocks/solr_documents_features_block.js +47 -23
- data/app/javascript/spotlight/admin/blocks/uploaded_items_block.js +35 -19
- data/app/javascript/spotlight/admin/crop.js +146 -66
- data/app/javascript/spotlight/admin/croppable.js +5 -4
- data/app/javascript/spotlight/admin/croppable_modal.js +67 -32
- data/app/javascript/spotlight/admin/edit_in_place.js +69 -54
- data/app/javascript/spotlight/admin/exhibits.js +92 -45
- data/app/javascript/spotlight/admin/form_observer.js +92 -78
- data/app/javascript/spotlight/admin/multi_image_selector.js +201 -153
- data/app/javascript/spotlight/admin/pages.js +13 -7
- data/app/javascript/spotlight/admin/progress_monitor.js +65 -50
- data/app/javascript/spotlight/admin/readonly_checkbox.js +7 -3
- data/app/javascript/spotlight/admin/search_typeahead.js +20 -11
- data/app/javascript/spotlight/admin/select_related_input.js +23 -23
- data/app/javascript/spotlight/admin/serialize_json.js +120 -0
- data/app/javascript/spotlight/admin/sir-trevor/block_controls.js +24 -11
- data/app/javascript/spotlight/admin/sir-trevor/block_limits.js +9 -6
- data/app/javascript/spotlight/admin/sir-trevor/locales.js +2 -2
- data/app/javascript/spotlight/admin/spotlight_nestable.js +2 -5
- data/app/javascript/spotlight/admin/translation_progress.js +18 -13
- data/app/javascript/spotlight/admin/users.js +151 -64
- data/app/javascript/spotlight/user/carousel.js +104 -32
- data/app/javascript/spotlight/user/clear_form_button.js +24 -14
- data/app/javascript/spotlight/user/index.js +0 -2
- data/app/javascript/spotlight/user/zpr_links.js +99 -40
- data/app/jobs/concerns/spotlight/gather_documents.rb +1 -1
- data/app/jobs/spotlight/add_uploads_from_csv.rb +31 -8
- data/app/jobs/spotlight/cleanup_job_trackers_job.rb +5 -3
- data/app/models/concerns/spotlight/access_controls_enforcement_search_builder.rb +13 -2
- data/app/models/concerns/spotlight/browse_category_search_builder.rb +3 -3
- data/app/models/concerns/spotlight/exhibit_documents.rb +2 -2
- data/app/models/concerns/spotlight/sitemap.rb +2 -2
- data/app/models/concerns/spotlight/solr_document/finder.rb +2 -2
- data/app/models/concerns/spotlight/solr_document.rb +1 -47
- data/app/models/sir_trevor_rails/blocks/browse_block.rb +0 -4
- data/app/models/sir_trevor_rails/blocks/solr_documents_block.rb +2 -1
- data/app/models/spotlight/blacklight_configuration.rb +5 -3
- data/app/models/spotlight/contact_email.rb +2 -2
- data/app/models/spotlight/custom_field.rb +17 -0
- data/app/models/spotlight/exhibit.rb +28 -0
- data/app/models/spotlight/featured_image.rb +1 -1
- data/app/models/spotlight/field_metadata.rb +10 -5
- data/app/models/spotlight/job_tracker.rb +0 -18
- data/app/models/spotlight/main_navigation.rb +2 -2
- data/app/models/spotlight/page.rb +3 -3
- data/app/models/spotlight/resources/iiif_harvester.rb +7 -4
- data/app/models/spotlight/resources/iiif_manifest.rb +0 -4
- data/app/models/spotlight/resources/iiif_manifest_metadata.rb +1 -3
- data/app/models/spotlight/resources/iiif_service.rb +21 -6
- data/app/models/spotlight/resources/upload.rb +6 -0
- data/app/models/spotlight/search.rb +5 -9
- data/app/models/spotlight/solr_document_sidecar.rb +18 -0
- data/app/services/spotlight/bulk_updates_csv_template_service.rb +1 -1
- data/app/services/spotlight/etl/pipeline.rb +2 -2
- data/app/services/spotlight/etl/step.rb +2 -2
- data/app/views/catalog/_remove_tags.html.erb +2 -2
- data/app/views/layouts/spotlight/base.html.erb +3 -6
- data/app/views/layouts/spotlight/spotlight.html.erb +2 -2
- data/app/views/shared/_analytics.html.erb +4 -3
- data/app/views/shared/_masthead.html.erb +2 -18
- data/app/views/spotlight/about_pages/_empty.html.erb +1 -1
- data/app/views/spotlight/about_pages/_sidebar.html.erb +10 -8
- data/app/views/spotlight/browse/_search.html.erb +2 -2
- data/app/views/spotlight/browse/_search_box.html.erb +6 -14
- data/app/views/spotlight/browse/index.html.erb +1 -1
- data/app/views/spotlight/browse/show.html.erb +1 -1
- data/app/views/spotlight/catalog/_document_admin_table.html.erb +1 -1
- data/app/views/spotlight/catalog/_edit_default.html.erb +1 -1
- data/app/views/spotlight/catalog/edit.html.erb +1 -1
- data/app/views/spotlight/dashboards/_page.html.erb +1 -1
- data/app/views/spotlight/exhibits/_exhibit_card.html.erb +3 -1
- data/app/views/spotlight/exhibits/_export.html.erb +17 -1
- data/app/views/spotlight/exhibits/_form.html.erb +20 -4
- data/app/views/spotlight/exhibits/_missing_exhibits.html.erb +3 -3
- data/app/views/spotlight/exhibits/index.html.erb +21 -8
- data/app/views/spotlight/feature_pages/_empty.html.erb +1 -1
- data/app/views/spotlight/feature_pages/_sidebar.html.erb +17 -15
- data/app/views/spotlight/home_pages/_empty.html.erb +7 -7
- data/app/views/spotlight/pages/show.html.erb +1 -0
- data/app/views/spotlight/resources/upload/_form.html.erb +2 -2
- data/app/views/spotlight/sir_trevor/blocks/_browse_group_categories_block.html.erb +5 -16
- data/app/views/spotlight/sir_trevor/blocks/_embedded_document.html.erb +3 -5
- data/app/views/spotlight/sir_trevor/blocks/_link_to_search_block.html.erb +1 -1
- data/app/views/spotlight/sir_trevor/blocks/_solr_documents_carousel_block.html.erb +11 -2
- data/app/views/spotlight/sites/_exhibit.html.erb +1 -1
- data/config/i18n-tasks.yml +1 -1
- data/config/locales/spotlight.ar.yml +1 -2
- data/config/locales/spotlight.de.yml +1 -2
- data/config/locales/spotlight.en.yml +39 -10
- data/config/locales/spotlight.es.yml +1 -2
- data/config/locales/spotlight.fr.yml +1 -2
- data/config/locales/spotlight.it.yml +1 -2
- data/config/locales/spotlight.ja.yml +39 -0
- data/config/locales/spotlight.zh.yml +1 -2
- data/db/migrate/20261007133748_add_discovery_enabled_to_spotlight_exhibits.rb +5 -0
- data/lib/generators/spotlight/assets/importmap_generator.rb +2 -10
- data/lib/generators/spotlight/assets/propshaft_generator.rb +5 -4
- data/lib/generators/spotlight/install_generator.rb +2 -4
- data/lib/generators/spotlight/templates/catalog_controller.rb +0 -1
- data/lib/generators/spotlight/templates/config/initializers/spotlight_initializer.rb +26 -4
- data/lib/generators/spotlight/templates/solr/conf/schema.xml +4 -3
- data/lib/generators/spotlight/templates/solr/conf/solrconfig.xml +3 -8
- data/lib/migration/page_language.rb +0 -2
- data/lib/spotlight/engine.rb +28 -9
- data/lib/spotlight/search_state.rb +7 -11
- data/lib/spotlight/upload_field_config.rb +19 -1
- data/lib/spotlight/version.rb +1 -1
- data/lib/spotlight.rb +1 -1
- data/spec/support/features/capybara_wait_metadata_helper.rb +3 -1
- data/spec/support/features/test_features_helpers.rb +24 -1
- data/spec/support/iiif_manifest_fixtures.rb +33 -0
- data/spec/support/stub_dns_resolution.rb +12 -0
- metadata +50 -80
- data/app/assets/stylesheets/spotlight/_uploaded_items_block.scss +0 -7
- data/app/assets/stylesheets/spotlight/_variables.scss +0 -13
- data/app/components/blacklight/icons/arrow_alt_circle_left_component.rb +0 -15
- data/app/components/blacklight/icons/arrow_alt_circle_right_component.rb +0 -15
- data/app/components/blacklight/icons/info_component.rb +0 -15
- data/app/components/blacklight/icons/zoom_in_component.rb +0 -15
- data/app/components/blacklight/icons/zoom_out_component.rb +0 -15
- data/app/components/spotlight/icon_component.rb +0 -13
- data/app/javascript/spotlight/user/browse_group_categories.js +0 -54
- data/app/mailers/spotlight/invitation_mailer.rb +0 -21
- data/app/models/concerns/spotlight/resources/web.rb +0 -38
- data/app/services/spotlight/iiif_resource_resolver.rb +0 -78
- data/app/views/catalog/_image_default.html.erb +0 -6
- data/app/views/shared/_exhibit_navbar.html.erb +0 -2
- data/app/views/shared/_header_navbar.html.erb +0 -6
- data/app/views/spotlight/catalog/_admin_index_header_default.html.erb +0 -23
- data/app/views/spotlight/catalog/_admin_thumbnail_default.html.erb +0 -7
- data/app/views/spotlight/catalog/_curation_mode_toggle_default.html.erb +0 -1
- data/app/views/spotlight/catalog/_document_row.html.erb +0 -2
- data/app/views/spotlight/catalog/_edit_sidecar.html.erb +0 -24
- data/app/views/spotlight/catalog/_index_compact_default.html.erb +0 -21
- data/app/views/spotlight/custom_fields/form_group/_text.html.erb +0 -23
- data/app/views/spotlight/custom_fields/form_group/_vocab.html.erb +0 -23
- data/app/views/spotlight/invitation_mailer/exhibit_invitation_notification.html.erb +0 -12
- data/lib/generators/spotlight/templates/javascript/jquery-shim.js +0 -1
- data/vendor/assets/javascripts/tiny-slider.js +0 -3221
- data/vendor/assets/stylesheets/tiny-slider.css +0 -1
- /data/app/assets/stylesheets/spotlight/{_breadcrumbs.scss → _breadcrumbs.css} +0 -0
- /data/app/assets/stylesheets/spotlight/{_croppable.scss → _croppable.css} +0 -0
- /data/app/assets/stylesheets/spotlight/{_modals.scss → _modals.css} +0 -0
- /data/app/assets/stylesheets/spotlight/{_utilities.scss → _utilities.css} +0 -0
|
@@ -33,8 +33,8 @@ module Spotlight
|
|
|
33
33
|
errors.add :email, 'is not valid' if parsed.nil? || parsed.address != email || parsed.local == email
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def send_devise_notification(notification, *
|
|
37
|
-
notice = notification_mailer.send(notification, self,
|
|
36
|
+
def send_devise_notification(notification, *)
|
|
37
|
+
notice = notification_mailer.send(notification, self, *, exhibit:)
|
|
38
38
|
if notice.respond_to? :deliver_now
|
|
39
39
|
notice.deliver_now
|
|
40
40
|
else
|
|
@@ -59,6 +59,19 @@ module Spotlight
|
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
# The component used to render the field on the upload and edit forms,
|
|
63
|
+
# from the `component` option of the field's entry in Spotlight::Engine.config.custom_field_types
|
|
64
|
+
def component
|
|
65
|
+
component = field_type_config[:component]
|
|
66
|
+
return Spotlight::SidecarFormFieldComponent if component.blank?
|
|
67
|
+
|
|
68
|
+
component.is_a?(String) ? component.constantize : component
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def form_field_type
|
|
72
|
+
field_type_config.fetch(:form_field_type, :text_field)
|
|
73
|
+
end
|
|
74
|
+
|
|
62
75
|
def solr_field
|
|
63
76
|
if field&.starts_with?(solr_field_prefix)
|
|
64
77
|
# backwards compatibility with pre-0.9 custom fields
|
|
@@ -70,6 +83,10 @@ module Spotlight
|
|
|
70
83
|
|
|
71
84
|
protected
|
|
72
85
|
|
|
86
|
+
def field_type_config
|
|
87
|
+
Spotlight::Engine.config.custom_field_types.fetch(field_type.to_s.to_sym, {})
|
|
88
|
+
end
|
|
89
|
+
|
|
73
90
|
def blacklight_configuration
|
|
74
91
|
exhibit&.blacklight_configuration
|
|
75
92
|
end
|
|
@@ -5,6 +5,8 @@ module Spotlight
|
|
|
5
5
|
##
|
|
6
6
|
# Spotlight exhibit
|
|
7
7
|
class Exhibit < ActiveRecord::Base
|
|
8
|
+
PUBLISHING_STATUSES = %w[unpublished previewable discoverable].freeze
|
|
9
|
+
|
|
8
10
|
class_attribute :themes_selector
|
|
9
11
|
include Spotlight::ExhibitAnalytics
|
|
10
12
|
include Spotlight::ExhibitDefaults
|
|
@@ -17,6 +19,8 @@ module Spotlight
|
|
|
17
19
|
|
|
18
20
|
scope :published, -> { where(published: true) }
|
|
19
21
|
scope :unpublished, -> { where(published: false) }
|
|
22
|
+
scope :previewable, -> { published.where(discovery_enabled: false) }
|
|
23
|
+
scope :discoverable, -> { published.where(discovery_enabled: true) }
|
|
20
24
|
scope :ordered_by_weight, -> { order(:weight) }
|
|
21
25
|
|
|
22
26
|
paginates_per 48
|
|
@@ -96,6 +100,30 @@ module Spotlight
|
|
|
96
100
|
searches.published.any?
|
|
97
101
|
end
|
|
98
102
|
|
|
103
|
+
# Published, but only viewable by people with the exhibit link
|
|
104
|
+
def previewable?
|
|
105
|
+
published? && !discovery_enabled?
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Published, and included on the home page and in the sitemap
|
|
109
|
+
def discoverable?
|
|
110
|
+
published? && discovery_enabled?
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def publishing_status
|
|
114
|
+
return 'unpublished' unless published?
|
|
115
|
+
|
|
116
|
+
discovery_enabled? ? 'discoverable' : 'previewable'
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def publishing_status=(status)
|
|
120
|
+
status = status.to_s
|
|
121
|
+
return unless PUBLISHING_STATUSES.include?(status)
|
|
122
|
+
|
|
123
|
+
self.published = status != 'unpublished'
|
|
124
|
+
self.discovery_enabled = status != 'previewable'
|
|
125
|
+
end
|
|
126
|
+
|
|
99
127
|
def themes
|
|
100
128
|
@themes ||= begin
|
|
101
129
|
return Spotlight::Engine.config.exhibit_themes unless self.class.themes_selector
|
|
@@ -38,7 +38,7 @@ module Spotlight
|
|
|
38
38
|
# If the image is local, this step will fail, which is okay since the only
|
|
39
39
|
# consumer is CSV uploads and the URL is intended to be remote
|
|
40
40
|
def remote_image_url=(url)
|
|
41
|
-
super
|
|
41
|
+
super unless url.starts_with? '/'
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def document
|
|
@@ -24,11 +24,16 @@ module Spotlight
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def search_params
|
|
27
|
-
search_service.search_builder.merge(rows: 0, 'facet.limit' => FACET_LIMIT + 1)
|
|
27
|
+
search_service.search_builder.with(search_state).merge(rows: 0, 'facet.limit' => FACET_LIMIT + 1)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
private
|
|
31
31
|
|
|
32
|
+
# Field metadata queries aren't scoped to a user query, so use an empty search state
|
|
33
|
+
def search_state
|
|
34
|
+
Blacklight::SearchState.new({}, blacklight_config)
|
|
35
|
+
end
|
|
36
|
+
|
|
32
37
|
def field_name(key)
|
|
33
38
|
if blacklight_config.facet_fields[key]
|
|
34
39
|
blacklight_config.facet_fields[key].field
|
|
@@ -38,15 +43,15 @@ module Spotlight
|
|
|
38
43
|
end
|
|
39
44
|
|
|
40
45
|
def solr_response
|
|
41
|
-
@solr_response ||= repository.search(search_params.merge('facet.query' => facet_fields.map { |_key, fields| "#{fields.field}:[* TO *]" },
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
@solr_response ||= repository.search(params: search_params.merge('facet.query' => facet_fields.map { |_key, fields| "#{fields.field}:[* TO *]" },
|
|
47
|
+
'rows' => 0,
|
|
48
|
+
'facet' => true))
|
|
44
49
|
end
|
|
45
50
|
|
|
46
51
|
# This gets the number of *documents* with a field
|
|
47
52
|
def document_counts
|
|
48
53
|
@document_count ||= solr_response.facet_queries.each_with_object({}) do |(k, v), h|
|
|
49
|
-
h[k.split(
|
|
54
|
+
h[k.split(':').first] = v
|
|
50
55
|
end
|
|
51
56
|
end
|
|
52
57
|
|
|
@@ -43,12 +43,6 @@ module Spotlight
|
|
|
43
43
|
@job_status ||= ActiveJob::Status.get(job_id)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
def progress_label
|
|
47
|
-
return number_with_delimiter(progress) unless total?
|
|
48
|
-
|
|
49
|
-
"#{number_with_delimiter(progress)} / #{number_with_delimiter(total)}"
|
|
50
|
-
end
|
|
51
|
-
|
|
52
46
|
def progress
|
|
53
47
|
data[:progress] || job_status[:progress] || 0
|
|
54
48
|
end
|
|
@@ -61,12 +55,6 @@ module Spotlight
|
|
|
61
55
|
total(default: 0).positive?
|
|
62
56
|
end
|
|
63
57
|
|
|
64
|
-
def percent
|
|
65
|
-
return nil unless total?
|
|
66
|
-
|
|
67
|
-
(100.0 * progress) / total
|
|
68
|
-
end
|
|
69
|
-
|
|
70
58
|
def status
|
|
71
59
|
@status ||= super
|
|
72
60
|
@status ||= 'missing'
|
|
@@ -104,11 +92,5 @@ module Spotlight
|
|
|
104
92
|
self
|
|
105
93
|
end
|
|
106
94
|
end
|
|
107
|
-
|
|
108
|
-
private
|
|
109
|
-
|
|
110
|
-
def number_with_delimiter(*args)
|
|
111
|
-
ActiveSupport::NumberHelper.number_to_delimited(*args)
|
|
112
|
-
end
|
|
113
95
|
end
|
|
114
96
|
end
|
|
@@ -22,8 +22,8 @@ module Spotlight
|
|
|
22
22
|
label.presence || default_label
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
def default_label(**
|
|
26
|
-
I18n.t(:"spotlight.main_navigation.#{nav_type}", **
|
|
25
|
+
def default_label(**)
|
|
26
|
+
I18n.t(:"spotlight.main_navigation.#{nav_type}", **)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
private
|
|
@@ -74,7 +74,7 @@ module Spotlight
|
|
|
74
74
|
|
|
75
75
|
def content=(content)
|
|
76
76
|
if content.is_a? Array
|
|
77
|
-
super
|
|
77
|
+
super(content.to_json)
|
|
78
78
|
else
|
|
79
79
|
super
|
|
80
80
|
end
|
|
@@ -167,12 +167,12 @@ module Spotlight
|
|
|
167
167
|
end
|
|
168
168
|
end
|
|
169
169
|
|
|
170
|
-
private
|
|
171
|
-
|
|
172
170
|
def default_locale?
|
|
173
171
|
locale.to_sym == I18n.default_locale
|
|
174
172
|
end
|
|
175
173
|
|
|
174
|
+
private
|
|
175
|
+
|
|
176
176
|
def parent_page_for(locale)
|
|
177
177
|
parent_page&.translated_page_for(locale)
|
|
178
178
|
end
|
|
@@ -33,13 +33,16 @@ module Spotlight
|
|
|
33
33
|
|
|
34
34
|
def url_is_iiif?(url)
|
|
35
35
|
valid_content_types = ['application/json', 'application/ld+json']
|
|
36
|
-
req = Spotlight::Resources::IiifService.
|
|
37
|
-
req = Spotlight::Resources::IiifService.
|
|
38
|
-
return unless req.
|
|
36
|
+
req = Spotlight::Resources::IiifService.fetch(:head, url)
|
|
37
|
+
req = Spotlight::Resources::IiifService.fetch(:get, url) if req.code == '405'
|
|
38
|
+
return unless req.is_a?(Net::HTTPSuccess)
|
|
39
39
|
|
|
40
40
|
valid_content_types.any? do |valid_type|
|
|
41
|
-
req
|
|
41
|
+
req['content-type'].to_s.include?(valid_type)
|
|
42
42
|
end
|
|
43
|
+
rescue *Spotlight::Resources::IiifService::FETCH_ERRORS => e
|
|
44
|
+
Rails.logger.warn("Unable to fetch #{url}: #{e.message}")
|
|
45
|
+
false
|
|
43
46
|
end
|
|
44
47
|
end
|
|
45
48
|
end
|
|
@@ -120,13 +120,11 @@ module Spotlight
|
|
|
120
120
|
if value.key? '@value'
|
|
121
121
|
value['@value']
|
|
122
122
|
# IIIF v3 multilingual(?), multivalued(?) values
|
|
123
|
-
# If all of the values are associated with the none key, the client must display all of those values.
|
|
124
|
-
elsif value.keys == ['none']
|
|
125
|
-
value['none']
|
|
126
123
|
# If any of the values have a language associated with them, the client must display all of the values associated with the language
|
|
127
124
|
# that best matches the language preference.
|
|
128
125
|
elsif value.key? default_json_ld_language
|
|
129
126
|
value[default_json_ld_language]
|
|
127
|
+
# If all of the values are associated with the none key, the client must display all of those values.
|
|
130
128
|
# If some of the values have a language associated with them, but none match the language preference, the client must display all
|
|
131
129
|
# of the values that do not have a language associated with them.
|
|
132
130
|
elsif value.key? 'none'
|
|
@@ -2,12 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
require 'iiif/presentation'
|
|
4
4
|
require 'iiif/v3/presentation'
|
|
5
|
+
require 'ssrf_filter'
|
|
5
6
|
module Spotlight
|
|
6
7
|
module Resources
|
|
7
8
|
###
|
|
8
9
|
# Wrapper around IIIF-Presentation's IIIF::Service that provides the
|
|
9
10
|
# ability to recursively traverse through all collections and manifests
|
|
10
11
|
class IiifService
|
|
12
|
+
# Errors from a failed fetch, covering what Faraday::ConnectionFailed used to wrap
|
|
13
|
+
FETCH_ERRORS = [
|
|
14
|
+
SsrfFilter::Error, URI::Error, IOError, SocketError, SystemCallError, Timeout::Error,
|
|
15
|
+
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
|
|
16
|
+
OpenSSL::SSL::SSLError, Zlib::Error
|
|
17
|
+
].freeze
|
|
18
|
+
MAX_REDIRECTS = 3
|
|
19
|
+
|
|
11
20
|
def initialize(url)
|
|
12
21
|
@url = url
|
|
13
22
|
end
|
|
@@ -32,11 +41,17 @@ module Spotlight
|
|
|
32
41
|
recursive_manifests(new(url))
|
|
33
42
|
end
|
|
34
43
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
# SsrfFilter only resolves redirects whose Location starts with "/", so
|
|
45
|
+
# follow them here instead; each hop still goes through SsrfFilter.
|
|
46
|
+
def self.fetch(verb, url, **)
|
|
47
|
+
(MAX_REDIRECTS + 1).times do
|
|
48
|
+
response = SsrfFilter.public_send(verb, url, max_redirects: 0, allow_unfollowed_redirects: true, **)
|
|
49
|
+
return response unless response.is_a?(Net::HTTPRedirection) && response['location']
|
|
50
|
+
|
|
51
|
+
url = URI.join(url, response['location']).to_s
|
|
39
52
|
end
|
|
53
|
+
|
|
54
|
+
raise SsrfFilter::TooManyRedirects, "Got #{MAX_REDIRECTS} redirects fetching #{url}"
|
|
40
55
|
end
|
|
41
56
|
|
|
42
57
|
protected
|
|
@@ -64,8 +79,8 @@ module Spotlight
|
|
|
64
79
|
|
|
65
80
|
class << self
|
|
66
81
|
def iiif_response(url)
|
|
67
|
-
|
|
68
|
-
rescue
|
|
82
|
+
fetch(:get, url, http_options: { response_body_encoding: true }).body
|
|
83
|
+
rescue *FETCH_ERRORS => e
|
|
69
84
|
Rails.logger.warn("HTTP GET for #{url} failed with #{e}")
|
|
70
85
|
{}.to_json
|
|
71
86
|
end
|
|
@@ -23,6 +23,12 @@ module Spotlight
|
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
def self.as_strong_params(exhibit)
|
|
27
|
+
fields(exhibit).map do |field|
|
|
28
|
+
field.is_multiple? ? { field.field_name => [] } : field.field_name
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
26
32
|
def self.indexing_pipeline
|
|
27
33
|
@indexing_pipeline ||= super.dup.tap do |pipeline|
|
|
28
34
|
pipeline.transforms = [
|
|
@@ -52,14 +52,14 @@ module Spotlight
|
|
|
52
52
|
|
|
53
53
|
def documents(&)
|
|
54
54
|
start = 0
|
|
55
|
-
response = repository.search(search_params.start(start))
|
|
55
|
+
response = repository.search(params: search_params.start(start))
|
|
56
56
|
|
|
57
57
|
return to_enum(:documents) { response['response']['numFound'] } unless block_given?
|
|
58
58
|
|
|
59
59
|
while response.documents.present?
|
|
60
60
|
response.documents.each(&)
|
|
61
61
|
start += response.documents.length
|
|
62
|
-
response = repository.search(search_params.start(start))
|
|
62
|
+
response = repository.search(params: search_params.start(start))
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
|
|
@@ -75,20 +75,16 @@ module Spotlight
|
|
|
75
75
|
|
|
76
76
|
delegate :blacklight_config, to: :exhibit
|
|
77
77
|
|
|
78
|
-
def display_masthead?
|
|
79
|
-
masthead&.display?
|
|
80
|
-
end
|
|
81
|
-
|
|
82
78
|
def search_params
|
|
83
|
-
search_service.search_builder.with(
|
|
79
|
+
search_service.search_builder.with(search_state).merge(facet: false)
|
|
84
80
|
end
|
|
85
81
|
|
|
86
82
|
def merge_params_for_search(params, blacklight_config)
|
|
87
83
|
user_query = Blacklight::SearchState.new(params, blacklight_config, nil).to_h
|
|
88
|
-
|
|
84
|
+
search_state.params_for_search(user_query).merge(user_query.slice(:page))
|
|
89
85
|
end
|
|
90
86
|
|
|
91
|
-
def
|
|
87
|
+
def search_state
|
|
92
88
|
Blacklight::SearchState.new((query_params || {}).with_indifferent_access, blacklight_config, nil)
|
|
93
89
|
end
|
|
94
90
|
|
|
@@ -22,6 +22,8 @@ module Spotlight
|
|
|
22
22
|
|
|
23
23
|
delegate :has_key?, :key?, to: :data
|
|
24
24
|
|
|
25
|
+
before_save :strip_blank_values_from_data
|
|
26
|
+
|
|
25
27
|
def to_solr
|
|
26
28
|
{ document.class.unique_key.to_sym => document.id,
|
|
27
29
|
visibility_field => public? }
|
|
@@ -48,6 +50,22 @@ module Spotlight
|
|
|
48
50
|
|
|
49
51
|
private
|
|
50
52
|
|
|
53
|
+
# Remove blank entries (e.g. from an empty "Add another value" input) from multivalued fields
|
|
54
|
+
def strip_blank_values_from_data
|
|
55
|
+
self.data = strip_blank_values(data)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def strip_blank_values(value)
|
|
59
|
+
case value
|
|
60
|
+
when Array
|
|
61
|
+
value.compact_blank
|
|
62
|
+
when Hash
|
|
63
|
+
value.transform_values { |v| strip_blank_values(v) }
|
|
64
|
+
else
|
|
65
|
+
value
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
51
69
|
def document_type_class
|
|
52
70
|
return default_document_type unless document_type.is_a?(String)
|
|
53
71
|
|
|
@@ -74,7 +74,7 @@ module Spotlight
|
|
|
74
74
|
until next_cursor_mark == cursor_mark || next_cursor_mark.nil?
|
|
75
75
|
cursor_mark = next_cursor_mark
|
|
76
76
|
response = exhibit.blacklight_config.repository.search(
|
|
77
|
-
solr_params.merge(
|
|
77
|
+
params: solr_params.merge(
|
|
78
78
|
'q' => '*',
|
|
79
79
|
'rows' => Spotlight::Engine.config.bulk_actions_batch_size,
|
|
80
80
|
'cursorMark' => cursor_mark,
|
|
@@ -58,8 +58,8 @@ module Spotlight
|
|
|
58
58
|
# @param [Hash] data the initial data structure to pass through to the transform steps
|
|
59
59
|
# @yield (optioanlly..) each transformed document after it is transformed but before
|
|
60
60
|
# it is sent to the loaders
|
|
61
|
-
def call(context, data: {}, cache: nil, &
|
|
62
|
-
executor(context, cache:).call(data:, &
|
|
61
|
+
def call(context, data: {}, cache: nil, &)
|
|
62
|
+
executor(context, cache:).call(data:, &)
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
##
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
<% end %>
|
|
21
21
|
|
|
22
22
|
<div class="form-group mb-3 mt-4">
|
|
23
|
-
<%= label_tag
|
|
24
|
-
<%= render Spotlight::TagSelectorComponent.new(field_name: :tags, all_tags: current_exhibit.owned_tags) %>
|
|
23
|
+
<%= label_tag 'remove_tags', t(:'spotlight.bulk_actions.remove_tags.label') %>
|
|
24
|
+
<%= render Spotlight::TagSelectorComponent.new(field_name: :tags, id: 'remove_tags', all_tags: current_exhibit.owned_tags) %>
|
|
25
25
|
</div>
|
|
26
26
|
<%= render Blacklight::HiddenSearchStateComponent.new(params: search_state.params_for_search) %>
|
|
27
27
|
</div>
|
|
@@ -15,8 +15,6 @@
|
|
|
15
15
|
<% end %>
|
|
16
16
|
<%= javascript_tag "window.sirTrevorIcon = '#{asset_path('spotlight/blocks/sir-trevor-icons.svg')}'" %>
|
|
17
17
|
|
|
18
|
-
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
19
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.serializeJSON/3.2.1/jquery.serializejson.min.js"></script>
|
|
20
18
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
|
21
19
|
<%= javascript_tag 'L.Icon.Default.prototype.options.imagePath = "https://unpkg.com/leaflet@1.9.4/dist/images/"' %>
|
|
22
20
|
<%= javascript_include_tag 'leaflet-iiif', "data-turbo-track": "reload", defer: true %>
|
|
@@ -25,7 +23,7 @@
|
|
|
25
23
|
<% if defined? Importmap %>
|
|
26
24
|
<%= javascript_importmap_tags %>
|
|
27
25
|
<% else %>
|
|
28
|
-
<%= javascript_include_tag "application", "data-turbo-track": "reload",
|
|
26
|
+
<%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
|
|
29
27
|
<% end %>
|
|
30
28
|
<%= csrf_meta_tags %>
|
|
31
29
|
<%= content_for(:head) %>
|
|
@@ -36,15 +34,14 @@
|
|
|
36
34
|
<% add_exhibit_meta_content %>
|
|
37
35
|
<% end %>
|
|
38
36
|
<%= yield(:meta) %>
|
|
39
|
-
<%= javascript_tag "window.addEventListener('load', () => $.fx.off = true)" if Rails.env.test? %>
|
|
40
37
|
</head>
|
|
41
38
|
<body class="<%= render_body_class %>">
|
|
42
39
|
<%= render partial: 'shared/body_preamble' %>
|
|
43
40
|
<%= render blacklight_config.skip_link_component.new(render_search_link: should_render_spotlight_search_bar?) do %>
|
|
44
41
|
<%= content_for(:skip_links) %>
|
|
45
42
|
<% end %>
|
|
46
|
-
<%= render
|
|
47
|
-
<%= render
|
|
43
|
+
<%= render blacklight_config.header_component.new(blacklight_config:) %>
|
|
44
|
+
<%= render Spotlight::MastheadComponent.new(current_exhibit:, current_masthead:, resource_masthead: resource_masthead?) %>
|
|
48
45
|
<%= content_for?(:header_content) ? yield(:header_content) : "" %>
|
|
49
46
|
|
|
50
47
|
<main id="main-container" class="<%= container_classes %>" aria-label="<%= t('blacklight.main.aria.main_container') %>">
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<% content_for(:content) do %>
|
|
2
2
|
<% if content? :sidebar %>
|
|
3
|
-
<aside id="sidebar" class="<%= sidebar_classes %> <%= content_for(:sidebar_position) %>"
|
|
3
|
+
<aside id="sidebar" class="<%= sidebar_classes %> <%= content_for(:sidebar_position) %>">
|
|
4
4
|
<%= content_for(:sidebar) %>
|
|
5
5
|
</aside>
|
|
6
6
|
|
|
7
|
-
<section id="content" class="<%= main_content_classes %> <%= content_for(:content_position) %>"
|
|
7
|
+
<section id="content" class="<%= main_content_classes %> <%= content_for(:content_position) %>">
|
|
8
8
|
<%= yield %>
|
|
9
9
|
</section>
|
|
10
10
|
<% else %>
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
<%
|
|
1
|
+
<% ga_id = Spotlight::Engine.config.ga_measurement_id.presence || Spotlight::Engine.config.ga_web_property_id %>
|
|
2
|
+
<% if ga_id.present? %>
|
|
2
3
|
<!-- Google tag (gtag.js) -->
|
|
3
4
|
<% if Spotlight::Engine.config.ga_debug_mode %>
|
|
4
5
|
<script>window.analyticsDebug = true;</script>
|
|
5
6
|
<% end %>
|
|
6
|
-
<script async src="https://www.googletagmanager.com/gtag/js?id=<%=
|
|
7
|
+
<script async src="https://www.googletagmanager.com/gtag/js?id=<%= ga_id %>"></script>
|
|
7
8
|
<script>
|
|
8
9
|
window.dataLayer = window.dataLayer || [];
|
|
9
10
|
function gtag() { dataLayer.push(arguments); }
|
|
10
11
|
var opts = window.analyticsDebug ? {'debug_mode': true} : {};
|
|
11
12
|
gtag("js", new Date());
|
|
12
|
-
gtag("config", "<%=
|
|
13
|
+
gtag("config", "<%= ga_id %>", opts);
|
|
13
14
|
</script>
|
|
14
15
|
<% end %>
|
|
@@ -1,18 +1,2 @@
|
|
|
1
|
-
<%
|
|
2
|
-
|
|
3
|
-
<%= render 'spotlight/shared/report_a_problem' %>
|
|
4
|
-
</div>
|
|
5
|
-
<% end %>
|
|
6
|
-
|
|
7
|
-
<header class="masthead <%= 'image-masthead' if current_masthead %> <%= 'resource-masthead' if resource_masthead? %>">
|
|
8
|
-
<% if current_masthead %>
|
|
9
|
-
<span class="background-container" style="background-image: url('<%= current_masthead.iiif_url %>')"></span>
|
|
10
|
-
<span class="background-container-gradient"></span>
|
|
11
|
-
<% end %>
|
|
12
|
-
|
|
13
|
-
<%= render current_exhibit ? 'shared/exhibit_navbar' : 'shared/site_navbar' if resource_masthead? %>
|
|
14
|
-
<%= render Spotlight::Engine.config.spotlight.title_component.new(title: content_for(:masthead) || masthead_heading_content, subtitle: masthead_subheading_content) %>
|
|
15
|
-
<%= render current_exhibit ? 'shared/exhibit_navbar' : 'shared/site_navbar' unless resource_masthead? %>
|
|
16
|
-
</header>
|
|
17
|
-
|
|
18
|
-
<%= render Spotlight::BreadcrumbsComponent.new(breadcrumbs:) %>
|
|
1
|
+
<% Spotlight.deprecator.warn "Rendering deprecated partial shared/masthead. Use MastheadComponent instead" %>
|
|
2
|
+
<%= render Spotlight::MastheadComponent.new(current_exhibit:, current_masthead:, resource_masthead: resource_masthead?) %>
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
Use the checkbox in the <b>Options</b> tab to publish or unpublish the page.
|
|
20
20
|
</li>
|
|
21
21
|
<li>
|
|
22
|
-
Visit the Spotlight <%= link_to 'documentation pages', 'https://github.com/projectblacklight/spotlight/wiki' %> to learn more about creating exhibit pages.
|
|
22
|
+
Visit the Spotlight <%= link_to 'documentation pages', 'https://github.com/projectblacklight/spotlight/wiki', class: "alert-link" %> to learn more about creating exhibit pages.
|
|
23
23
|
</li>
|
|
24
24
|
</ol>
|
|
25
25
|
</div>
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
<% cache_unless current_user, [current_exhibit, @page, I18n.locale] do %>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
<%=
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
<nav aria-labelledby="about-pages-h">
|
|
3
|
+
<h2 id="about-pages-h" class="nav-heading h4"><%= current_exhibit.main_navigations.about.label_or_default %></h2>
|
|
4
|
+
<ul class="nav sidenav flex-column">
|
|
5
|
+
<% current_exhibit.about_pages.for_locale.published.each do |page| %>
|
|
6
|
+
<li class="nav-item <%= 'active' if current_page? [spotlight, current_exhibit, page] %>">
|
|
7
|
+
<%= link_to page.title, [spotlight, current_exhibit, page], class: 'nav-link' %>
|
|
8
|
+
</li>
|
|
9
|
+
<% end %>
|
|
10
|
+
</ul>
|
|
11
|
+
</nav>
|
|
10
12
|
<%= cache [current_exhibit, I18n.locale] do %>
|
|
11
13
|
<%= render 'contacts' if current_exhibit.contacts.published.present? %>
|
|
12
14
|
<% end %>
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
<div class="image-overlay">
|
|
6
6
|
<%= image_tag(search.thumbnail_image_url || 'spotlight/default_browse_thumbnail.jpg', class: 'img-responsive', alt: '') %>
|
|
7
7
|
<div class="text-overlay">
|
|
8
|
-
<
|
|
8
|
+
<h3 class="browse-category-title">
|
|
9
9
|
<span class="title"><%= search.title %></span>
|
|
10
10
|
<% if search.subtitle.present? %><span class="category-subtitle"><%= search.subtitle %></span><% end %>
|
|
11
11
|
<small class="item-count"><%= t :'spotlight.browse.search.item_count', count: search.count %></small>
|
|
12
|
-
</
|
|
12
|
+
</h3>
|
|
13
13
|
</div>
|
|
14
14
|
</div>
|
|
15
15
|
<% end %>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<div class="search-box-container">
|
|
2
2
|
<%= form_tag exhibit_browse_path(current_exhibit, search), method: :get, class: 'browse-search-form search-query-form form-horizontal', role: 'search', 'aria-labelledby' => 'browse-search-form' do %>
|
|
3
3
|
<%= render Blacklight::HiddenSearchStateComponent.new(params: search_state.params_for_search.except(:q, :search_field, :exhibit_id, :qt, :page)) %>
|
|
4
|
-
<div class="form-group mb-
|
|
4
|
+
<div class="form-group mb-3 row">
|
|
5
5
|
<label id="browse-search-form" class="col-sm-4 col-form-label h6" for="browse_q"><%= t(:'.label') %></label>
|
|
6
6
|
<div class="col-sm-6">
|
|
7
7
|
<div class="input-group">
|
|
@@ -18,22 +18,14 @@
|
|
|
18
18
|
</div>
|
|
19
19
|
</div>
|
|
20
20
|
<% end %>
|
|
21
|
-
|
|
22
|
-
<
|
|
21
|
+
<div class="text-center">
|
|
22
|
+
<p><%= link_to(t(:'.view_search'), path_to_saved_search(search)) %></p>
|
|
23
|
+
<% if params[:q].present? %>
|
|
23
24
|
<% if @response.size > 0 %>
|
|
24
25
|
<p><%= t(:'.success.result_number_html', search_size: @response.size, parent_search_count: parent_search_count) %></p>
|
|
25
|
-
<p><%= t(:'.success.expand_html',
|
|
26
|
-
expand_search_url: search_exhibit_catalog_path(current_exhibit, q: params[:q]),
|
|
27
|
-
browse_query: params[:q]) %>
|
|
28
|
-
</p>
|
|
29
26
|
<% else %>
|
|
30
27
|
<p><%= t(:'.zero_results.result_number') %></p>
|
|
31
|
-
<p><%= t(:'.zero_results.expand_html',
|
|
32
|
-
clear_search_url: exhibit_browse_path(current_exhibit, search),
|
|
33
|
-
expand_search_url: search_exhibit_catalog_path(current_exhibit, q: params[:q]),
|
|
34
|
-
browse_query: params[:q]) %>
|
|
35
|
-
</p>
|
|
36
28
|
<% end %>
|
|
37
|
-
|
|
38
|
-
|
|
29
|
+
<% end %>
|
|
30
|
+
</div>
|
|
39
31
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<% title = current_exhibit.main_navigations.browse.label_or_default %>
|
|
2
2
|
<% set_html_page_title(title) %>
|
|
3
|
-
<
|
|
3
|
+
<h2 class="visually-hidden"><%= title %></h2>
|
|
4
4
|
|
|
5
5
|
<% if @groups.any? # active %>
|
|
6
6
|
<ul class="browse-group-navigation nav nav-pills justify-content-center">
|