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.
- checksums.yaml +4 -4
- data/README.md +153 -48
- data/app/assets/javascripts/spotlight/spotlight.esm.js +33 -1
- data/app/assets/javascripts/spotlight/spotlight.esm.js.map +1 -1
- data/app/assets/javascripts/spotlight/spotlight.js +33 -1
- data/app/assets/javascripts/spotlight/spotlight.js.map +1 -1
- data/app/assets/stylesheets/spotlight/_admin_users.scss +28 -0
- data/app/assets/stylesheets/spotlight/_browse.scss +1 -1
- data/app/assets/stylesheets/spotlight/_featured_browse_categories_block.scss +1 -1
- data/app/assets/stylesheets/spotlight/_spotlight.scss +1 -0
- data/app/components/spotlight/admin_users/email_component.html.erb +5 -0
- data/app/components/spotlight/admin_users/email_component.rb +22 -0
- data/app/components/spotlight/admin_users/exhibit_roles_component.html.erb +28 -0
- data/app/components/spotlight/admin_users/exhibit_roles_component.rb +19 -0
- data/app/components/spotlight/admin_users/site_admin_component.html.erb +13 -0
- data/app/components/spotlight/admin_users/site_admin_component.rb +17 -0
- data/app/components/spotlight/analytics/aggregation_component.rb +1 -1
- data/app/components/spotlight/analytics/dashboard_component.rb +1 -1
- data/app/components/spotlight/breadcrumbs_component.rb +1 -1
- data/app/components/spotlight/bulk_action_component.rb +1 -1
- data/app/components/spotlight/edit_view_links_component.rb +1 -1
- data/app/components/spotlight/header_navigation_link_component.rb +1 -1
- data/app/components/spotlight/save_search_component.rb +1 -1
- data/app/components/spotlight/select_image_component.rb +1 -1
- data/app/components/spotlight/skip_link_component.rb +1 -1
- data/app/components/spotlight/solr_document_legacy_embed_component.rb +2 -2
- data/app/components/spotlight/tag_list_form_component.rb +1 -1
- data/app/components/spotlight/tag_selector_component.rb +1 -1
- data/app/components/spotlight/title_component.rb +1 -1
- data/app/components/spotlight/translations/subheading_component.rb +1 -1
- data/app/components/spotlight/uneditable_non_default_language_component.html.erb +5 -0
- data/app/components/spotlight/uneditable_non_default_language_component.rb +25 -0
- data/app/controllers/spotlight/admin_users_controller.rb +11 -1
- data/app/controllers/spotlight/browse_controller.rb +2 -8
- data/app/controllers/spotlight/bulk_actions_controller.rb +1 -1
- data/app/controllers/spotlight/bulk_updates_controller.rb +22 -7
- data/app/controllers/spotlight/catalog_controller.rb +8 -16
- data/app/controllers/spotlight/dashboards_controller.rb +2 -6
- data/app/controllers/spotlight/exhibits_controller.rb +1 -0
- data/app/controllers/spotlight/home_pages_controller.rb +1 -1
- data/app/controllers/spotlight/searches_controller.rb +1 -1
- data/app/controllers/spotlight/solr_controller.rb +1 -0
- data/app/helpers/spotlight/main_app_helpers.rb +1 -5
- data/app/helpers/spotlight/rendering_helper.rb +4 -1
- data/app/javascript/spotlight/admin/blocks/pages_block.js +2 -0
- data/app/javascript/spotlight/user/carousel.js +32 -2
- data/app/jobs/spotlight/add_tags_job.rb +1 -0
- data/app/jobs/spotlight/add_uploads_from_csv.rb +1 -0
- data/app/jobs/spotlight/change_visibility_job.rb +1 -0
- data/app/jobs/spotlight/process_bulk_updates_csv_job.rb +1 -0
- data/app/jobs/spotlight/reindex_exhibit_job.rb +1 -0
- data/app/jobs/spotlight/reindex_job.rb +1 -0
- data/app/jobs/spotlight/remove_tags_job.rb +1 -0
- data/app/jobs/spotlight/rename_sidecar_field_job.rb +1 -0
- data/app/models/concerns/spotlight/user.rb +5 -0
- data/app/models/sir_trevor_rails/blocks/browse_block.rb +1 -1
- data/app/models/sir_trevor_rails/blocks/featured_pages_block.rb +1 -1
- data/app/models/sir_trevor_rails/blocks/solr_documents_block.rb +1 -0
- data/app/models/spotlight/ability.rb +1 -1
- data/app/models/spotlight/about_page.rb +1 -0
- data/app/models/spotlight/blacklight_configuration.rb +6 -7
- data/app/models/spotlight/contact.rb +2 -1
- data/app/models/spotlight/contact_email.rb +1 -0
- data/app/models/spotlight/custom_field.rb +1 -0
- data/app/models/spotlight/exhibit.rb +2 -9
- data/app/models/spotlight/feature_page.rb +1 -0
- data/app/models/spotlight/group.rb +2 -1
- data/app/models/spotlight/home_page.rb +1 -0
- data/app/models/spotlight/job_tracker.rb +1 -1
- data/app/models/spotlight/main_navigation.rb +1 -1
- data/app/models/spotlight/page.rb +35 -19
- data/app/models/spotlight/resource.rb +1 -0
- data/app/models/spotlight/resources/iiif_manifest.rb +0 -126
- data/app/models/spotlight/resources/iiif_manifest_metadata.rb +161 -0
- data/app/models/spotlight/resources/iiif_manifest_v3.rb +41 -0
- data/app/models/spotlight/resources/iiif_service.rb +25 -1
- data/app/models/spotlight/search.rb +2 -1
- data/app/services/spotlight/exhibit_import_export_service.rb +3 -1
- data/app/views/spotlight/accessibility/alt_text.html.erb +3 -0
- data/app/views/spotlight/admin_users/index.html.erb +16 -26
- data/app/views/spotlight/appearances/edit.html.erb +14 -9
- data/app/views/spotlight/catalog/_document_admin_table.html.erb +2 -6
- data/app/views/spotlight/catalog/edit.html.erb +1 -6
- data/app/views/spotlight/exhibits/_exhibit_card.html.erb +1 -1
- data/app/views/spotlight/exhibits/_form.html.erb +2 -2
- data/app/views/spotlight/metadata_configurations/edit.html.erb +47 -44
- data/app/views/spotlight/pages/_order_pages.html.erb +34 -29
- data/app/views/spotlight/pages/show.html.erb +4 -2
- data/app/views/spotlight/resources/csv_upload/_form.html.erb +2 -0
- data/app/views/spotlight/search_configurations/edit.html.erb +40 -34
- data/app/views/spotlight/searches/index.html.erb +4 -2
- data/app/views/spotlight/sir_trevor/blocks/_embedded_document.html.erb +2 -2
- data/app/views/spotlight/sir_trevor/blocks/_search_results_block.html.erb +2 -2
- data/app/views/spotlight/sir_trevor/blocks/_solr_documents_carousel_block.html.erb +5 -3
- data/app/views/spotlight/sir_trevor/blocks/_text_block.html.erb +1 -1
- data/app/views/spotlight/translations/_page.html.erb +1 -1
- data/config/initializers/devise_rails8_patch.rb +13 -0
- data/config/locales/spotlight.en.yml +39 -5
- data/config/routes.rb +5 -1
- data/lib/spotlight/engine.rb +10 -26
- data/lib/spotlight/version.rb +1 -1
- data/spec/fixtures/iiif_responses.rb +344 -0
- data/spec/support/features/test_features_helpers.rb +4 -6
- data/spec/support/stub_iiif_response.rb +1 -0
- data/spec/support/with_queue_adapter.rb +17 -0
- metadata +44 -31
- data/app/components/spotlight/blocks/heading_block_component.erb +0 -2
- 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
|
-
|
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(
|
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.
|
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
|
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
|
12
|
+
<tbody>
|
12
13
|
<% @site.roles.map(&:user).each do |user| %>
|
13
14
|
<tr>
|
14
|
-
<td
|
15
|
-
<%= user
|
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
|
-
<%=
|
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)
|
71
|
-
<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
|
73
|
+
<tbody>
|
75
74
|
<% @users.each do |user| %>
|
76
75
|
<tr>
|
77
|
-
<td class="
|
78
|
-
<%= user
|
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
|
82
|
-
<%=
|
79
|
+
<td>
|
80
|
+
<%= render Spotlight::AdminUsers::SiteAdminComponent.new(user:) %>
|
83
81
|
</td>
|
84
|
-
<td class="
|
85
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
<
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
<%
|
14
|
-
|
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(
|
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 %>
|
@@ -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
|
-
|
7
|
-
|
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
|
-
|
11
|
+
<p class="instructions"><%= t :'.instructions' %></p>
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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 :
|
18
|
+
<div>
|
19
|
+
<%= t :'.view.show' %>
|
30
20
|
</div>
|
31
21
|
<div class="text-center">
|
32
|
-
<%= label_tag
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
40
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
53
|
+
<div class="form-actions">
|
54
|
+
<div class="primary-actions">
|
55
|
+
<%= f.submit nil, class: 'btn btn-primary' %>
|
56
|
+
</div>
|
54
57
|
</div>
|
55
|
-
|
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
|
-
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
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 %>
|