blacklight-spotlight 5.0.1 → 5.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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +153 -48
  3. data/app/assets/javascripts/spotlight/spotlight.esm.js +33 -1
  4. data/app/assets/javascripts/spotlight/spotlight.esm.js.map +1 -1
  5. data/app/assets/javascripts/spotlight/spotlight.js +33 -1
  6. data/app/assets/javascripts/spotlight/spotlight.js.map +1 -1
  7. data/app/assets/stylesheets/spotlight/_admin_users.scss +28 -0
  8. data/app/assets/stylesheets/spotlight/_browse.scss +1 -1
  9. data/app/assets/stylesheets/spotlight/_featured_browse_categories_block.scss +1 -1
  10. data/app/assets/stylesheets/spotlight/_spotlight.scss +1 -0
  11. data/app/components/spotlight/admin_users/email_component.html.erb +5 -0
  12. data/app/components/spotlight/admin_users/email_component.rb +22 -0
  13. data/app/components/spotlight/admin_users/exhibit_roles_component.html.erb +28 -0
  14. data/app/components/spotlight/admin_users/exhibit_roles_component.rb +19 -0
  15. data/app/components/spotlight/admin_users/site_admin_component.html.erb +13 -0
  16. data/app/components/spotlight/admin_users/site_admin_component.rb +17 -0
  17. data/app/components/spotlight/analytics/aggregation_component.rb +1 -1
  18. data/app/components/spotlight/analytics/dashboard_component.rb +1 -1
  19. data/app/components/spotlight/breadcrumbs_component.rb +1 -1
  20. data/app/components/spotlight/bulk_action_component.rb +1 -1
  21. data/app/components/spotlight/edit_view_links_component.rb +1 -1
  22. data/app/components/spotlight/header_navigation_link_component.rb +1 -1
  23. data/app/components/spotlight/save_search_component.rb +1 -1
  24. data/app/components/spotlight/select_image_component.rb +1 -1
  25. data/app/components/spotlight/skip_link_component.rb +1 -1
  26. data/app/components/spotlight/solr_document_legacy_embed_component.rb +2 -2
  27. data/app/components/spotlight/tag_list_form_component.rb +1 -1
  28. data/app/components/spotlight/tag_selector_component.rb +1 -1
  29. data/app/components/spotlight/title_component.rb +1 -1
  30. data/app/components/spotlight/translations/subheading_component.rb +1 -1
  31. data/app/components/spotlight/uneditable_non_default_language_component.html.erb +5 -0
  32. data/app/components/spotlight/uneditable_non_default_language_component.rb +25 -0
  33. data/app/controllers/spotlight/admin_users_controller.rb +11 -1
  34. data/app/controllers/spotlight/browse_controller.rb +2 -8
  35. data/app/controllers/spotlight/bulk_actions_controller.rb +1 -1
  36. data/app/controllers/spotlight/bulk_updates_controller.rb +22 -7
  37. data/app/controllers/spotlight/catalog_controller.rb +8 -16
  38. data/app/controllers/spotlight/dashboards_controller.rb +2 -6
  39. data/app/controllers/spotlight/exhibits_controller.rb +1 -0
  40. data/app/controllers/spotlight/home_pages_controller.rb +1 -1
  41. data/app/controllers/spotlight/searches_controller.rb +1 -1
  42. data/app/controllers/spotlight/solr_controller.rb +1 -0
  43. data/app/helpers/spotlight/main_app_helpers.rb +1 -5
  44. data/app/helpers/spotlight/rendering_helper.rb +4 -1
  45. data/app/javascript/spotlight/admin/blocks/pages_block.js +2 -0
  46. data/app/javascript/spotlight/user/carousel.js +32 -2
  47. data/app/jobs/spotlight/add_tags_job.rb +1 -0
  48. data/app/jobs/spotlight/add_uploads_from_csv.rb +1 -0
  49. data/app/jobs/spotlight/change_visibility_job.rb +1 -0
  50. data/app/jobs/spotlight/process_bulk_updates_csv_job.rb +1 -0
  51. data/app/jobs/spotlight/reindex_exhibit_job.rb +1 -0
  52. data/app/jobs/spotlight/reindex_job.rb +1 -0
  53. data/app/jobs/spotlight/remove_tags_job.rb +1 -0
  54. data/app/jobs/spotlight/rename_sidecar_field_job.rb +1 -0
  55. data/app/models/concerns/spotlight/user.rb +5 -0
  56. data/app/models/sir_trevor_rails/blocks/browse_block.rb +1 -1
  57. data/app/models/sir_trevor_rails/blocks/featured_pages_block.rb +1 -1
  58. data/app/models/sir_trevor_rails/blocks/solr_documents_block.rb +1 -0
  59. data/app/models/spotlight/ability.rb +1 -1
  60. data/app/models/spotlight/about_page.rb +1 -0
  61. data/app/models/spotlight/blacklight_configuration.rb +6 -7
  62. data/app/models/spotlight/contact.rb +2 -1
  63. data/app/models/spotlight/contact_email.rb +1 -0
  64. data/app/models/spotlight/custom_field.rb +1 -0
  65. data/app/models/spotlight/exhibit.rb +2 -9
  66. data/app/models/spotlight/feature_page.rb +1 -0
  67. data/app/models/spotlight/group.rb +2 -1
  68. data/app/models/spotlight/home_page.rb +1 -0
  69. data/app/models/spotlight/job_tracker.rb +1 -1
  70. data/app/models/spotlight/main_navigation.rb +1 -1
  71. data/app/models/spotlight/page.rb +35 -19
  72. data/app/models/spotlight/resource.rb +1 -0
  73. data/app/models/spotlight/resources/iiif_manifest.rb +0 -126
  74. data/app/models/spotlight/resources/iiif_manifest_metadata.rb +161 -0
  75. data/app/models/spotlight/resources/iiif_manifest_v3.rb +41 -0
  76. data/app/models/spotlight/resources/iiif_service.rb +25 -1
  77. data/app/models/spotlight/search.rb +2 -1
  78. data/app/services/spotlight/exhibit_import_export_service.rb +3 -1
  79. data/app/views/spotlight/accessibility/alt_text.html.erb +3 -0
  80. data/app/views/spotlight/admin_users/index.html.erb +16 -26
  81. data/app/views/spotlight/appearances/edit.html.erb +14 -9
  82. data/app/views/spotlight/catalog/_document_admin_table.html.erb +2 -6
  83. data/app/views/spotlight/catalog/edit.html.erb +1 -6
  84. data/app/views/spotlight/exhibits/_exhibit_card.html.erb +1 -1
  85. data/app/views/spotlight/exhibits/_form.html.erb +2 -2
  86. data/app/views/spotlight/metadata_configurations/edit.html.erb +47 -44
  87. data/app/views/spotlight/pages/_order_pages.html.erb +34 -29
  88. data/app/views/spotlight/pages/show.html.erb +4 -2
  89. data/app/views/spotlight/resources/csv_upload/_form.html.erb +2 -0
  90. data/app/views/spotlight/search_configurations/edit.html.erb +40 -34
  91. data/app/views/spotlight/searches/index.html.erb +4 -2
  92. data/app/views/spotlight/sir_trevor/blocks/_embedded_document.html.erb +2 -2
  93. data/app/views/spotlight/sir_trevor/blocks/_search_results_block.html.erb +2 -2
  94. data/app/views/spotlight/sir_trevor/blocks/_solr_documents_carousel_block.html.erb +5 -3
  95. data/app/views/spotlight/sir_trevor/blocks/_text_block.html.erb +1 -1
  96. data/app/views/spotlight/translations/_page.html.erb +1 -1
  97. data/config/initializers/devise_rails8_patch.rb +13 -0
  98. data/config/locales/spotlight.en.yml +39 -5
  99. data/config/routes.rb +5 -1
  100. data/lib/spotlight/engine.rb +10 -26
  101. data/lib/spotlight/version.rb +1 -1
  102. data/spec/fixtures/iiif_responses.rb +344 -0
  103. data/spec/support/features/test_features_helpers.rb +4 -6
  104. data/spec/support/stub_iiif_response.rb +1 -0
  105. data/spec/support/with_queue_adapter.rb +17 -0
  106. metadata +44 -31
  107. data/app/components/spotlight/blocks/heading_block_component.erb +0 -2
  108. data/app/components/spotlight/blocks/heading_block_component.rb +0 -36
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotlight
4
+ module Resources
5
+ ###
6
+ # A simple class to map the metadata field
7
+ # in a IIIF document to label/value pairs
8
+ # This is intended to be overriden by an
9
+ # application if a different metadata
10
+ # strucure is used by the consumer
11
+ # override with Spotlight::Engine.config.iiif_metadata_class
12
+ class IiifManifestMetadata
13
+ def initialize(manifest)
14
+ @manifest = manifest
15
+ end
16
+
17
+ def to_solr
18
+ metadata_hash.merge(manifest_level_metadata)
19
+ end
20
+
21
+ def label
22
+ return unless manifest&.label
23
+
24
+ Array(json_ld_value(manifest.label)).map { |v| html_sanitize(v) }.first
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :manifest
30
+
31
+ def metadata
32
+ manifest&.metadata || []
33
+ end
34
+
35
+ def metadata_hash
36
+ return {} if metadata.blank?
37
+ return {} unless metadata.is_a?(Array)
38
+
39
+ metadata.each_with_object({}) do |md, hash|
40
+ next unless md['label'] && md['value']
41
+
42
+ label = Array(json_ld_value(md['label'])).first
43
+
44
+ hash[label] ||= []
45
+ hash[label] += Array(json_ld_value(md['value'])).map { |v| html_sanitize(v) }
46
+ end
47
+ end
48
+
49
+ def manifest_level_metadata
50
+ return manifest_level_metadata_v2 unless manifest_level_metadata_v2.empty?
51
+
52
+ manifest_level_metadata_v3
53
+ end
54
+
55
+ def manifest_level_metadata_v2
56
+ @manifest_level_metadata_v2 ||=
57
+ manifest2_fields.each_with_object({}) do |field, hash|
58
+ next unless manifest.respond_to?(field) && manifest.send(field).present?
59
+
60
+ hash[field.capitalize] ||= []
61
+ hash[field.capitalize] += Array(json_ld_value(manifest.send(field))).map { |v| html_sanitize(v) }
62
+ end
63
+ end
64
+
65
+ def manifest2_fields
66
+ %w[attribution description license]
67
+ end
68
+
69
+ def manifest_level_metadata_v3
70
+ manifest3_fields.each_with_object({}) do |field, hash|
71
+ manifest_key, solr_key = field
72
+ next unless manifest.respond_to?(manifest_key) && manifest.send(manifest_key).present?
73
+
74
+ hash[solr_key.capitalize] ||= []
75
+ hash[solr_key.capitalize] += Array(json_ld_value(manifest.send(manifest_key))).map { |v| html_sanitize(v) }
76
+ end.merge(attribution_v3)
77
+ end
78
+
79
+ def manifest3_fields
80
+ { rights: 'license', summary: 'description' }
81
+ end
82
+
83
+ def attribution_v3
84
+ rs = manifest['required_statement']
85
+ return {} if rs.blank?
86
+
87
+ key = json_ld_value(rs['label']).first
88
+ val = json_ld_value(rs['value'])
89
+ { key => val }
90
+ end
91
+
92
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
93
+ def json_ld_value(value)
94
+ case value
95
+ # In the case where multiple values are supplied, clients must use the following algorithm to determine which values to display to the user.
96
+ when Array
97
+ # IIIF v2, multivalued monolingual, or multivalued multilingual values
98
+
99
+ # If none of the values have a language associated with them, the client must display all of the values.
100
+ if value.none? { |v| v.is_a?(Hash) && v.key?('@language') }
101
+ value.map { |v| json_ld_value(v) }
102
+ # If any of the values have a language associated with them, the client must display all of the values associated with the language that best
103
+ # matches the language preference.
104
+ elsif value.any? { |v| v.is_a?(Hash) && v['@language'] == default_json_ld_language }
105
+ value.select { |v| v.is_a?(Hash) && v['@language'] == default_json_ld_language }.pluck('@value')
106
+ # If all of the values have a language associated with them, and none match the language preference, the client must select a language
107
+ # and display all of the values associated with that language.
108
+ elsif value.all? { |v| v.is_a?(Hash) && v.key?('@language') }
109
+ selected_json_ld_language = value.find { |v| v.is_a?(Hash) && v.key?('@language') }
110
+
111
+ value.select { |v| v.is_a?(Hash) && v['@language'] == selected_json_ld_language['@language'] }
112
+ .pluck('@value')
113
+ # If some of the values have a language associated with them, but none match the language preference, the client must display all of the values
114
+ # that do not have a language associated with them.
115
+ else
116
+ value.select { |v| !v.is_a?(Hash) || !v.key?('@language') }.map { |v| json_ld_value(v) }
117
+ end
118
+ when Hash
119
+ # IIIF v2 single-valued value
120
+ if value.key? '@value'
121
+ value['@value']
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
+ # If any of the values have a language associated with them, the client must display all of the values associated with the language
127
+ # that best matches the language preference.
128
+ elsif value.key? default_json_ld_language
129
+ value[default_json_ld_language]
130
+ # If some of the values have a language associated with them, but none match the language preference, the client must display all
131
+ # of the values that do not have a language associated with them.
132
+ elsif value.key? 'none'
133
+ value['none']
134
+ # If all of the values have a language associated with them, and none match the language preference, the client must select a
135
+ # language and display all of the values associated with that language.
136
+ else
137
+ value.values.first
138
+ end
139
+ else
140
+ # plain old string/number/boolean
141
+ value
142
+ end
143
+ end
144
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
145
+
146
+ def html_sanitize(value)
147
+ return value unless value.is_a? String
148
+
149
+ html_sanitizer.sanitize(value)
150
+ end
151
+
152
+ def html_sanitizer
153
+ @html_sanitizer ||= Rails::Html::FullSanitizer.new
154
+ end
155
+
156
+ def default_json_ld_language
157
+ Spotlight::Engine.config.default_json_ld_language
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotlight
4
+ module Resources
5
+ # A PORO to construct a solr hash for a given v3 IiifManifest
6
+ class IiifManifestV3 < Spotlight::Resources::IiifManifest
7
+ private
8
+
9
+ def add_thumbnail_url
10
+ return unless thumbnail_field && manifest['thumbnail'].present?
11
+
12
+ solr_hash[thumbnail_field] = manifest.thumbnail.map(&:id)
13
+ end
14
+
15
+ def image_urls
16
+ resources.map do |resource|
17
+ image_url = (resource['id'] || resource['@id']).dup # break reference, otherwise it changes values of other fields
18
+ image_url << '/info.json' unless image_url.downcase.ends_with?('/info.json')
19
+ image_url
20
+ end
21
+ end
22
+
23
+ def full_image_url
24
+ resources.first.try(:[], 'id') || resources.first.try(:[], '@id')
25
+ end
26
+
27
+ def resources
28
+ @resources ||=
29
+ canvases
30
+ .flat_map(&:items).select { |item| item.type == 'AnnotationPage' }
31
+ .flat_map(&:items).select { |item| item.motivation == 'painting' }
32
+ .flat_map(&:body)
33
+ .flat_map(&:service)
34
+ end
35
+
36
+ def canvases
37
+ manifest.try(:items).select { |canvas| canvas.type == 'Canvas' }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'iiif/presentation'
4
+ require 'iiif/v3/presentation'
4
5
  module Spotlight
5
6
  module Resources
6
7
  ###
@@ -20,6 +21,8 @@ module Spotlight
20
21
  def manifests
21
22
  @manifests ||= if manifest?
22
23
  [create_iiif_manifest(object)]
24
+ elsif v3_manifest?
25
+ [create_iiif_v3_manifest(object)]
23
26
  else
24
27
  build_collection_manifest.to_a
25
28
  end
@@ -39,13 +42,26 @@ module Spotlight
39
42
  protected
40
43
 
41
44
  def object
42
- @object ||= IIIF::Service.parse(response)
45
+ # If it's a v3 manifest, the v2 library will parse it as an OrderedHash
46
+ @object ||= parse_v2? ? manifest_v2 : manifest_v3
43
47
  end
44
48
 
45
49
  private
46
50
 
47
51
  attr_reader :url
48
52
 
53
+ def parse_v2?
54
+ manifest_v2.is_a?(IIIF::Presentation::Manifest) || manifest_v2.is_a?(IIIF::Presentation::Collection)
55
+ end
56
+
57
+ def manifest_v2
58
+ @manifest_v2 ||= IIIF::Presentation::Service.parse(response)
59
+ end
60
+
61
+ def manifest_v3
62
+ IIIF::V3::Presentation::Service.parse(response)
63
+ end
64
+
49
65
  class << self
50
66
  def iiif_response(url)
51
67
  Spotlight::Resources::IiifService.http_client.get(url).body
@@ -73,10 +89,18 @@ module Spotlight
73
89
  IiifManifest.new(url: manifest['@id'], manifest:, collection:)
74
90
  end
75
91
 
92
+ def create_iiif_v3_manifest(manifest, collection = nil)
93
+ IiifManifestV3.new(url: manifest['id'], manifest:, collection:)
94
+ end
95
+
76
96
  def manifest?
77
97
  object.is_a?(IIIF::Presentation::Manifest)
78
98
  end
79
99
 
100
+ def v3_manifest?
101
+ object.is_a?(IIIF::V3::Presentation::Manifest)
102
+ end
103
+
80
104
  def collection?
81
105
  object.is_a?(IIIF::Presentation::Collection)
82
106
  end
@@ -8,6 +8,7 @@ module Spotlight
8
8
  include Spotlight::SearchHelper
9
9
 
10
10
  extend FriendlyId
11
+
11
12
  friendly_id :title, use: %i[slugged scoped finders history], scope: :exhibit
12
13
 
13
14
  self.table_name = 'spotlight_searches'
@@ -25,7 +26,7 @@ module Spotlight
25
26
  serialize :query_params, Hash
26
27
  end
27
28
  end
28
- default_scope { order('weight ASC') }
29
+ default_scope { order(:weight) }
29
30
  scope :published, -> { where(published: true) }
30
31
  scope :unpublished, -> { where(published: [nil, false]) }
31
32
  validates :title, presence: true
@@ -164,12 +164,13 @@ module Spotlight
164
164
 
165
165
  hash[:custom_fields].each do |attr|
166
166
  ar = exhibit.custom_fields.find_or_initialize_by(slug: attr[:slug])
167
- attr[:configuration] = attr[:configuration].clone.transform_keys(&:to_s)
167
+ attr[:configuration] = attr[:configuration].clone.deep_transform_keys(&:to_s) if attr[:configuration]
168
168
  ar.update(attr)
169
169
  end
170
170
 
171
171
  hash[:solr_document_sidecars].each do |attr|
172
172
  ar = exhibit.solr_document_sidecars.find_or_initialize_by(document_id: attr[:document_id])
173
+ attr[:data] = attr[:data].clone.deep_transform_keys(&:to_s) if attr[:data]
173
174
  ar.update(attr)
174
175
  end
175
176
 
@@ -177,6 +178,7 @@ module Spotlight
177
178
  upload = attr.delete(:upload)
178
179
 
179
180
  ar = exhibit.resources.find_or_initialize_by(type: attr[:type], url: attr[:url])
181
+ attr[:data] = attr[:data].clone.deep_transform_keys(&:to_s) if attr[:data]
180
182
  ar.update(attr)
181
183
 
182
184
  deserialize_featured_image(ar, :upload, upload) if upload
@@ -43,6 +43,9 @@
43
43
  </svg>
44
44
  <% end %>
45
45
  </span>
46
+ <% unless page.published? %>
47
+ <div class="badge bg-info unpublished align-baseline p-1"><%= t('.unpublished') %></div>
48
+ <% end %>
46
49
  </h4>
47
50
  <%= render Spotlight::EditViewLinksComponent.new(page:, classes:'page-links pt-0') %>
48
51
  </td>
@@ -5,20 +5,18 @@
5
5
  <table class="table table-striped">
6
6
  <thead>
7
7
  <tr>
8
- <th colspan="2"><%= Spotlight::Engine.user_class.human_attribute_name(:email) %></th>
8
+ <th scope="col"><%= Spotlight::Engine.user_class.human_attribute_name(:email) %></th>
9
+ <th scope="col"><%= t('.site_admin') %></th>
9
10
  </tr>
10
11
  </thead>
11
- <tbody class="table">
12
+ <tbody>
12
13
  <% @site.roles.map(&:user).each do |user| %>
13
14
  <tr>
14
- <td class="<%= 'invite-pending' if user.invite_pending? %>">
15
- <%= user.email %>
16
- <span class='badge bg-warning pending-label'><%= t('.pending') %></span>
15
+ <td>
16
+ <%= render Spotlight::AdminUsers::EmailComponent.new(user:) %>
17
17
  </td>
18
18
  <td>
19
- <%= link_to(t('.destroy'), admin_user_path(user),
20
- data: { method: :delete, turbo_method: :delete },
21
- class: 'btn btn-sm btn-danger float-end') unless user == current_user %>
19
+ <%= render Spotlight::AdminUsers::SiteAdminComponent.new(user:) %>
22
20
  </td>
23
21
  </tr>
24
22
  <% end %>
@@ -67,30 +65,22 @@
67
65
  <table class="table table-striped ">
68
66
  <thead>
69
67
  <tr>
70
- <th><%= Spotlight::Engine.user_class.human_attribute_name(:email) %></th>
71
- <th><%= Spotlight::Engine.user_class.human_attribute_name(:role) %></th>
68
+ <th scope="col"><%= Spotlight::Engine.user_class.human_attribute_name(:email) %></th>
69
+ <th scope="col"><%= t('.site_admin') %></th>
70
+ <th scope="col"><%= t('.exhibit_roles') %></th>
72
71
  </tr>
73
72
  </thead>
74
- <tbody class="table">
73
+ <tbody>
75
74
  <% @users.each do |user| %>
76
75
  <tr>
77
- <td class="<%= 'invite-pending' if user.invite_pending? %>">
78
- <%= user.email %>
79
- <span class='badge bg-warning pending-label'><%= t('.pending') %></span>
76
+ <td class="user-emails">
77
+ <%= render Spotlight::AdminUsers::EmailComponent.new(user:) %>
80
78
  </td>
81
- <td class="role">
82
- <%= user.roles.map { |r| r.role.titleize }.uniq.join(", ") %>
79
+ <td>
80
+ <%= render Spotlight::AdminUsers::SiteAdminComponent.new(user:) %>
83
81
  </td>
84
- <td class="text-end">
85
- <% if user.superadmin? %>
86
- <%= link_to(t('.destroy'), admin_user_path(user),
87
- data: { method: :delete, turbo_method: :delete },
88
- class: 'btn btn-sm btn-danger') unless user == current_user %>
89
- <% else %>
90
- <%= link_to(t('.update'), admin_user_path(user),
91
- data: { method: :patch, turbo_method: :patch },
92
- class: 'btn btn-sm btn-primary') %>
93
- <% end %>
82
+ <td class="user-exhibit-roles">
83
+ <%= render Spotlight::AdminUsers::ExhibitRolesComponent.new(user:) %>
94
84
  </td>
95
85
  </tr>
96
86
  <% end %>
@@ -66,15 +66,20 @@
66
66
  </div>
67
67
 
68
68
  <div role="tabpanel" class="tab-pane" id="main-menu">
69
- <%= field_set_tag do %>
70
- <p class="instructions"><%= t(:'.main_navigation.help') %></p>
71
- <div class="card-group dd main_navigation_admin col-sm-7" id="nested-navigation" data-behavior="nestable" data-max-depth="1">
72
- <ol class="dd-list">
73
- <%= f.fields_for :main_navigations do |label| %>
74
- <%= render layout: 'spotlight/shared/dd3_item', locals: { id: label.object.nav_type, field: label, label: label.object.label_or_default, default_value: label.object.default_label, index: label.index, enabled_method: :display } do; end %>
75
- <% end %>
76
- </ol>
77
- </div>
69
+ <%# These fields are translatable and should only be edited here in the default locale %>
70
+ <% if default_language? %>
71
+ <%= field_set_tag do %>
72
+ <p class="instructions"><%= t(:'.main_navigation.help') %></p>
73
+ <div class="card-group dd main_navigation_admin col-sm-7" id="nested-navigation" data-behavior="nestable" data-max-depth="1">
74
+ <ol class="dd-list">
75
+ <%= f.fields_for :main_navigations do |label| %>
76
+ <%= render layout: 'spotlight/shared/dd3_item', locals: { id: label.object.nav_type, field: label, label: label.object.label_or_default, default_value: label.object.default_label, index: label.index, enabled_method: :display } do; end %>
77
+ <% end %>
78
+ </ol>
79
+ </div>
80
+ <% end %>
81
+ <% else %>
82
+ <%= render Spotlight::UneditableNonDefaultLanguageComponent.new(current_exhibit:, current_language: I18n.locale)%>
78
83
  <% end %>
79
84
  </div>
80
85
  </div>
@@ -10,10 +10,6 @@
10
10
  </tr>
11
11
  </thead>
12
12
 
13
- <% if Blacklight.version < '8.0' %>
14
- <%= render (view_config.document_component || Spotlight::DocumentAdminTableComponent).with_collection(documents) %>
15
- <% else %>
16
- <% document_presenters = documents.map { |doc| document_presenter(doc) } -%>
17
- <%= render view_config.document_component.with_collection(document_presenters) %>
18
- <% end %>
13
+ <% document_presenters = documents.map { |doc| document_presenter(doc) } -%>
14
+ <%= render view_config.document_component.with_collection(document_presenters) %>
19
15
  </table>
@@ -1,13 +1,8 @@
1
1
  <div class="container">
2
2
  <div class="row">
3
3
  <%- view_config = blacklight_config.view_config(action_name: :edit) %>
4
- <%= render (view_config.document_component || Blacklight::DocumentComponent).new((Blacklight.version > '8.0' ? :document : :presenter) => document_presenter(@document), classes: ['col-md-8'], component: :div, show: true, actions: false, partials: view_config.partials) do |component| %>
4
+ <%= render (view_config.document_component || Blacklight::DocumentComponent).new(document: document_presenter(@document), classes: ['col-md-8'], component: :div, show: true, partials: view_config.partials) do |component| %>
5
5
  <% component.with_title(as: 'h1', classes: '', link_to_document: false) %>
6
- <% component.with_body do %>
7
- <% view_config.partials.each do |view_partial| %>
8
- <%= render_document_partial @document, view_partial, component: component, document_counter: 1 %>
9
- <% end %>
10
- <% end if Blacklight.version < '8.0' && view_config.document_component.blank? %>
11
6
  <% end %>
12
7
  <div class="col-md-4">
13
8
  <%= render 'edit_default', document: @document %>
@@ -28,7 +28,7 @@
28
28
 
29
29
  <% if exhibit.description %>
30
30
  <p class="description">
31
- <%= exhibit.description %>
31
+ <%= strip_tags(exhibit.description) %>
32
32
  </p>
33
33
  <% end %>
34
34
  <% end %>
@@ -1,8 +1,8 @@
1
1
  <%= bootstrap_form_for @exhibit, url: ((spotlight.exhibit_path(@exhibit) if @exhibit.persisted?) || spotlight.exhibits_path), layout: :horizontal, label_col: 'col-md-2', control_col: 'col-md-10', html: {class: "row"} do |f| %>
2
2
  <div class="col-md-12">
3
3
  <%= f.text_field :title, disabled: !default_language?, help: !default_language? ? t('.uneditable_non_default_language') : '' %>
4
- <%= f.text_field :subtitle %>
5
- <%= f.text_area :description %>
4
+ <%= f.text_field :subtitle, disabled: !default_language?, help: !default_language? ? t('.uneditable_non_default_language') : '' %>
5
+ <%= f.text_area :description, disabled: !default_language?, help: !default_language? ? t('.uneditable_non_default_language') : '' %>
6
6
  <%= render Spotlight::TagListFormComponent.new(form: f) %>
7
7
  <%= f.form_group(:contact_emails, label: { text: nil, class: nil, for: 'exhibit_contact_email_0' }, class: 'form-group mb-3', help: nil) do %>
8
8
  <%= f.fields_for :contact_emails do |contact| %>
@@ -3,59 +3,59 @@
3
3
  <% end %>
4
4
 
5
5
  <%= configuration_page_title %>
6
- <%= bootstrap_form_for @blacklight_configuration, url: spotlight.exhibit_metadata_configuration_path(@exhibit), layout: :horizontal, label_col: 'col-md-3 col-sm-3', control_col: 'col-md-5 col-sm-5' do |f| %>
7
- <h2><%= t(:'.order_header') %></h2>
6
+ <%# These fields are translatable and should only be edited here in the default locale %>
7
+ <% if default_language? %>
8
+ <%= bootstrap_form_for @blacklight_configuration, url: spotlight.exhibit_metadata_configuration_path(@exhibit), layout: :horizontal, label_col: 'col-md-3 col-sm-3', control_col: 'col-md-5 col-sm-5' do |f| %>
9
+ <h2><%= t(:'.order_header') %></h2>
8
10
 
9
- <p class="instructions"><%= t :'.instructions' %></p>
11
+ <p class="instructions"><%= t :'.instructions' %></p>
10
12
 
11
- <table id="nested-fields" class="metadata-configuration table table-striped dd-table">
12
- <thead>
13
- <tr>
14
- <th class="w-50"><%= t :'.field.label' %></th>
15
- <th class="text-center">
16
- <div>
17
- <%= t :'.view.show' %>
18
- </div>
19
- <div class="text-center">
20
- <%= label_tag 'item_details', class: 'select-label' do %>
21
- <%= select_deselect_action(t :'.view.select_id') %>
22
- <%= t(:'.select_all') %>
23
- <% end %>
24
- </div>
25
- </th>
26
- <% available_view_fields.keys.each do |type| %>
13
+ <table id="nested-fields" class="metadata-configuration table table-striped dd-table">
14
+ <thead>
15
+ <tr>
16
+ <th class="w-50"><%= t :'.field.label' %></th>
27
17
  <th class="text-center">
28
- <div>
29
- <%= t :".view.#{type}", default: t("blacklight.search.view.#{type}", default: type.to_s.humanize.titleize) %>
18
+ <div>
19
+ <%= t :'.view.show' %>
30
20
  </div>
31
21
  <div class="text-center">
32
- <%= label_tag t(:'.deselect_all') + type.to_s, class: 'select-label' do %>
33
- <%= select_deselect_action(t(:'.deselect_all') + type.to_s) %>
34
- <%= t(:'.select_all') %>
35
- <% end %>
22
+ <%= label_tag 'item_details', class: 'select-label' do %>
23
+ <%= select_deselect_action(t :'.view.select_id') %>
24
+ <%= t(:'.select_all') %>
25
+ <% end %>
36
26
  </div>
37
27
  </th>
28
+ <% available_view_fields.keys.each do |type| %>
29
+ <th class="text-center">
30
+ <div>
31
+ <%= t :".view.#{type}", default: t("blacklight.search.view.#{type}", default: type.to_s.humanize.titleize) %>
32
+ </div>
33
+ <div class="text-center">
34
+ <%= label_tag t(:'.deselect_all') + type.to_s, class: 'select-label' do %>
35
+ <%= select_deselect_action(t(:'.deselect_all') + type.to_s) %>
36
+ <%= t(:'.select_all') %>
37
+ <% end %>
38
+ </div>
39
+ </th>
40
+ <% end %>
41
+ <th class="text-center"><%= t :'.type_label' %></th>
42
+ </tr>
43
+ </thead>
44
+ <tbody class="metadata_fields dd dd-list" data-behavior="nestable" data-max-depth="1" data-list-node-name="tbody" data-item-node-name="tr" data-expand-btn-HTML=" " data-collapse-btn-HTML=" ">
45
+ <%= f.fields_for :index_fields do |idxf| %>
46
+ <% @blacklight_configuration.blacklight_config.index_fields.select { |k, v| blacklight_configuration_context.evaluate_if_unless_configuration(v.original) }.each do |key, config| %>
47
+ <%= render partial: 'metadata_field', locals: { key: key, config: config, f: idxf } %>
48
+ <% end %>
38
49
  <% end %>
39
- <th class="text-center"><%= t :'.type_label' %></th>
40
- </tr>
41
- </thead>
42
- <tbody class="metadata_fields dd dd-list" data-behavior="nestable" data-max-depth="1" data-list-node-name="tbody" data-item-node-name="tr" data-expand-btn-HTML=" " data-collapse-btn-HTML=" ">
43
- <%= f.fields_for :index_fields do |idxf| %>
44
- <% @blacklight_configuration.blacklight_config.index_fields.select { |k, v| blacklight_configuration_context.evaluate_if_unless_configuration(v.original) }.each do |key, config| %>
45
- <%= render partial: 'metadata_field', locals: { key: key, config: config, f: idxf } %>
46
- <% end %>
47
- <% end %>
48
- </tbody>
49
- </table>
50
+ </tbody>
51
+ </table>
50
52
 
51
- <div class="form-actions">
52
- <div class="primary-actions">
53
- <%= f.submit nil, class: 'btn btn-primary' %>
53
+ <div class="form-actions">
54
+ <div class="primary-actions">
55
+ <%= f.submit nil, class: 'btn btn-primary' %>
56
+ </div>
54
57
  </div>
55
- </div>
56
- <% end %>
57
-
58
-
58
+ <% end %>
59
59
  <h2 class="mt-4"><%= t(:'.exhibit_specific.header') %></h2>
60
60
  <p class="instructions"><%= t(:'.exhibit_specific.instructions') %></p>
61
61
 
@@ -80,4 +80,7 @@
80
80
  </table>
81
81
 
82
82
 
83
- <%= exhibit_create_link Spotlight::CustomField.new, class: 'btn btn-primary' %>
83
+ <%= exhibit_create_link Spotlight::CustomField.new, class: 'btn btn-primary' %>
84
+ <% else %>
85
+ <%= render Spotlight::UneditableNonDefaultLanguageComponent.new(current_exhibit:, current_language: I18n.locale) %>
86
+ <% end %>
@@ -1,33 +1,38 @@
1
1
  <%= curation_page_title t(:"spotlight.pages.index.#{page_collection_name}.header") %>
2
- <%= bootstrap_form_for @exhibit, url: polymorphic_path([:update_all, @exhibit, page_collection_name.to_sym]), layout: :horizontal, control_col: 'col-sm-10', html: {:'data-form-observer' => true} do |f| %>
2
+ <%# These fields are translatable and should only be edited here in the default locale %>
3
+ <% if default_language? %>
4
+ <%= bootstrap_form_for @exhibit, url: polymorphic_path([:update_all, @exhibit, page_collection_name.to_sym]), layout: :horizontal, control_col: 'col-sm-10', html: {:'data-form-observer' => true} do |f| %>
3
5
 
4
- <%= render partial: 'header', locals: {f: f} %>
5
- <h2 class="mt-4"><%= t :'.pages_header' %></h2>
6
- <p class="instructions"><%= t :'.instructions' %></p>
7
- <div class="panel-group dd <%= page_collection_name %>_admin" id="nested-pages" data-behavior="nestable" <%= nestable_data_attributes(page_collection_name).html_safe %> >
8
- <ol class="dd-list">
9
- <%= f.fields_for page_collection_name do |p| %>
10
- <%- if p.object.about_page? || p.object.top_level_page? -%>
11
- <%= render partial: 'page', locals: {f: p, parent_form: f} %>
12
- <%- end -%>
13
- <% end %>
14
- </ol>
15
- </div>
16
- <div class="form-actions float-end">
17
- <div class="primary-actions">
18
- <%= button_tag action_label(page_collection_name, :update_all), class: "btn btn-primary", disabled: disable_save_pages_button? %>
6
+ <%= render partial: 'header', locals: {f: f} %>
7
+ <h2 class="mt-4"><%= t :'.pages_header' %></h2>
8
+ <div class="instructions"><%= t :'.instructions_html' %></div>
9
+ <div class="panel-group dd <%= page_collection_name %>_admin" id="nested-pages" data-behavior="nestable" <%= nestable_data_attributes(page_collection_name).html_safe %> >
10
+ <ol class="dd-list">
11
+ <%= f.fields_for page_collection_name do |p| %>
12
+ <%- if p.object.about_page? || p.object.top_level_page? -%>
13
+ <%= render partial: 'page', locals: {f: p, parent_form: f} %>
14
+ <%- end -%>
15
+ <% end %>
16
+ </ol>
17
+ </div>
18
+ <div class="form-actions float-end">
19
+ <div class="primary-actions">
20
+ <%= button_tag action_label(page_collection_name, :update_all), class: "btn btn-primary", disabled: disable_save_pages_button? %>
21
+ </div>
19
22
  </div>
20
- </div>
21
- <%- end -%>
22
- <div>
23
- <%= form_for @page, url: spotlight.polymorphic_path([@exhibit, page_collection_name.to_sym]), html: {class: "expanded-add-button"} do |f|%>
24
- <a href='#add-new' class="btn btn-primary" data-turbo="false" data-turbolinks="false" data-expanded-add-button="true" data-field-target="[data-title-field]">
25
- <%= t(:'.new_page') %> <%= blacklight_icon('chevron_right') %>
26
- <span data-title-field="true" class="input-field">
27
- <%= f.text_field(:title) %>
28
- <%= f.submit t(:'.save'), data: {behavior: "save"} %>
29
- <%= f.submit t(:'.cancel'), data: {behavior: "cancel"} %>
30
- </span>
31
- </a>
32
23
  <%- end -%>
33
- </div>
24
+ <div>
25
+ <%= form_for @page, url: spotlight.polymorphic_path([@exhibit, page_collection_name.to_sym]), html: {class: "expanded-add-button"} do |f|%>
26
+ <a href='#add-new' class="btn btn-primary" data-turbo="false" data-turbolinks="false" data-expanded-add-button="true" data-field-target="[data-title-field]">
27
+ <%= t(:'.new_page') %> <%= blacklight_icon('chevron_right') %>
28
+ <span data-title-field="true" class="input-field">
29
+ <%= f.text_field(:title) %>
30
+ <%= f.submit t(:'.save'), data: {behavior: "save"} %>
31
+ <%= f.submit t(:'.cancel'), data: {behavior: "cancel"} %>
32
+ </span>
33
+ </a>
34
+ <%- end -%>
35
+ </div>
36
+ <% else %>
37
+ <%= render Spotlight::UneditableNonDefaultLanguageComponent.new(current_exhibit:, current_language: I18n.locale)%>
38
+ <% end %>