blacklight-spotlight 2.9.0 → 2.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/spotlight/custom_search_fields_controller.rb +65 -0
- data/app/controllers/spotlight/featured_images_controller.rb +4 -1
- data/app/models/spotlight/ability.rb +1 -0
- data/app/models/spotlight/blacklight_configuration.rb +13 -0
- data/app/models/spotlight/custom_search_field.rb +37 -0
- data/app/models/spotlight/exhibit.rb +1 -0
- data/app/models/spotlight/featured_image.rb +1 -2
- data/app/models/spotlight/resources/iiif_manifest.rb +80 -15
- data/app/models/spotlight/solr_document_sidecar.rb +4 -4
- data/app/presenters/spotlight/iiif_manifest_presenter.rb +4 -2
- data/app/services/spotlight/upload_solr_document_builder.rb +3 -2
- data/app/views/spotlight/custom_search_fields/_form.html.erb +13 -0
- data/app/views/spotlight/custom_search_fields/edit.html.erb +5 -0
- data/app/views/spotlight/custom_search_fields/new.html.erb +5 -0
- data/app/views/spotlight/search_configurations/_search_fields.html.erb +27 -0
- data/app/views/spotlight/search_configurations/edit.html.erb +0 -1
- data/config/i18n-tasks.yml +3 -1
- data/config/locales/spotlight.en.yml +22 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20190910200927_create_spotlight_custom_search_fields.rb +12 -0
- data/lib/generators/spotlight/templates/config/initializers/spotlight_initializer.rb +34 -0
- data/lib/spotlight/engine.rb +32 -0
- data/lib/spotlight/upload_field_config.rb +21 -4
- data/lib/spotlight/version.rb +1 -1
- data/spec/controllers/spotlight/custom_search_fields_controller_spec.rb +60 -0
- data/spec/examples.txt +1326 -1282
- data/spec/factories/custom_search_fields.rb +9 -0
- data/spec/features/exhibits/custom_search_fields_spec.rb +55 -0
- data/spec/fixtures/iiif_responses.rb +24 -0
- data/spec/lib/spotlight/upload_field_config_spec.rb +16 -0
- data/spec/models/spotlight/blacklight_configuration_spec.rb +21 -0
- data/spec/models/spotlight/custom_search_field_spec.rb +54 -0
- data/spec/models/spotlight/resources/iiif_manifest_spec.rb +38 -2
- data/spec/presenters/spotlight/iiif_manifest_presenter_spec.rb +2 -4
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2148d0879e24d68085e54c6fb9569af9900383d7bdb12c00f1568cf846e6b3c0
|
4
|
+
data.tar.gz: e2ff46720e4f722e64a47eeb29ceaf4cc7a981731942134b81ae0f94eb376b02
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9a8590c2c12ab0c5df2e61ea8e5608a465564dd33ee7b6fb849578f98fb5aa5116b7706240ec2955065b6e0d1c65d1db4fa0837e2d73083fc4a0cf06cff7c0b6
|
7
|
+
data.tar.gz: 290494323ae7ccb37ccfd9e80df582df82814f6129025bc95d42dad2611ed7d105cb3cdc0c1cd40239edb05aa3dff4177ee784980209e8100cb0bf82351aba58
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spotlight
|
4
|
+
# CRUD actions for exhibit custom search field management.
|
5
|
+
class CustomSearchFieldsController < ApplicationController
|
6
|
+
before_action :authenticate_user!
|
7
|
+
|
8
|
+
load_and_authorize_resource :exhibit, class: Spotlight::Exhibit
|
9
|
+
load_and_authorize_resource through: :exhibit
|
10
|
+
before_action :attach_breadcrumbs, only: [:new, :edit]
|
11
|
+
|
12
|
+
# GET /custom_search_fields/new
|
13
|
+
def new
|
14
|
+
add_breadcrumb t(:'helpers.action.spotlight/custom_search_field.create'), new_exhibit_custom_search_field_path(@exhibit)
|
15
|
+
end
|
16
|
+
|
17
|
+
# GET /custom_search_fields/1/edit
|
18
|
+
def edit
|
19
|
+
add_breadcrumb @custom_search_field.label, edit_exhibit_custom_search_field_path(@custom_search_field.exhibit, @custom_search_field)
|
20
|
+
end
|
21
|
+
|
22
|
+
# POST /custom_search_fields
|
23
|
+
def create
|
24
|
+
@custom_search_field.attributes = custom_search_field_params
|
25
|
+
@custom_search_field.exhibit = current_exhibit
|
26
|
+
|
27
|
+
if @custom_search_field.save
|
28
|
+
redirect_to edit_exhibit_search_configuration_path(@custom_search_field.exhibit),
|
29
|
+
notice: t(:'helpers.submit.custom_search_field.created', model: @custom_search_field.class.model_name.human.downcase)
|
30
|
+
else
|
31
|
+
render action: 'new'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# PATCH/PUT /custom_search_fields/1
|
36
|
+
def update
|
37
|
+
if @custom_search_field.update(custom_search_field_params)
|
38
|
+
redirect_to edit_exhibit_search_configuration_path(@custom_search_field.exhibit),
|
39
|
+
notice: t(:'helpers.submit.custom_search_field.updated', model: @custom_search_field.class.model_name.human.downcase)
|
40
|
+
else
|
41
|
+
render :edit
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def destroy
|
46
|
+
@custom_search_field.destroy
|
47
|
+
|
48
|
+
redirect_to edit_exhibit_search_configuration_path(@custom_search_field.exhibit),
|
49
|
+
notice: t(:'helpers.submit.custom_search_field.destroyed', model: @custom_search_field.class.model_name.human.downcase)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def attach_breadcrumbs
|
55
|
+
add_breadcrumb t(:'spotlight.exhibits.breadcrumb', title: @exhibit.title), @exhibit
|
56
|
+
add_breadcrumb t(:'spotlight.configuration.sidebar.header'), exhibit_dashboard_path(@exhibit)
|
57
|
+
add_breadcrumb t(:'spotlight.configuration.sidebar.search_configuration'), edit_exhibit_search_configuration_path(@exhibit)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Only allow a trusted parameter "white list" through.
|
61
|
+
def custom_search_field_params
|
62
|
+
params.require(:custom_search_field).permit(:slug, :field, :label)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -16,7 +16,10 @@ module Spotlight
|
|
16
16
|
private
|
17
17
|
|
18
18
|
def tilesource
|
19
|
-
|
19
|
+
Spotlight::Engine.config.iiif_url_helpers.info_url(
|
20
|
+
@featured_image.id,
|
21
|
+
host: request.host_with_port
|
22
|
+
)
|
20
23
|
end
|
21
24
|
|
22
25
|
# The create action can be called from a number of different forms, so
|
@@ -136,6 +136,7 @@ module Spotlight
|
|
136
136
|
|
137
137
|
config.show_fields = config.index_fields
|
138
138
|
|
139
|
+
config.search_fields.merge! custom_search_fields(config)
|
139
140
|
unless search_fields.blank?
|
140
141
|
config.search_fields = Hash[config.search_fields.sort_by { |k, _v| field_weight(search_fields, k) }]
|
141
142
|
|
@@ -227,6 +228,18 @@ module Spotlight
|
|
227
228
|
end]
|
228
229
|
end
|
229
230
|
|
231
|
+
def custom_search_fields(blacklight_config)
|
232
|
+
Hash[exhibit.custom_search_fields.reject(&:new_record?).map do |custom_field|
|
233
|
+
original_config = blacklight_config.search_fields[custom_field.field] || {}
|
234
|
+
field = Blacklight::Configuration::SearchField.new original_config.merge(
|
235
|
+
custom_field.configuration.merge(
|
236
|
+
key: custom_field.slug, solr_parameters: { qf: custom_field.field }, custom_field: true
|
237
|
+
)
|
238
|
+
)
|
239
|
+
[custom_field.slug, field]
|
240
|
+
end]
|
241
|
+
end
|
242
|
+
|
230
243
|
##
|
231
244
|
# Get the "upstream" blacklight configuration to use
|
232
245
|
def default_blacklight_config
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spotlight
|
4
|
+
# Exhibit-specific custom search fields
|
5
|
+
class CustomSearchField < ApplicationRecord
|
6
|
+
serialize :configuration, Hash
|
7
|
+
belongs_to :exhibit
|
8
|
+
|
9
|
+
def label=(label)
|
10
|
+
configuration['label'] = label
|
11
|
+
|
12
|
+
update_blacklight_configuration_label label
|
13
|
+
end
|
14
|
+
|
15
|
+
def label
|
16
|
+
conf = if slug && blacklight_configuration && blacklight_configuration.search_fields.key?(slug)
|
17
|
+
blacklight_configuration.search_fields[slug].reverse_merge(configuration)
|
18
|
+
else
|
19
|
+
configuration
|
20
|
+
end
|
21
|
+
conf['label']
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def blacklight_configuration
|
27
|
+
exhibit&.blacklight_configuration
|
28
|
+
end
|
29
|
+
|
30
|
+
def update_blacklight_configuration_label(label)
|
31
|
+
return unless slug && blacklight_configuration && blacklight_configuration.search_fields.key?(slug)
|
32
|
+
|
33
|
+
blacklight_configuration.search_fields[slug]['label'] = label
|
34
|
+
blacklight_configuration.save
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -53,8 +53,7 @@ module Spotlight
|
|
53
53
|
def set_tilesource_from_uploaded_resource
|
54
54
|
return if iiif_tilesource
|
55
55
|
|
56
|
-
|
57
|
-
self.iiif_tilesource = riiif.info_path(id)
|
56
|
+
self.iiif_tilesource = Spotlight::Engine.config.iiif_url_helpers.info_path(id)
|
58
57
|
save
|
59
58
|
end
|
60
59
|
|
@@ -70,7 +70,7 @@ module Spotlight
|
|
70
70
|
return unless title_fields.present? && manifest.try(:label)
|
71
71
|
|
72
72
|
Array.wrap(title_fields).each do |field|
|
73
|
-
solr_hash[field] =
|
73
|
+
solr_hash[field] = metadata_class.new(manifest).label
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
@@ -96,13 +96,6 @@ module Spotlight
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
-
def json_ld_value(value)
|
100
|
-
return value['@value'] if value.is_a?(Hash)
|
101
|
-
return value.find { |v| v['@language'] == default_json_ld_language }.try(:[], '@value') if value.is_a?(Array)
|
102
|
-
|
103
|
-
value
|
104
|
-
end
|
105
|
-
|
106
99
|
def create_sidecars_for(*keys)
|
107
100
|
missing_keys(keys).each do |k|
|
108
101
|
exhibit.custom_fields.create! label: k, readonly_field: true
|
@@ -164,10 +157,6 @@ module Spotlight
|
|
164
157
|
Spotlight::Engine.config.iiif_title_fields || blacklight_config.index.try(:title_field)
|
165
158
|
end
|
166
159
|
|
167
|
-
def default_json_ld_language
|
168
|
-
Spotlight::Engine.config.default_json_ld_language
|
169
|
-
end
|
170
|
-
|
171
160
|
def sidecar
|
172
161
|
@sidecar ||= document_model.new(id: compound_id).sidecar(exhibit)
|
173
162
|
end
|
@@ -195,6 +184,12 @@ module Spotlight
|
|
195
184
|
metadata_hash.merge(manifest_level_metadata)
|
196
185
|
end
|
197
186
|
|
187
|
+
def label
|
188
|
+
return unless manifest.try(:label)
|
189
|
+
|
190
|
+
Array(json_ld_value(manifest.label)).map { |v| html_sanitize(v) }.first
|
191
|
+
end
|
192
|
+
|
198
193
|
private
|
199
194
|
|
200
195
|
attr_reader :manifest
|
@@ -210,8 +205,10 @@ module Spotlight
|
|
210
205
|
metadata.each_with_object({}) do |md, hash|
|
211
206
|
next unless md['label'] && md['value']
|
212
207
|
|
213
|
-
|
214
|
-
|
208
|
+
label = Array(json_ld_value(md['label'])).first
|
209
|
+
|
210
|
+
hash[label] ||= []
|
211
|
+
hash[label] += Array(json_ld_value(md['value'])).map { |v| html_sanitize(v) }
|
215
212
|
end
|
216
213
|
end
|
217
214
|
|
@@ -221,13 +218,81 @@ module Spotlight
|
|
221
218
|
manifest.send(field).present?
|
222
219
|
|
223
220
|
hash[field.capitalize] ||= []
|
224
|
-
hash[field.capitalize] += Array(manifest.send(field))
|
221
|
+
hash[field.capitalize] += Array(json_ld_value(manifest.send(field))).map { |v| html_sanitize(v) }
|
225
222
|
end
|
226
223
|
end
|
227
224
|
|
228
225
|
def manifest_fields
|
229
226
|
%w(attribution description license)
|
230
227
|
end
|
228
|
+
|
229
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
|
230
|
+
def json_ld_value(value)
|
231
|
+
case value
|
232
|
+
# In the case where multiple values are supplied, clients must use the following algorithm to determine which values to display to the user.
|
233
|
+
when Array
|
234
|
+
# IIIF v2, multivalued monolingual, or multivalued multilingual values
|
235
|
+
|
236
|
+
# If none of the values have a language associated with them, the client must display all of the values.
|
237
|
+
if value.none? { |v| v.is_a?(Hash) && v.key?('@language') }
|
238
|
+
value.map { |v| json_ld_value(v) }
|
239
|
+
# 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
|
240
|
+
# matches the language preference.
|
241
|
+
elsif value.any? { |v| v.is_a?(Hash) && v['@language'] == default_json_ld_language }
|
242
|
+
value.select { |v| v.is_a?(Hash) && v['@language'] == default_json_ld_language }.map { |v| v['@value'] }
|
243
|
+
# If all of the values have a language associated with them, and none match the language preference, the client must select a language
|
244
|
+
# and display all of the values associated with that language.
|
245
|
+
elsif value.all? { |v| v.is_a?(Hash) && v.key?('@language') }
|
246
|
+
selected_json_ld_language = value.find { |v| v.is_a?(Hash) && v.key?('@language') }
|
247
|
+
|
248
|
+
value.select { |v| v.is_a?(Hash) && v['@language'] == selected_json_ld_language['@language'] }
|
249
|
+
.map { |v| v['@value'] }
|
250
|
+
# 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
|
251
|
+
# that do not have a language associated with them.
|
252
|
+
else
|
253
|
+
value.select { |v| !v.is_a?(Hash) || !v.key?('@language') }.map { |v| json_ld_value(v) }
|
254
|
+
end
|
255
|
+
when Hash
|
256
|
+
# IIIF v2 single-valued value
|
257
|
+
if value.key? '@value'
|
258
|
+
value['@value']
|
259
|
+
# IIIF v3 multilingual(?), multivalued(?) values
|
260
|
+
# If all of the values are associated with the none key, the client must display all of those values.
|
261
|
+
elsif value.keys == ['none']
|
262
|
+
value['none']
|
263
|
+
# If any of the values have a language associated with them, the client must display all of the values associated with the language
|
264
|
+
# that best matches the language preference.
|
265
|
+
elsif value.key? default_json_ld_language
|
266
|
+
value[default_json_ld_language]
|
267
|
+
# If some of the values have a language associated with them, but none match the language preference, the client must display all
|
268
|
+
# of the values that do not have a language associated with them.
|
269
|
+
elsif value.key? 'none'
|
270
|
+
value['none']
|
271
|
+
# If all of the values have a language associated with them, and none match the language preference, the client must select a
|
272
|
+
# language and display all of the values associated with that language.
|
273
|
+
else
|
274
|
+
value.values.first
|
275
|
+
end
|
276
|
+
else
|
277
|
+
# plain old string/number/boolean
|
278
|
+
value
|
279
|
+
end
|
280
|
+
end
|
281
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
|
282
|
+
|
283
|
+
def html_sanitize(value)
|
284
|
+
return value unless value.is_a? String
|
285
|
+
|
286
|
+
html_sanitizer.sanitize(value)
|
287
|
+
end
|
288
|
+
|
289
|
+
def html_sanitizer
|
290
|
+
@html_sanitizer ||= Rails::Html::FullSanitizer.new
|
291
|
+
end
|
292
|
+
|
293
|
+
def default_json_ld_language
|
294
|
+
Spotlight::Engine.config.default_json_ld_language
|
295
|
+
end
|
231
296
|
end
|
232
297
|
end
|
233
298
|
end
|
@@ -74,11 +74,11 @@ module Spotlight
|
|
74
74
|
field_name = field.field_name.to_s
|
75
75
|
next unless configured_fields && configured_fields[field_name].present?
|
76
76
|
|
77
|
-
|
77
|
+
value = configured_fields[field_name]
|
78
|
+
field_data = field.data_to_solr(value)
|
78
79
|
|
79
|
-
|
80
|
-
|
81
|
-
end
|
80
|
+
# merge duplicate field mappings into a multivalued field
|
81
|
+
solr_hash.merge!(field_data) { |_key, v1, v2| Array(v1) + Array(v2) }
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
@@ -74,8 +74,10 @@ module Spotlight
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def iiif_url
|
77
|
-
|
78
|
-
|
77
|
+
Spotlight::Engine.config.iiif_url_helpers.info_url(
|
78
|
+
uploaded_resource.upload.id,
|
79
|
+
host: controller.request.host_with_port
|
80
|
+
).sub(%r{/info\.json\Z}, '')
|
79
81
|
end
|
80
82
|
end
|
81
83
|
end
|
@@ -31,7 +31,8 @@ module Spotlight
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def add_file_versions(solr_hash)
|
34
|
-
solr_hash[Spotlight::Engine.config.thumbnail_field] =
|
34
|
+
solr_hash[Spotlight::Engine.config.thumbnail_field] =
|
35
|
+
Spotlight::Engine.config.iiif_url_helpers.image_path(resource.upload_id, size: '!400,400')
|
35
36
|
end
|
36
37
|
|
37
38
|
def add_sidecar_fields(solr_hash)
|
@@ -47,7 +48,7 @@ module Spotlight
|
|
47
48
|
end
|
48
49
|
|
49
50
|
def riiif
|
50
|
-
|
51
|
+
Spotlight::Engine.config.iiif_url_helpers
|
51
52
|
end
|
52
53
|
|
53
54
|
def attached_file?
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<%= bootstrap_form_for @custom_search_field.new_record? ? [current_exhibit, @custom_search_field] : [@custom_search_field.exhibit, @custom_search_field], layout: :horizontal, label_col: 'col-md-3', control_col: 'col-md-9', html: {class: 'col-md-9', id: 'edit-search-field'} do |f| %>
|
2
|
+
|
3
|
+
<%= f.text_field :slug %>
|
4
|
+
<%= f.text_field :field, help: t('.field.help') %>
|
5
|
+
<%= f.text_field :label %>
|
6
|
+
|
7
|
+
<div class="form-actions">
|
8
|
+
<div class="primary-actions">
|
9
|
+
<%= link_to t(:"cancel"), edit_exhibit_search_configuration_path(current_exhibit), class: "btn btn-link" %>
|
10
|
+
<%= f.submit nil, class: 'btn btn-primary' %>
|
11
|
+
</div>
|
12
|
+
</div>
|
13
|
+
<% end %>
|
@@ -55,3 +55,30 @@
|
|
55
55
|
</div>
|
56
56
|
<% end %>
|
57
57
|
<% end %>
|
58
|
+
|
59
|
+
<% if can? :manage, Spotlight::CustomSearchField.new(exhibit: current_exhibit) %>
|
60
|
+
<h3><%= t(:'.exhibit_specific.header') %></h3>
|
61
|
+
<p class="instructions"><%= t(:'.exhibit_specific.instructions') %></p>
|
62
|
+
|
63
|
+
<table class="table table-striped" id="exhibit-specific-fields">
|
64
|
+
<tbody>
|
65
|
+
<% @exhibit.custom_search_fields.each do |field| %>
|
66
|
+
<tr>
|
67
|
+
<td>
|
68
|
+
<div class="field-label"><%= field.label %></div>
|
69
|
+
<div class="actions">
|
70
|
+
<%= exhibit_edit_link field, class: 'btn btn-link' %> ·
|
71
|
+
<%= exhibit_delete_link field, class: 'btn btn-link' %>
|
72
|
+
</div>
|
73
|
+
</td>
|
74
|
+
<td class="field-description">
|
75
|
+
<%= field.field %>
|
76
|
+
</td>
|
77
|
+
</tr>
|
78
|
+
<% end %>
|
79
|
+
|
80
|
+
</tbody>
|
81
|
+
</table>
|
82
|
+
|
83
|
+
<%= exhibit_create_link Spotlight::CustomSearchField.new, class: 'btn btn-primary' %>
|
84
|
+
<% end %>
|
data/config/i18n-tasks.yml
CHANGED
@@ -37,6 +37,7 @@ ignore_unused:
|
|
37
37
|
- activerecord.attributes.spotlight/exhibit.published # app/views/spotlight/sites/_exhibit.html.erb
|
38
38
|
- activerecord.attributes.spotlight/masthead.display # app/views/spotlight/appearances/edit.html.erb
|
39
39
|
- activerecord.attributes.spotlight/custom_field.is_multiple # app/views/spotlight/custom_fields/_form.html.erb
|
40
|
+
- activerecord.attributes.spotlight/custom_search_field.field # app/views/spotlight/custom_search_fields/_form.html.erb
|
40
41
|
- helpers.label.spotlight/filter.{field,value} # app/views/spotlight/filters/_form.html.erb
|
41
42
|
- spotlight.catalog.admin.{title,header} # app/helpers/spotlight/title_helper.rb
|
42
43
|
- spotlight.{contacts,pages,searches}.edit.{title,header} # app/helpers/spotlight/title_helper.rb
|
@@ -46,8 +47,9 @@ ignore_unused:
|
|
46
47
|
- spotlight.metadata_configurations.edit.{select_all,deselect_all} # app/helpers/spotlight/application_helper.rb
|
47
48
|
- spotlight.featured_images.upload_form.{non_iiif_alert_html,source.exhibit.help,source.exhibit.label} # app/views/spotlight/featured_images/_form.html.erb
|
48
49
|
- spotlight.feature_pages.page_options.published # app/views/spotlight/feature_pages/_page_options.html.erb
|
49
|
-
- spotlight.{exhibits,custom_fields}.{new,edit}.header # configuration_page_title
|
50
|
+
- spotlight.{exhibits,custom_fields,custom_search_fields}.{new,edit}.header # configuration_page_title
|
50
51
|
- helpers.submit.custom_field.{batch_error,batch_updated,create,submit,update} # Generic repeated template
|
52
|
+
- helpers.submit.custom_search_field.{batch_error,batch_updated,create,submit,update} # Generic repeated template
|
51
53
|
- helpers.submit.exhibit.{batch_error,batch_updated,create,submit,update} # Generic repeated template
|
52
54
|
- helpers.submit.search.{create,submit,update} # Generic repeated template
|
53
55
|
- helpers.submit.site.{batch_error,batch_updated,create,created,destroyed,submit,update} # Generic repeated template
|