blacklight-spotlight 1.5.1 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -3
  3. data/app/assets/javascripts/spotlight/translation_progress.js +23 -0
  4. data/app/assets/stylesheets/spotlight/_spotlight.scss +2 -0
  5. data/app/assets/stylesheets/spotlight/_translations.scss +47 -0
  6. data/app/assets/stylesheets/spotlight/_utilities.scss +3 -0
  7. data/app/assets/stylesheets/spotlight/_variables.scss +5 -0
  8. data/app/controllers/concerns/spotlight/controller.rb +12 -1
  9. data/app/controllers/spotlight/about_pages_controller.rb +6 -0
  10. data/app/controllers/spotlight/browse_controller.rb +1 -1
  11. data/app/controllers/spotlight/catalog_controller.rb +6 -1
  12. data/app/controllers/spotlight/exhibits_controller.rb +10 -2
  13. data/app/controllers/spotlight/feature_pages_controller.rb +6 -0
  14. data/app/controllers/spotlight/filters_controller.rb +2 -2
  15. data/app/controllers/spotlight/home_pages_controller.rb +10 -0
  16. data/app/controllers/spotlight/languages_controller.rb +46 -0
  17. data/app/controllers/spotlight/pages_controller.rb +57 -3
  18. data/app/controllers/spotlight/solr_controller.rb +3 -3
  19. data/app/controllers/spotlight/translations_controller.rb +34 -0
  20. data/app/helpers/spotlight/application_helper.rb +1 -0
  21. data/app/helpers/spotlight/languages_helper.rb +33 -0
  22. data/app/helpers/spotlight/translations_helper.rb +13 -0
  23. data/app/models/concerns/spotlight/custom_translation_extension.rb +4 -0
  24. data/app/models/concerns/spotlight/translatables.rb +30 -0
  25. data/app/models/spotlight/ability.rb +5 -2
  26. data/app/models/spotlight/about_page.rb +1 -1
  27. data/app/models/spotlight/exhibit.rb +14 -6
  28. data/app/models/spotlight/feature_page.rb +1 -2
  29. data/app/models/spotlight/home_page.rb +1 -3
  30. data/app/models/spotlight/language.rb +24 -0
  31. data/app/models/spotlight/main_navigation.rb +12 -0
  32. data/app/models/spotlight/page.rb +53 -1
  33. data/app/models/spotlight/resources/upload.rb +6 -1
  34. data/app/models/spotlight/search.rb +4 -0
  35. data/app/services/spotlight/clone_translated_page_from_locale.rb +32 -0
  36. data/app/views/_user_util_links.html.erb +1 -0
  37. data/app/views/devise/mailer/invitation_instructions.html.erb +2 -2
  38. data/app/views/layouts/spotlight/spotlight.html.erb +2 -2
  39. data/app/views/shared/_curated_features_navbar.html.erb +2 -2
  40. data/app/views/spotlight/about_pages/_sidebar.html.erb +1 -1
  41. data/app/views/spotlight/admin_users/index.html.erb +1 -1
  42. data/app/views/spotlight/catalog/_edit_default.html.erb +1 -1
  43. data/app/views/spotlight/exhibits/_export.html.erb +1 -1
  44. data/app/views/spotlight/exhibits/_form.html.erb +1 -1
  45. data/app/views/spotlight/exhibits/_import.html.erb +2 -1
  46. data/app/views/spotlight/exhibits/_languages.html.erb +56 -0
  47. data/app/views/spotlight/exhibits/edit.html.erb +11 -5
  48. data/app/views/spotlight/feature_pages/_sidebar.html.erb +1 -1
  49. data/app/views/spotlight/filters/_form.html.erb +2 -1
  50. data/app/views/spotlight/resources/upload/_form.html.erb +1 -1
  51. data/app/views/spotlight/shared/_curation_sidebar.html.erb +5 -0
  52. data/app/views/spotlight/shared/_locale_picker.html.erb +14 -0
  53. data/app/views/spotlight/translations/_browse_categories.html.erb +67 -0
  54. data/app/views/spotlight/translations/_general.html.erb +176 -0
  55. data/app/views/spotlight/translations/_metadata.html.erb +67 -0
  56. data/app/views/spotlight/translations/_page.html.erb +45 -0
  57. data/app/views/spotlight/translations/_pages.html.erb +37 -0
  58. data/app/views/spotlight/translations/_pages_table.html.erb +15 -0
  59. data/app/views/spotlight/translations/_search_fields.html.erb +97 -0
  60. data/app/views/spotlight/translations/edit.html.erb +55 -0
  61. data/config/i18n-tasks.yml +81 -0
  62. data/config/locales/devise.en.yml +2 -1
  63. data/config/locales/spotlight.en.yml +657 -614
  64. data/config/routes.rb +19 -1
  65. data/db/migrate/20180308203409_create_spotlight_languages.rb +12 -0
  66. data/db/migrate/20180403130857_translation_uniqueness.rb +5 -0
  67. data/db/migrate/20180405044000_add_locale_to_pages.rb +6 -0
  68. data/db/migrate/20180406212516_add_default_locale_reference_to_spotlight_pages.rb +7 -0
  69. data/lib/generators/spotlight/templates/catalog_controller.rb +2 -2
  70. data/lib/generators/spotlight/templates/config/initializers/spotlight_initializer.rb +14 -4
  71. data/lib/generators/spotlight/templates/config/initializers/translation.rb +4 -2
  72. data/lib/migration/page_language.rb +27 -0
  73. data/lib/spotlight/engine.rb +32 -4
  74. data/lib/spotlight/upload_field_config.rb +27 -0
  75. data/lib/spotlight/version.rb +1 -1
  76. data/lib/tasks/spotlight_tasks.rake +6 -0
  77. data/spec/controllers/spotlight/about_pages_controller_spec.rb +67 -1
  78. data/spec/controllers/spotlight/feature_pages_controller_spec.rb +50 -0
  79. data/spec/controllers/spotlight/filters_controller_spec.rb +4 -4
  80. data/spec/controllers/spotlight/home_pages_controller_spec.rb +25 -0
  81. data/spec/controllers/spotlight/languages_controller_spec.rb +66 -0
  82. data/spec/controllers/spotlight/pages_controller_spec.rb +45 -0
  83. data/spec/controllers/spotlight/solr_controller_spec.rb +1 -1
  84. data/spec/controllers/spotlight/translations_controller_spec.rb +67 -0
  85. data/spec/examples.txt +1171 -1281
  86. data/spec/factories/exhibits.rb +1 -1
  87. data/spec/factories/language.rb +6 -0
  88. data/spec/factories/main_navigation.rb +7 -0
  89. data/spec/factories/searches.rb +1 -0
  90. data/spec/factories/tags.rb +1 -0
  91. data/spec/features/exhibits/language_create_edit_spec.rb +53 -0
  92. data/spec/features/exhibits/translation_editing_spec.rb +397 -0
  93. data/spec/features/javascript/locale_selector_spec.rb +61 -0
  94. data/spec/helpers/spotlight/languages_helper_spec.rb +73 -0
  95. data/spec/helpers/spotlight/translations_helper_spec.rb +16 -0
  96. data/spec/i18n_spec.rb +29 -0
  97. data/spec/lib/migration/page_language_spec.rb +21 -0
  98. data/spec/lib/spotlight/upload_field_config_spec.rb +24 -0
  99. data/spec/models/spotlight/ability_spec.rb +17 -4
  100. data/spec/models/spotlight/exhibit_spec.rb +49 -0
  101. data/spec/models/spotlight/language_spec.rb +41 -0
  102. data/spec/models/spotlight/page_spec.rb +109 -0
  103. data/spec/models/translation_spec.rb +11 -0
  104. data/spec/services/spotlight/clone_translated_page_from_locale_spec.rb +29 -0
  105. data/spec/spec_helper.rb +1 -13
  106. data/spec/support/disable_friendly_id_deprecation_warnings.rb +7 -6
  107. data/spec/test_app_templates/Gemfile.extra +3 -0
  108. data/spec/test_app_templates/catalog_controller.rb +31 -31
  109. data/spec/test_app_templates/lib/generators/test_app_generator.rb +9 -6
  110. data/spec/views/spotlight/contacts/edit.html.erb_spec.rb +13 -0
  111. data/spec/views/spotlight/exhibits/_form.html.erb_spec.rb +33 -0
  112. data/spec/views/spotlight/exhibits/edit.html.erb_spec.rb +4 -1
  113. data/spec/views/spotlight/resources/_external_resources_form.html.erb_spec.rb +13 -0
  114. data/spec/views/spotlight/resources/new.html.erb_spec.rb +10 -4
  115. data/spec/views/spotlight/translations/_page.html.erb_spec.rb +75 -0
  116. metadata +88 -28
  117. data/app/assets/javascripts/spotlight/sir-trevor.js +0 -0
  118. data/app/views/shared/_share_follow.html.erb +0 -5
  119. data/config/locales/social_share_button.en.yml +0 -18
@@ -21,10 +21,10 @@ module Spotlight
21
21
 
22
22
  repository.connection.update params: { commitWithin: 500 }, data: data.to_json, headers: { 'Content-Type' => 'application/json' } unless data.empty?
23
23
 
24
- if respond_to? :head
25
- head :ok
24
+ if params[:resources_json_upload]
25
+ redirect_back fallback_location: exhibit_resources_path(@exhibit)
26
26
  else
27
- render nothing: true
27
+ head :ok
28
28
  end
29
29
  end
30
30
 
@@ -0,0 +1,34 @@
1
+ module Spotlight
2
+ ##
3
+ # Base CRUD controller for translations
4
+ class TranslationsController < Spotlight::ApplicationController
5
+ before_action :authenticate_user!, :set_language, :set_tab
6
+ load_and_authorize_resource :exhibit, class: Spotlight::Exhibit
7
+
8
+ def edit; end
9
+
10
+ def update
11
+ if current_exhibit.update(exhibit_params)
12
+ I18n.reload! # reload since we're memoizing
13
+ notice = t(:'helpers.submit.spotlight_default.updated', model: current_exhibit.class.model_name.human.downcase)
14
+ redirect_to edit_exhibit_translations_path(current_exhibit, language: @language, tab: @tab), notice: notice
15
+ else
16
+ render 'edit'
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def exhibit_params
23
+ params.require(:exhibit).permit(translations_attributes: [:id, :locale, :key, :value])
24
+ end
25
+
26
+ def set_language
27
+ @language = params[:language] || current_exhibit.available_locales.first
28
+ end
29
+
30
+ def set_tab
31
+ @tab = params[:tab]
32
+ end
33
+ end
34
+ end
@@ -6,6 +6,7 @@ module Spotlight
6
6
  include TitleHelper
7
7
  include MetaHelper
8
8
  include CropHelper
9
+ include LanguagesHelper
9
10
 
10
11
  ##
11
12
  # Give the application name a chance to include the exhibit title
@@ -0,0 +1,33 @@
1
+ module Spotlight
2
+ ##
3
+ # Helpers for the Language form and UI elements
4
+ module LanguagesHelper
5
+ def add_exhibit_language_dropdown_options
6
+ non_default_or_current_exhibit_languages = I18n.available_locales.reject do |locale|
7
+ locale == I18n.default_locale || current_exhibit.available_locales.include?(locale.to_s)
8
+ end
9
+
10
+ non_default_or_current_exhibit_languages_with_labels = non_default_or_current_exhibit_languages.map do |locale|
11
+ [t("locales.#{locale.downcase}"), locale]
12
+ end
13
+
14
+ non_default_or_current_exhibit_languages_with_labels.sort_by { |label, _locale| label }
15
+ end
16
+
17
+ def locale_selecter_dropown_options
18
+ languages = current_exhibit.languages.accessible_by(current_ability).to_a << Spotlight::Language.default_instance
19
+
20
+ # String#casecmp returns 0 when the two strings compared are identical (ignoring case)
21
+ languages.reject { |language| language.locale.to_s.casecmp(I18n.locale.to_s).zero? }.sort_by(&:to_native)
22
+ end
23
+
24
+ ##
25
+ # Can determine whether the current page is using the application's default
26
+ # locale
27
+ # @return [Boolean]
28
+ def default_language?
29
+ return true unless params[:locale]
30
+ params[:locale].to_s == I18n.default_locale.to_s
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ module Spotlight
2
+ ##
3
+ # Helper module for the Translations admin UI
4
+ module TranslationsHelper
5
+ def non_custom_metadata_fields
6
+ custom_field_keys = current_exhibit.custom_fields.pluck(:field)
7
+
8
+ current_exhibit.blacklight_config.show_fields.reject do |key, _|
9
+ custom_field_keys.include?(key)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -8,6 +8,10 @@ module Spotlight
8
8
  included do
9
9
  default_scope { where(exhibit: current_exhibit) }
10
10
  belongs_to :exhibit, class_name: 'Spotlight::Exhibit', inverse_of: :translations
11
+
12
+ before_validation do
13
+ mark_for_destruction if value.blank?
14
+ end
11
15
  end
12
16
 
13
17
  class_methods do
@@ -0,0 +1,30 @@
1
+ module Spotlight
2
+ # Mixin for adding translatable ActiveRecord accessors
3
+ module Translatables
4
+ extend ActiveSupport::Concern
5
+
6
+ class_methods do
7
+ def translates(*attr_names)
8
+ attr_names.map(&:to_sym)
9
+ attr_names.map(&method(:define_translated_attr_reader))
10
+ end
11
+
12
+ ##
13
+ # Set up a reader for the specified attribute that uses the I18n backend,
14
+ # and defaults to the ActiveRecord value
15
+ def define_translated_attr_reader(attr_name)
16
+ define_method(:"#{attr_name}") do
17
+ I18n.translate(attr_name, scope: slug, default: attr_translation(attr_name))
18
+ end
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ ##
25
+ # Will return the default ActiveRecord value for the value
26
+ def attr_translation(attr_name)
27
+ self[attr_name]
28
+ end
29
+ end
30
+ end
@@ -14,7 +14,7 @@ module Spotlight
14
14
 
15
15
  # exhibit admin
16
16
  can [:update, :import, :export, :destroy], Spotlight::Exhibit, id: user.admin_roles.pluck(:resource_id)
17
- can :manage, [Spotlight::BlacklightConfiguration, Spotlight::ContactEmail], exhibit_id: user.admin_roles.pluck(:resource_id)
17
+ can :manage, [Spotlight::BlacklightConfiguration, Spotlight::ContactEmail, Spotlight::Language], exhibit_id: user.admin_roles.pluck(:resource_id)
18
18
  can :manage, Spotlight::Role, resource_id: user.admin_roles.pluck(:resource_id), resource_type: 'Spotlight::Exhibit'
19
19
 
20
20
  can :manage, PaperTrail::Version if user.roles.any?
@@ -26,11 +26,13 @@ module Spotlight
26
26
  Spotlight::Resource,
27
27
  Spotlight::Page,
28
28
  Spotlight::Contact,
29
- Spotlight::CustomField
29
+ Spotlight::CustomField,
30
+ Translation
30
31
  ], exhibit_id: user.exhibit_roles.pluck(:resource_id)
31
32
 
32
33
  can :manage, Spotlight::Lock, by: user
33
34
 
35
+ can :read, Spotlight::Language, exhibit_id: user.exhibit_roles.pluck(:resource_id)
34
36
  can [:read, :curate, :tag], Spotlight::Exhibit, id: user.exhibit_roles.pluck(:resource_id)
35
37
 
36
38
  # public
@@ -38,6 +40,7 @@ module Spotlight
38
40
  can :read, Spotlight::Exhibit, published: true
39
41
  can :read, Spotlight::Page, published: true
40
42
  can :read, Spotlight::Search, published: true
43
+ can :read, Spotlight::Language, public: true
41
44
  end
42
45
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
43
46
  end
@@ -3,6 +3,6 @@ module Spotlight
3
3
  # About pages
4
4
  class AboutPage < Spotlight::Page
5
5
  extend FriendlyId
6
- friendly_id :title, use: [:slugged, :scoped, :finders, :history], scope: :exhibit
6
+ friendly_id :title, use: [:slugged, :scoped, :finders, :history], scope: [:exhibit, :locale]
7
7
  end
8
8
  end
@@ -6,6 +6,9 @@ module Spotlight
6
6
  include Spotlight::ExhibitAnalytics
7
7
  include Spotlight::ExhibitDefaults
8
8
  include Spotlight::ExhibitDocuments
9
+ include Spotlight::Translatables
10
+
11
+ translates :title, :subtitle, :description
9
12
 
10
13
  has_paper_trail
11
14
 
@@ -17,7 +20,7 @@ module Spotlight
17
20
 
18
21
  extend FriendlyId
19
22
  friendly_id :title, use: [:slugged, :finders]
20
- validates :title, presence: true
23
+ validates :title, presence: true, if: -> { I18n.locale == I18n.default_locale }
21
24
  validates :slug, uniqueness: true
22
25
  validates :theme, inclusion: { in: Spotlight::Engine.config.exhibit_themes }, allow_blank: true
23
26
 
@@ -27,12 +30,12 @@ module Spotlight
27
30
  serialize :facets, Array
28
31
 
29
32
  # Note: friendly id associations need to be 'destroy'ed to reap the slug history
30
- has_many :about_pages, extend: FriendlyId::FinderMethods
33
+ has_many :about_pages, -> { for_default_locale }, extend: FriendlyId::FinderMethods
31
34
  has_many :attachments, dependent: :destroy
32
35
  has_many :contact_emails, dependent: :delete_all # These are the contacts who get "Contact us" emails
33
36
  has_many :contacts, dependent: :delete_all # These are the contacts who appear in the sidebar
34
37
  has_many :custom_fields, dependent: :delete_all
35
- has_many :feature_pages, extend: FriendlyId::FinderMethods
38
+ has_many :feature_pages, -> { for_default_locale }, extend: FriendlyId::FinderMethods
36
39
  has_many :main_navigations, dependent: :delete_all
37
40
  has_many :reindexing_log_entries, dependent: :destroy
38
41
  has_many :resources
@@ -43,16 +46,17 @@ module Spotlight
43
46
  has_many :pages, dependent: :destroy
44
47
  has_many :filters, dependent: :delete_all
45
48
  has_many :translations, class_name: 'I18n::Backend::ActiveRecord::Translation', dependent: :destroy, inverse_of: :exhibit
49
+ has_many :languages, dependent: :destroy
46
50
 
47
51
  has_one :blacklight_configuration, class_name: 'Spotlight::BlacklightConfiguration', dependent: :delete
48
- has_one :home_page
52
+ has_one :home_page, -> { for_default_locale }
49
53
 
50
54
  belongs_to :site, optional: true
51
55
  belongs_to :masthead, dependent: :destroy, optional: true
52
56
  belongs_to :thumbnail, class_name: 'Spotlight::ExhibitThumbnail', dependent: :destroy, optional: true
53
57
 
54
- accepts_nested_attributes_for :about_pages, :attachments, :contacts, :custom_fields, :feature_pages,
55
- :main_navigations, :owned_taggings, :resources, :searches, :solr_document_sidecars
58
+ accepts_nested_attributes_for :about_pages, :attachments, :contacts, :custom_fields, :feature_pages, :languages,
59
+ :main_navigations, :owned_taggings, :pages, :resources, :searches, :solr_document_sidecars, :translations
56
60
  accepts_nested_attributes_for :blacklight_configuration, :home_page, :filters, update_only: true
57
61
  accepts_nested_attributes_for :masthead, :thumbnail, update_only: true, reject_if: proc { |attr| attr['iiif_tilesource'].blank? }
58
62
  accepts_nested_attributes_for :contact_emails, reject_if: proc { |attr| attr['email'].blank? }
@@ -103,6 +107,10 @@ module Spotlight
103
107
  @reindex_progress ||= ReindexProgress.new(current_reindexing_log_entry)
104
108
  end
105
109
 
110
+ def available_locales
111
+ @available_locales ||= languages.pluck(:locale)
112
+ end
113
+
106
114
  protected
107
115
 
108
116
  def sanitize_description
@@ -3,7 +3,7 @@ module Spotlight
3
3
  # Feature pages
4
4
  class FeaturePage < Spotlight::Page
5
5
  extend FriendlyId
6
- friendly_id :title, use: [:slugged, :scoped, :finders, :history], scope: :exhibit
6
+ friendly_id :title, use: [:slugged, :scoped, :finders, :history], scope: [:exhibit, :locale]
7
7
 
8
8
  has_many :child_pages, class_name: 'Spotlight::FeaturePage', inverse_of: :parent_page, foreign_key: 'parent_page_id'
9
9
  belongs_to :parent_page, class_name: 'Spotlight::FeaturePage', optional: true
@@ -11,7 +11,6 @@ module Spotlight
11
11
  accepts_nested_attributes_for :child_pages
12
12
 
13
13
  belongs_to :thumbnail, class_name: 'Spotlight::FeaturedImage', dependent: :destroy, optional: true
14
- accepts_nested_attributes_for :thumbnail, update_only: true, reject_if: proc { |attr| attr['iiif_tilesource'].blank? }
15
14
 
16
15
  before_validation unless: :top_level_page? do
17
16
  self.exhibit = top_level_page_or_self.exhibit
@@ -3,13 +3,11 @@ module Spotlight
3
3
  # Exhibit home page
4
4
  class HomePage < Spotlight::Page
5
5
  extend FriendlyId
6
- friendly_id :title, use: [:slugged, :scoped, :finders], scope: :exhibit
6
+ friendly_id :title, use: [:slugged, :scoped, :finders], scope: [:exhibit, :locale]
7
7
 
8
8
  before_save :publish
9
9
  before_create :default_content
10
10
 
11
- accepts_nested_attributes_for :thumbnail, update_only: true, reject_if: proc { |attr| attr['iiif_tilesource'].blank? }
12
-
13
11
  class << self
14
12
  def default_title_text
15
13
  I18n.t('spotlight.pages.index.home_pages.title')
@@ -0,0 +1,24 @@
1
+ module Spotlight
2
+ # A language for an exhibit
3
+ class Language < ActiveRecord::Base
4
+ belongs_to :exhibit
5
+ has_many :pages, ->(page) { where(locale: page.locale) }, through: :exhibit
6
+ has_many :translations, ->(translation) { where(locale: translation.locale) }, through: :exhibit
7
+ validates :locale, presence: true
8
+
9
+ # Doing this instead of dependent: :destroy because
10
+ # has_many :through can't associate a has_many reflection
11
+ after_destroy do
12
+ pages.map(&:destroy)
13
+ translations.map(&:destroy)
14
+ end
15
+
16
+ def to_native
17
+ Spotlight::Engine.config.i18n_locales[locale.downcase.to_sym] || ''
18
+ end
19
+
20
+ def self.default_instance
21
+ new(locale: I18n.default_locale)
22
+ end
23
+ end
24
+ end
@@ -2,11 +2,15 @@ module Spotlight
2
2
  ##
3
3
  # Exhibit navbar links
4
4
  class MainNavigation < ActiveRecord::Base
5
+ include Spotlight::Translatables
6
+
5
7
  belongs_to :exhibit, touch: true
6
8
  default_scope { order('weight ASC') }
7
9
  scope :browse, -> { find_by(nav_type: 'browse') }
8
10
  scope :about, -> { find_by(nav_type: 'about') }
11
+ scope :curated_features, -> { find_by(nav_type: 'curated_features') }
9
12
  scope :displayable, -> { where(display: true) }
13
+ translates :label
10
14
 
11
15
  def displayable?
12
16
  display?
@@ -23,5 +27,13 @@ module Spotlight
23
27
  def default_label
24
28
  I18n.t(:"spotlight.main_navigation.#{nav_type}")
25
29
  end
30
+
31
+ private
32
+
33
+ ##
34
+ # Allows us to scope translations namespace.
35
+ def slug
36
+ ['main_navigation', nav_type].join('.')
37
+ end
26
38
  end
27
39
  end
@@ -1,16 +1,23 @@
1
1
  module Spotlight
2
2
  ##
3
3
  # Base page class. See {Spotlight::AboutPage}, {Spotlight::FeaturePage}, {Spotlight::HomePage}
4
+ # rubocop:disable Metrics/ClassLength
4
5
  class Page < ActiveRecord::Base
5
6
  MAX_PAGES = 50
6
7
 
7
8
  extend FriendlyId
8
- friendly_id :title, use: [:slugged, :scoped, :finders, :history], scope: :exhibit
9
+ friendly_id :title, use: [:slugged, :scoped, :finders, :history], scope: [:exhibit, :locale]
9
10
 
10
11
  belongs_to :exhibit, touch: true
11
12
  belongs_to :created_by, class_name: Spotlight::Engine.config.user_class, optional: true
12
13
  belongs_to :last_edited_by, class_name: Spotlight::Engine.config.user_class, optional: true
13
14
  belongs_to :thumbnail, class_name: 'Spotlight::FeaturedImage', dependent: :destroy, optional: true
15
+ belongs_to :default_locale_page, class_name: 'Spotlight::Page', optional: true, inverse_of: :translated_pages
16
+ has_many :translated_pages,
17
+ class_name: 'Spotlight::Page',
18
+ foreign_key: :default_locale_page_id,
19
+ dependent: :destroy,
20
+ inverse_of: :default_locale_page
14
21
 
15
22
  validates :weight, inclusion: { in: proc { 0..Spotlight::Page::MAX_PAGES } }
16
23
 
@@ -18,17 +25,23 @@ module Spotlight
18
25
  scope :at_top_level, -> { where(parent_page_id: nil) }
19
26
  scope :published, -> { where(published: true) }
20
27
  scope :recent, -> { order('updated_at DESC').limit(10) }
28
+ scope :for_locale, ->(locale = I18n.locale) { unscope(where: :locale).where(locale: locale) }
29
+ scope :for_default_locale, -> { for_locale(I18n.default_locale) }
21
30
 
22
31
  has_one :lock, as: :on, dependent: :destroy
23
32
  sir_trevor_content :content
24
33
  has_paper_trail
25
34
 
35
+ accepts_nested_attributes_for :thumbnail, update_only: true, reject_if: proc { |attr| attr['iiif_tilesource'].blank? }
36
+
26
37
  # display_sidebar should be set to true by default
27
38
  before_create do
28
39
  self.content ||= [].to_json
29
40
  self.display_sidebar = true
30
41
  end
31
42
 
43
+ after_update :update_translated_pages_weights_and_parent_page
44
+
32
45
  def content_changed!
33
46
  @content = nil
34
47
  end
@@ -74,6 +87,10 @@ module Spotlight
74
87
  is_a? AboutPage
75
88
  end
76
89
 
90
+ def home_page?
91
+ is_a? HomePage
92
+ end
93
+
77
94
  def top_level_page?
78
95
  try(:parent_page).blank?
79
96
  end
@@ -93,5 +110,40 @@ module Spotlight
93
110
  def lock!(user)
94
111
  create_lock(by: user).tap(&:current_session!) unless lock.present?
95
112
  end
113
+
114
+ def updated_after?(other_page)
115
+ return false unless other_page
116
+ updated_at > other_page.updated_at
117
+ end
118
+
119
+ def translated_page_for(locale)
120
+ translated_pages.for_locale(locale).first
121
+ end
122
+
123
+ def clone_for_locale(locale)
124
+ dup.tap do |np|
125
+ np.locale = locale
126
+ np.default_locale_page = self
127
+ np.published = false
128
+
129
+ if !top_level_page? && (parent_translation = parent_page.translated_page_for(locale)).present?
130
+ np.parent_page = parent_translation
131
+ end
132
+
133
+ child_pages.for_locale(locale).update(parent_page: np) if top_level_page? && respond_to?(:child_pages)
134
+ end
135
+ end
136
+
137
+ private
138
+
139
+ def update_translated_pages_weights_and_parent_page
140
+ return unless locale.to_sym == I18n.default_locale
141
+ return unless saved_change_to_weight? || saved_change_to_parent_page_id?
142
+ update_params = {}
143
+ update_params[:weight] = weight if saved_change_to_weight?
144
+ update_params[:parent_page_id] = parent_page_id if saved_change_to_parent_page_id?
145
+ translated_pages.update(update_params)
146
+ end
96
147
  end
148
+ # rubocop:enable Metrics/ClassLength
97
149
  end