blacklight-spotlight 3.0.0.alpha.9 → 3.0.0.alpha.10

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/app/assets/javascripts/spotlight/admin/blocks/browse_block.js +55 -1
  4. data/app/assets/javascripts/spotlight/admin/blocks/uploaded_items_block.js +7 -2
  5. data/app/assets/javascripts/spotlight/admin/crop.es6 +5 -0
  6. data/app/assets/javascripts/spotlight/admin/sir-trevor/locales.js +3 -2
  7. data/app/assets/stylesheets/spotlight/_accessibility.scss +1 -1
  8. data/app/assets/stylesheets/spotlight/_browse.scss +16 -0
  9. data/app/assets/stylesheets/spotlight/_curation.scss +6 -0
  10. data/app/assets/stylesheets/spotlight/_featured_browse_categories_block.scss +3 -0
  11. data/app/assets/stylesheets/spotlight/_header.scss +1 -1
  12. data/app/assets/stylesheets/spotlight/_translations.scss +1 -0
  13. data/app/controllers/spotlight/appearances_controller.rb +1 -0
  14. data/app/controllers/spotlight/browse_controller.rb +1 -1
  15. data/app/controllers/spotlight/exhibits_controller.rb +2 -3
  16. data/app/controllers/spotlight/featured_images_controller.rb +1 -1
  17. data/app/controllers/spotlight/pages_controller.rb +1 -1
  18. data/app/controllers/spotlight/searches_controller.rb +3 -2
  19. data/app/controllers/spotlight/translations_controller.rb +46 -0
  20. data/app/helpers/spotlight/crop_helper.rb +4 -1
  21. data/app/models/concerns/spotlight/translatables.rb +17 -1
  22. data/app/models/spotlight/about_page.rb +3 -1
  23. data/app/models/spotlight/exhibit.rb +4 -2
  24. data/app/models/spotlight/feature_page.rb +3 -1
  25. data/app/models/spotlight/featured_image.rb +29 -12
  26. data/app/models/spotlight/home_page.rb +3 -1
  27. data/app/models/spotlight/main_navigation.rb +2 -2
  28. data/app/models/spotlight/masthead.rb +1 -1
  29. data/app/models/spotlight/page.rb +5 -1
  30. data/app/models/spotlight/search.rb +5 -1
  31. data/app/models/spotlight/solr_document_sidecar.rb +7 -5
  32. data/app/models/spotlight/temporary_image.rb +8 -0
  33. data/app/services/spotlight/exhibit_import_export_service.rb +454 -0
  34. data/app/views/catalog/_save_search.html.erb +1 -1
  35. data/app/views/spotlight/browse/_search.html.erb +3 -2
  36. data/app/views/spotlight/browse/_search_title.html.erb +2 -1
  37. data/app/views/spotlight/pages/_form.html.erb +1 -1
  38. data/app/views/spotlight/searches/_form.html.erb +1 -0
  39. data/app/views/spotlight/searches/_search.html.erb +1 -0
  40. data/app/views/spotlight/sir_trevor/blocks/_browse_block.html.erb +1 -0
  41. data/app/views/spotlight/sir_trevor/blocks/_uploaded_items_block.html.erb +7 -1
  42. data/app/views/spotlight/translations/_browse_categories.html.erb +29 -3
  43. data/app/views/spotlight/translations/_general.html.erb +7 -7
  44. data/app/views/spotlight/translations/_import.html.erb +24 -0
  45. data/app/views/spotlight/translations/_metadata.html.erb +1 -1
  46. data/app/views/spotlight/translations/_page.html.erb +5 -5
  47. data/app/views/spotlight/translations/_pages.html.erb +4 -4
  48. data/app/views/spotlight/translations/_pages_table.html.erb +5 -5
  49. data/app/views/spotlight/translations/_search_fields.html.erb +3 -3
  50. data/app/views/spotlight/translations/edit.html.erb +8 -6
  51. data/app/views/spotlight/translations/show.yaml.yamlbuilder +81 -0
  52. data/config/locales/spotlight.ar.yml +43 -24
  53. data/config/locales/spotlight.en.yml +65 -53
  54. data/config/routes.rb +6 -1
  55. data/db/migrate/20200403161512_add_subtitle_to_searches.rb +7 -0
  56. data/lib/spotlight/engine.rb +20 -0
  57. data/lib/spotlight/version.rb +1 -1
  58. data/spec/controllers/spotlight/home_pages_controller_spec.rb +1 -1
  59. data/spec/controllers/spotlight/translations_controller_spec.rb +53 -2
  60. data/spec/factories/featured_images.rb +4 -0
  61. data/spec/features/exhibits/translation_editing_spec.rb +2 -2
  62. data/spec/features/home_page_spec.rb +9 -0
  63. data/spec/features/javascript/blocks/uploaded_items_block_spec.rb +4 -1
  64. data/spec/i18n_spec.rb +0 -2
  65. data/spec/models/spotlight/exhibit_spec.rb +9 -1
  66. data/spec/models/spotlight/featured_image_spec.rb +28 -0
  67. data/spec/models/spotlight/page_spec.rb +6 -1
  68. data/spec/models/spotlight/resources/upload_spec.rb +43 -79
  69. data/spec/{serializers/spotlight/exhibit_export_serializer_spec.rb → services/spotlight/exhibit_import_export_service_spec.rb} +108 -22
  70. data/spec/views/spotlight/translations/_import.html.erb_spec.rb +24 -0
  71. metadata +14 -11
  72. data/app/serializers/spotlight/exhibit_export_serializer.rb +0 -205
  73. data/app/serializers/spotlight/featured_image_representer.rb +0 -29
  74. data/app/serializers/spotlight/main_navigation_representer.rb +0 -13
  75. data/app/serializers/spotlight/page_representer.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b0f44024854f3173fde8b7045255474d7abf905a6496fe3ee7a757475afde58d
4
- data.tar.gz: e0c77bc04b435ba48a16d27e0c780b0a5159514096cae198e18e4ee7f54484df
3
+ metadata.gz: 27efff1c809c87463bcd240ccc65b6edca2182b73238f1027bc178a2f009ab5e
4
+ data.tar.gz: 9df0f469fa0bc43f39a718d2cdb3bc7909e25da15a2e1d189ccc054b006eb579
5
5
  SHA512:
6
- metadata.gz: df80d3a4bfda7921d8b3aa337a1beb8871b0c992a58264015341c8fa8c50f1028c04402be37f5dfc589f6aa223a2c757ffc4092c7d2d10ee99b496fa5e100eea
7
- data.tar.gz: 56a480928bc9a99b67024323517581f91c5f2cd1a44c98f5d75bc8c03e2917b84a25074d5653f3efc1619327328682df414dcb90987509ff8351a559bb734270
6
+ metadata.gz: fc49fe51f1c599c5c3569d4387f7b2c1e124e9d8369a2b554ec26e441d8197f4f7e154cec7ab0b9c8fd3bdb8597315675da6d54b129df4774cdafbecfa339c9f
7
+ data.tar.gz: c06bc5cd170cf772c13bf220bb8e0ebb136b81fb7a575b6cbfe16704fe77c4cf42c759131f9fda803361b909537ba97e9e366e58f4ab99b68d4275408b3c7853
data/README.md CHANGED
@@ -9,7 +9,7 @@ Read more about what Spotlight is, our motivations for creating it, and how to i
9
9
 
10
10
  ## Requirements
11
11
 
12
- 1. Ruby (2.3.0 or greater)
12
+ 1. Ruby (2.5.0 or greater)
13
13
  2. Rails (5.1 or greater)
14
14
  3. Java (7 or greater) *for Solr*
15
15
  4. ImageMagick (http://www.imagemagick.org/script/index.php) due to [carrierwave](https://github.com/carrierwaveuploader/carrierwave#adding-versions)
@@ -93,7 +93,7 @@ Spotlight ships with [`i18n-tasks`](https://github.com/glebm/i18n-tasks) to help
93
93
  $ bundle exec i18n-tasks health
94
94
  ```
95
95
 
96
- See [developer-facing instructions for enabling translation](https://github.com/projectblacklight/spotlight/wiki/Translations) on the wiki.
96
+ See [developer-facing instructions for enabling translation](https://github.com/projectblacklight/spotlight/wiki/Translations) on the wiki.
97
97
 
98
98
  ## Community
99
99
 
@@ -10,7 +10,7 @@ SirTrevor.Blocks.Browse = (function(){
10
10
  autocomplete_url: function() {
11
11
  return $(this.inner).closest('form[data-autocomplete-exhibit-searches-path]').data('autocomplete-exhibit-searches-path').replace("%25QUERY", "%QUERY");
12
12
  },
13
- autocomplete_template: function() { return '<div class="autocomplete-item{{#unless published}} blacklight-private{{/unless}}">{{#if thumbnail_image_url}}<div class="document-thumbnail"><img class="img-thumbnail" src="{{thumbnail_image_url}}" /></div>{{/if}}<span class="autocomplete-title">{{title}}</span><br/><small>&nbsp;&nbsp;{{description}}</small></div>' },
13
+ autocomplete_template: function() { return '<div class="autocomplete-item{{#unless published}} blacklight-private{{/unless}}">{{#if thumbnail_image_url}}<div class="document-thumbnail"><img class="img-thumbnail" src="{{thumbnail_image_url}}" /></div>{{/if}}<span class="autocomplete-title">{{full_title}}</span><br/><small>&nbsp;&nbsp;{{description}}</small></div>' },
14
14
 
15
15
  bloodhoundOptions: function() {
16
16
  return {
@@ -21,6 +21,60 @@ SirTrevor.Blocks.Browse = (function(){
21
21
  };
22
22
  },
23
23
 
24
+ _itemPanel: function(data) {
25
+ var index = "item_" + this.globalIndex++;
26
+ var checked;
27
+ if (data.display == "true") {
28
+ checked = "checked='checked'"
29
+ } else {
30
+ checked = "";
31
+ }
32
+ var resource_id = data.slug || data.id;
33
+ var markup = [
34
+ '<li class="field form-inline dd-item dd3-item" data-resource-id="' + resource_id + '" data-id="' + index + '" id="' + this.formId("item_" + data.id) + '">',
35
+ '<input type="hidden" name="item[' + index + '][id]" value="' + resource_id + '" />',
36
+ '<input type="hidden" name="item[' + index + '][full_title]" value="' + (data.full_title || data.title) + '" />',
37
+ '<input data-property="weight" type="hidden" name="item[' + index + '][weight]" value="' + data.weight + '" />',
38
+ '<div class="card d-flex dd3-content">',
39
+ '<div class="dd-handle dd3-handle"><%= i18n.t("blocks:resources:panel:drag") %></div>',
40
+ '<div class="card-header item-grid">',
41
+ '<div class="d-flex">',
42
+ '<div class="checkbox">',
43
+ '<input name="item[' + index + '][display]" type="hidden" value="false" />',
44
+ '<input name="item[' + index + '][display]" id="'+ this.formId(this.display_checkbox + '_' + data.id) + '" type="checkbox" ' + checked + ' class="item-grid-checkbox" value="true" />',
45
+ '<label class="sr-only" for="'+ this.formId(this.display_checkbox + '_' + data.id) +'"><%= i18n.t("blocks:resources:panel:display") %></label>',
46
+ '</div>',
47
+ '<div class="pic">',
48
+ '<img class="img-thumbnail" src="' + (data.thumbnail_image_url || ((data.iiif_tilesource || "").replace("/info.json", "/full/!100,100/0/default.jpg"))) + '" />',
49
+ '</div>',
50
+ '<div class="main">',
51
+ '<div class="title card-title">' + (data.full_title || data.title) + '</div>',
52
+ '<div>' + (data.slug || data.id) + '</div>',
53
+ '</div>',
54
+ '<div class="remove float-right">',
55
+ '<a data-item-grid-panel-remove="true" href="#"><%= i18n.t("blocks:resources:panel:remove") %></a>',
56
+ '</div>',
57
+ '</div>',
58
+ '</div>',
59
+ '</div>',
60
+ '</li>'
61
+ ].join("\n");
62
+
63
+ var panel = $(_.template(markup)(this));
64
+ var context = this;
65
+
66
+ $('.remove a', panel).on('click', function(e) {
67
+ e.preventDefault();
68
+ $(this).closest('.field').remove();
69
+ context.afterPanelDelete();
70
+
71
+ });
72
+
73
+ this.afterPanelRender(data, panel);
74
+
75
+ return panel;
76
+ },
77
+
24
78
  item_options: function() { return [
25
79
  '<label>',
26
80
  '<input type="hidden" name="display-item-counts" value="false" />',
@@ -85,9 +85,13 @@ SirTrevor.Blocks.UploadedItems = (function(){
85
85
  '</div>',
86
86
  '<div class="main form-horizontal">',
87
87
  '<div class="title card-title">' + dataTitle + '</div>',
88
- '<div class="field">',
88
+ '<div class="field row mr-3">',
89
89
  '<label for="' + this.formId('caption_' + dataId) + '" class="col-form-label col-md-3"><%= i18n.t("blocks:uploaded_items:caption") %></label>',
90
- '<input type="text" class="form-control" id="' + this.formId('caption_' + dataId) + '" name="item[' + index + '][caption]" data-field="caption"/>',
90
+ '<input type="text" class="form-control col" id="' + this.formId('caption_' + dataId) + '" name="item[' + index + '][caption]" data-field="caption"/>',
91
+ '</div>',
92
+ '<div class="field row mr-3">',
93
+ '<label for="' + this.formId('link_' + dataId) + '" class="col-form-label col-md-3"><%= i18n.t("blocks:uploaded_items:link") %></label>',
94
+ '<input type="text" class="form-control col" id="' + this.formId('link_' + dataId) + '" name="item[' + index + '][link]" data-field="link"/>',
91
95
  '</div>',
92
96
  '</div>',
93
97
  '<div class="remove float-right">',
@@ -99,6 +103,7 @@ SirTrevor.Blocks.UploadedItems = (function(){
99
103
 
100
104
  var panel = $(_.template(markup)(this));
101
105
  panel.find('[data-field="caption"]').val(data.caption);
106
+ panel.find('[data-field="link"]').val(data.link);
102
107
  var context = this;
103
108
 
104
109
  $('.remove a', panel).on('click', function(e) {
@@ -269,6 +269,11 @@ export default class Crop {
269
269
 
270
270
  successHandler(data, stat, xhr) {
271
271
  this.setIiifFields({ tilesource: data.tilesource });
272
+ this.setUploadId(data.id);
273
+ }
274
+
275
+ setUploadId(id) {
276
+ $('#' + this.formPrefix + "_upload_id").val(id);
272
277
  }
273
278
 
274
279
  aspectRatioPreservingRectangleEditor(aspect) {
@@ -29,8 +29,9 @@ SirTrevor.Locales.en.blocks = $.extend(SirTrevor.Locales.en.blocks, {
29
29
 
30
30
  uploaded_items: {
31
31
  title: "Uploaded Item Row",
32
- description: "This widget displays uploaded items in a horizontal row. Optionally, you can add a heading and/or text to be displayed adjacent to the items.",
33
- caption: 'Caption'
32
+ description: "This widget displays uploaded items in a horizontal row. Optionally, you can add a heading and/or text to be displayed adjacent to the items. The item caption and link URL fields are also optional.",
33
+ caption: 'Caption',
34
+ link: 'Link URL'
34
35
  },
35
36
 
36
37
  featured_pages: {
@@ -2,7 +2,7 @@
2
2
 
3
3
  // Increase visibility of Firefox focus indicator
4
4
  @-moz-document url-prefix() {
5
- &:focus {
5
+ :focus {
6
6
  outline: $input-focus-border-color auto 5px;
7
7
  }
8
8
  }
@@ -26,6 +26,18 @@ $image-overlay-max-height: 300px;
26
26
  }
27
27
  }
28
28
 
29
+ .blacklight-browse-show {
30
+ .subtitle {
31
+ padding-right: $spacer;
32
+
33
+ &::before {
34
+ content: " \000B7 ";
35
+ color: $text-muted;
36
+ padding-right: $spacer / 2;
37
+ }
38
+ }
39
+ }
40
+
29
41
  .browse-landing {
30
42
  text-align: center;
31
43
  // Placeholder for vertically alignment - might already be available from use in another feature
@@ -70,6 +82,10 @@ $image-overlay-max-height: 300px;
70
82
  font-size: $h3-font-size;
71
83
  @extend %vertical-align;
72
84
 
85
+ .category-subtitle {
86
+ display: block;
87
+ }
88
+
73
89
  small {
74
90
  display: block;
75
91
  color: $white;
@@ -216,3 +216,9 @@ table.tags {
216
216
  text-align: center;
217
217
  }
218
218
  }
219
+
220
+ .card-title .subtitle::before {
221
+ content: " \000B7 ";
222
+ color: $text-muted;
223
+ padding-right: 3px;
224
+ }
@@ -55,6 +55,9 @@ $aspect-ratio-factor-medium-image: 1; // 1:1 width: height
55
55
  margin: 0;
56
56
  padding: $spacer / 4;
57
57
  }
58
+ .category-subtitle {
59
+ display: block;
60
+ }
58
61
  .item-count {
59
62
  font-size: $font-size-base;
60
63
  text-transform: uppercase;
@@ -79,7 +79,7 @@
79
79
  .image-masthead {
80
80
  background-color: $black;
81
81
 
82
- .navbar {
82
+ .exhibit-navbar {
83
83
  @extend .navbar-dark;
84
84
  background-color: $navbar-transparent-page-bg;
85
85
 
@@ -36,6 +36,7 @@
36
36
  }
37
37
  }
38
38
 
39
+ .translation-subtitle-toggle,
39
40
  .translation-description-toggle {
40
41
  margin: 0;
41
42
  padding: 0;
@@ -40,6 +40,7 @@ module Spotlight
40
40
  source
41
41
  image
42
42
  document_global_id
43
+ upload_id
43
44
  ]
44
45
  end
45
46
  end
@@ -69,7 +69,7 @@ module Spotlight
69
69
  end
70
70
 
71
71
  def attach_search_breadcrumb
72
- add_breadcrumb @search.title, exhibit_browse_path(@exhibit, @search)
72
+ add_breadcrumb @search.full_title, exhibit_browse_path(@exhibit, @search)
73
73
  end
74
74
 
75
75
  def _prefixes
@@ -27,8 +27,7 @@ module Spotlight
27
27
  end
28
28
 
29
29
  def process_import
30
- @exhibit.import(JSON.parse(import_exhibit_params.read))
31
- if @exhibit.save && @exhibit.reindex_later
30
+ if @exhibit.import(JSON.parse(import_exhibit_params.read)) && @exhibit.reindex_later
32
31
  redirect_to spotlight.exhibit_dashboard_path(@exhibit), notice: t(:'helpers.submit.exhibit.updated', model: @exhibit.class.model_name.human.downcase)
33
32
  else
34
33
  render action: :import
@@ -50,7 +49,7 @@ module Spotlight
50
49
  respond_to do |format|
51
50
  format.json do
52
51
  authorize! :export, @exhibit
53
- send_data JSON.pretty_generate(Spotlight::ExhibitExportSerializer.new(@exhibit).as_json),
52
+ send_data JSON.pretty_generate(Spotlight::ExhibitImportExportService.new(@exhibit).as_json),
54
53
  type: 'application/json',
55
54
  disposition: 'attachment',
56
55
  filename: "#{@exhibit.friendly_id}-export.json"
@@ -3,7 +3,7 @@
3
3
  module Spotlight
4
4
  # Handles requests to upload images for exhibit thumbnails
5
5
  class FeaturedImagesController < Spotlight::ApplicationController
6
- load_and_authorize_resource instance_name: :featured_image
6
+ load_and_authorize_resource instance_name: :featured_image, class: 'Spotlight::TemporaryImage'
7
7
 
8
8
  def create
9
9
  if @featured_image.save && @featured_image.file_present?
@@ -70,7 +70,7 @@ module Spotlight
70
70
  @page.lock&.delete
71
71
 
72
72
  if @page.update(page_params.merge(last_edited_by: current_user))
73
- redirect_to [spotlight, @page.exhibit, @page], flash: { html_safe: true }, notice: undo_notice(:updated)
73
+ redirect_to [spotlight, @page.exhibit, @page, format: params.permit(:format)], flash: { html_safe: true }, notice: undo_notice(:updated)
74
74
  else
75
75
  render action: 'edit'
76
76
  end
@@ -31,7 +31,7 @@ module Spotlight
31
31
  respond_to do |format|
32
32
  format.html
33
33
  format.json do
34
- render json: @searches.as_json(methods: %i[count thumbnail_image_url]), root: false
34
+ render json: @searches.as_json(methods: %i[full_title count thumbnail_image_url]), root: false
35
35
  end
36
36
  end
37
37
  end
@@ -50,7 +50,7 @@ module Spotlight
50
50
  end
51
51
 
52
52
  def edit
53
- add_breadcrumb @search.title, edit_exhibit_search_path(@search.exhibit, @search)
53
+ add_breadcrumb @search.full_title, edit_exhibit_search_path(@search.exhibit, @search)
54
54
  @exhibit = @search.exhibit
55
55
  end
56
56
 
@@ -105,6 +105,7 @@ module Spotlight
105
105
  def search_params
106
106
  params.require(:search).permit(
107
107
  :title,
108
+ :subtitle,
108
109
  :long_description,
109
110
  :search_box,
110
111
  :default_index_view_type,
@@ -21,6 +21,22 @@ module Spotlight
21
21
  end
22
22
  end
23
23
 
24
+ def show
25
+ respond_to do |format|
26
+ format.yaml
27
+ end
28
+ end
29
+
30
+ def import
31
+ if current_exhibit.update(import_exhibit_params)
32
+ I18n.reload! # reload since we're memoizing
33
+ notice = t(:'helpers.submit.spotlight_default.updated', model: current_exhibit.class.model_name.human.downcase)
34
+ redirect_to edit_exhibit_translations_path(current_exhibit, language: @language), notice: notice
35
+ else
36
+ render 'edit'
37
+ end
38
+ end
39
+
24
40
  private
25
41
 
26
42
  def attach_breadcrumbs
@@ -33,6 +49,21 @@ module Spotlight
33
49
  params.require(:exhibit).permit(translations_attributes: %i[id locale key value])
34
50
  end
35
51
 
52
+ def import_exhibit_params
53
+ imported_translations = YAML.safe_load(params.require(:file).read)
54
+
55
+ # set language from YML root locale
56
+ language = imported_translations.keys.first
57
+
58
+ # convert YML to hash
59
+ translation = unfold(imported_translations.values.first).map do |k, v|
60
+ current_translation = Translation.find_or_initialize_by(exhibit: current_exhibit, key: k, locale: language)
61
+ { key: k, value: v, locale: language, id: current_translation.id }
62
+ end
63
+
64
+ { translations_attributes: translation }
65
+ end
66
+
36
67
  def set_language
37
68
  @language = params[:language] || current_exhibit.available_locales.first
38
69
  end
@@ -40,5 +71,20 @@ module Spotlight
40
71
  def set_tab
41
72
  @tab = params[:tab]
42
73
  end
74
+
75
+ def unfold(value, key = nil)
76
+ return to_enum(:unfold, value, key) unless block_given?
77
+
78
+ if value.is_a? Hash
79
+ value.each do |k, v|
80
+ arr = unfold(v, [key, k].compact.join('.'))
81
+ arr.each do |k1, v1|
82
+ yield k1, v1
83
+ end
84
+ end
85
+ else
86
+ yield key, value
87
+ end
88
+ end
43
89
  end
44
90
  end
@@ -25,7 +25,10 @@ module Spotlight
25
25
  end
26
26
 
27
27
  def iiif_upload_tag(f)
28
- f.file_field_without_bootstrap :file, name: 'featured_image[image]', data: { endpoint: polymorphic_path(f.object.model_name.route_key) }
28
+ content_tag(:div) do
29
+ concat f.file_field_without_bootstrap :file, name: 'featured_image[image]', data: { endpoint: polymorphic_path(f.object.model_name.route_key) }
30
+ concat f.hidden_field :upload_id
31
+ end
29
32
  end
30
33
 
31
34
  def form_prefix(f)
@@ -15,8 +15,24 @@ module Spotlight
15
15
  # Set up a reader for the specified attribute that uses the I18n backend,
16
16
  # and defaults to the ActiveRecord value
17
17
  def define_translated_attr_reader(attr_name)
18
+ # Define a dynamic method for translating database-backed attributes,
19
+ # falling back to the database information as needed.
20
+ #
21
+ # Note: the empty string is provided as the final fallback to avoid i18n blowing
22
+ # up on nil attributes.
18
23
  define_method(:"#{attr_name}") do
19
- I18n.translate(attr_name, scope: slug, default: attr_translation(attr_name))
24
+ send("translated_#{attr_name}", default: [attr_translation(attr_name), ''])
25
+ end
26
+
27
+ # Define an accessor that gets the value of the attribute in a given locale,
28
+ # returning `nil` for untranslated values.
29
+ #
30
+ # Note: For the default locale, we actually want to dig into the database,
31
+ # because that is the source of truth for the data.
32
+ define_method(:"translated_#{attr_name}") do |default: [], **options|
33
+ default = Array.wrap(default)
34
+ default.prepend(attr_translation(attr_name)) if I18n.locale == I18n.default_locale
35
+ I18n.translate(attr_name, scope: slug, default: default, **options).presence
20
36
  end
21
37
  end
22
38
  end
@@ -5,6 +5,8 @@ module Spotlight
5
5
  # About pages
6
6
  class AboutPage < Spotlight::Page
7
7
  extend FriendlyId
8
- friendly_id :title, use: %i[slugged scoped finders history], scope: %i[exhibit locale]
8
+ friendly_id :title, use: %i[slugged scoped finders history], scope: %i[exhibit locale] do |config|
9
+ config.reserved_words.concat(%w[update_all contacts])
10
+ end
9
11
  end
10
12
  end
@@ -100,8 +100,10 @@ module Spotlight
100
100
  end
101
101
 
102
102
  def import(hash)
103
- Spotlight::ExhibitExportSerializer.prepare(self).from_hash(hash)
104
- self
103
+ ActiveRecord::Base.transaction do
104
+ Spotlight::ExhibitImportExportService.new(self).from_hash!(hash)
105
+ save
106
+ end
105
107
  end
106
108
 
107
109
  def solr_data