blacklight-spotlight 0.14.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/spotlight/default_thumbnail.jpg +0 -0
  3. data/app/controllers/concerns/spotlight/catalog/access_controls_enforcement.rb +1 -1
  4. data/app/controllers/spotlight/exhibits_controller.rb +1 -0
  5. data/app/controllers/spotlight/filters_controller.rb +28 -0
  6. data/app/controllers/spotlight/solr_controller.rb +10 -0
  7. data/app/models/concerns/spotlight/exhibit_defaults.rb +59 -0
  8. data/app/models/concerns/spotlight/solr_document/atomic_updates.rb +5 -0
  9. data/app/models/spotlight/exhibit.rb +7 -32
  10. data/app/models/spotlight/filter.rb +26 -0
  11. data/app/models/spotlight/resource.rb +2 -0
  12. data/app/models/spotlight/search.rb +4 -1
  13. data/app/views/spotlight/exhibits/_exhibit_card.html.erb +2 -25
  14. data/app/views/spotlight/exhibits/_exhibit_card_back.html.erb +23 -0
  15. data/app/views/spotlight/exhibits/_exhibit_card_front.html.erb +11 -0
  16. data/app/views/spotlight/exhibits/_exhibits.html.erb +5 -0
  17. data/app/views/spotlight/exhibits/_form.html.erb +2 -1
  18. data/app/views/spotlight/exhibits/_new_exhibit_form.html.erb +2 -1
  19. data/app/views/spotlight/exhibits/edit.html.erb +6 -0
  20. data/app/views/spotlight/exhibits/index.html.erb +4 -16
  21. data/app/views/spotlight/filters/_form.html.erb +13 -0
  22. data/config/locales/spotlight.en.yml +11 -0
  23. data/config/routes.rb +2 -0
  24. data/db/migrate/20151217211019_create_spotlight_exhibit_filters.rb +17 -0
  25. data/lib/spotlight/engine.rb +9 -5
  26. data/lib/spotlight/version.rb +1 -1
  27. data/lib/tasks/spotlight_tasks.rake +3 -3
  28. data/spec/controllers/spotlight/exhibits_controller_spec.rb +2 -1
  29. data/spec/controllers/spotlight/filters_controller_spec.rb +71 -0
  30. data/spec/controllers/spotlight/solr_controller_spec.rb +13 -1
  31. data/spec/lib/spotlight/catalog/access_controls_enforcement_spec.rb +18 -0
  32. data/spec/models/spotlight/exhibit_spec.rb +34 -3
  33. data/spec/models/spotlight/filter_spec.rb +19 -0
  34. data/spec/models/spotlight/resource_spec.rb +11 -0
  35. data/spec/models/spotlight/search_spec.rb +11 -0
  36. data/spec/models/spotlight/solr_document/atomic_updates_spec.rb +11 -0
  37. data/spec/views/spotlight/exhibits/_exhibit_card_front.html.erb_spec.rb +42 -0
  38. data/spec/views/spotlight/exhibits/edit.html.erb_spec.rb +1 -0
  39. metadata +17 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b22ac299ef75ed65fdbf1db182d8ffdd7c0fb955
4
- data.tar.gz: 2a309cdf19b962204d101ccb949ee9dc3a9ea5f7
3
+ metadata.gz: 2b45039dfeb64f44d24665c3b2fbabef264eca86
4
+ data.tar.gz: 8d3f3015e3b61bbd5d185903b82af1c17a3c302a
5
5
  SHA512:
6
- metadata.gz: 44cf9fb4a4c7550722d1b3ff8d3d9f5aaa69e515089b55739899cea0fafe93d33f0f413cafa61480dd221d2c60b3d1097fcf316b319ba05122f37a7970d04fb4
7
- data.tar.gz: 9285521d65457583d865917083633faefc00dd9413120ee6e4182a655964f8fa13cada3f1d1975f404765ee20157b0eb17b39c1165b670ae97e4376d72c1d7d0
6
+ metadata.gz: 2d146ecf16f66315c47f81ef1e1f7a5a911cc0cb9989955a30bb64707e2a9a4b762b849578481ff0021b78d44998382265718f1c13e9af92e5a6a63482a23891
7
+ data.tar.gz: 14e5e9cb7329342bc6c8ed68cdd2bfae3e22f8e0efcc66d1c4297a26bb7b49a88924ba7f6a6ad0b51f4e37c05292354cc9ccfb12ffc427a2b03a769b2c59efa0
@@ -27,7 +27,7 @@ module Spotlight
27
27
  end
28
28
 
29
29
  def apply_exhibit_resources_filter(solr_params)
30
- return unless Spotlight::Engine.config.filter_resources_by_exhibit && current_exhibit
30
+ return unless current_exhibit
31
31
 
32
32
  current_exhibit.solr_data.each do |facet_field, values|
33
33
  Array(values).each do |value|
@@ -85,6 +85,7 @@ module Spotlight
85
85
  :subtitle,
86
86
  :description,
87
87
  :published,
88
+ :tag_list,
88
89
  contact_emails_attributes: [:id, :email]
89
90
  )
90
91
  end
@@ -0,0 +1,28 @@
1
+ module Spotlight
2
+ # Create and update filters for an exhibit
3
+ class FiltersController < ApplicationController
4
+ before_action :authenticate_user!
5
+ load_and_authorize_resource :exhibit, class: 'Spotlight::Exhibit'
6
+ load_and_authorize_resource through: :exhibit
7
+
8
+ def create
9
+ if @filter.save
10
+ flash[:notice] = t('helpers.submit.filter.updated', model: @filter.model_name.human)
11
+ else
12
+ flash[:alert] = @filter.errors.full_messages.join('<br/>'.html_safe)
13
+ end
14
+ redirect_to spotlight.edit_exhibit_path @exhibit, anchor: 'filter'
15
+ end
16
+
17
+ def update
18
+ unless @filter.update(filter_params)
19
+ flash[:alert] = @filter.errors.full_messages.join('<br/>'.html_safe)
20
+ end
21
+ redirect_to spotlight.edit_exhibit_path @exhibit, anchor: 'filter'
22
+ end
23
+
24
+ def filter_params
25
+ params.require(:filter).permit(:field, :value)
26
+ end
27
+ end
28
+ end
@@ -7,6 +7,8 @@ module Spotlight
7
7
  # workflows with exhibit-specific content
8
8
  class SolrController < Spotlight::ApplicationController
9
9
  before_action :authenticate_user!
10
+ before_action :validate_writable_index!
11
+
10
12
  load_and_authorize_resource :exhibit, class: Spotlight::Exhibit
11
13
 
12
14
  def update
@@ -22,5 +24,13 @@ module Spotlight
22
24
 
23
25
  render nothing: true
24
26
  end
27
+
28
+ private
29
+
30
+ def validate_writable_index!
31
+ return if Spotlight::Engine.config.writable_index
32
+
33
+ render text: 'Spotlight is unable to write to solr', status: 409
34
+ end
25
35
  end
26
36
  end
@@ -0,0 +1,59 @@
1
+ module Spotlight
2
+ # Mixin for adding default configuration to exhibits
3
+ module ExhibitDefaults
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_create :build_home_page
8
+ before_create :add_site_reference
9
+ after_create :initialize_config
10
+ after_create :initialize_browse
11
+ after_create :initialize_main_navigation
12
+ after_create :initialize_filter
13
+ end
14
+
15
+ protected
16
+
17
+ def initialize_filter
18
+ return unless Spotlight::Engine.config.filter_resources_by_exhibit
19
+
20
+ filters.create field: default_filter_field, value: default_filter_value
21
+ end
22
+
23
+ def initialize_config
24
+ self.blacklight_configuration ||= Spotlight::BlacklightConfiguration.create!
25
+ end
26
+
27
+ def initialize_browse
28
+ return unless searches.blank?
29
+
30
+ searches.create title: 'All Exhibit Items',
31
+ long_description: 'All items in this exhibit.'
32
+ end
33
+
34
+ def initialize_main_navigation
35
+ default_main_navigations.each_with_index do |nav_type, weight|
36
+ main_navigations.create nav_type: nav_type, weight: weight
37
+ end
38
+ end
39
+
40
+ def add_site_reference
41
+ self.site ||= Spotlight::Site.instance
42
+ end
43
+
44
+ def default_filter_field
45
+ "#{Spotlight::Engine.config.solr_fields.prefix}spotlight_exhibit_slug_#{slug}#{Spotlight::Engine.config.solr_fields.boolean_suffix}"
46
+ end
47
+
48
+ # Return a string to work around any ActiveRecord type-casting
49
+ def default_filter_value
50
+ 'true'
51
+ end
52
+
53
+ private
54
+
55
+ def default_main_navigations
56
+ Spotlight::Engine.config.exhibit_main_navigation.dup
57
+ end
58
+ end
59
+ end
@@ -4,11 +4,16 @@ module Spotlight
4
4
  # Solr indexing strategy using Solr's Atomic Updates
5
5
  module AtomicUpdates
6
6
  def reindex
7
+ return unless write?
7
8
  data = hash_for_solr_update(to_solr)
8
9
 
9
10
  blacklight_solr.update params: { commitWithin: 500 }, data: data.to_json, headers: { 'Content-Type' => 'application/json' } unless data.empty?
10
11
  end
11
12
 
13
+ def write?
14
+ Spotlight::Engine.config.writable_index
15
+ end
16
+
12
17
  private
13
18
 
14
19
  def hash_for_solr_update(data)
@@ -4,6 +4,7 @@ module Spotlight
4
4
  # Spotlight exhibit
5
5
  class Exhibit < ActiveRecord::Base
6
6
  include Spotlight::ExhibitAnalytics
7
+ include Spotlight::ExhibitDefaults
7
8
  include Spotlight::ExhibitDocuments
8
9
 
9
10
  scope :published, -> { where(published: true) }
@@ -17,6 +18,7 @@ module Spotlight
17
18
  default_scope { order('weight ASC') }
18
19
 
19
20
  acts_as_tagger
21
+ acts_as_taggable
20
22
  delegate :blacklight_config, to: :blacklight_configuration
21
23
  serialize :facets, Array
22
24
 
@@ -35,6 +37,7 @@ module Spotlight
35
37
  has_many :solr_document_sidecars, dependent: :delete_all
36
38
  has_many :users, through: :roles, class_name: Spotlight::Engine.config.user_class
37
39
  has_many :pages, dependent: :destroy
40
+ has_many :filters, dependent: :delete_all
38
41
 
39
42
  has_one :blacklight_configuration, class_name: 'Spotlight::BlacklightConfiguration', dependent: :delete
40
43
  has_one :home_page
@@ -45,16 +48,11 @@ module Spotlight
45
48
 
46
49
  accepts_nested_attributes_for :about_pages, :attachments, :contacts, :custom_fields, :feature_pages,
47
50
  :main_navigations, :owned_taggings, :resources, :searches, :solr_document_sidecars
48
- accepts_nested_attributes_for :blacklight_configuration, :home_page, :masthead, :thumbnail, update_only: true
51
+ accepts_nested_attributes_for :blacklight_configuration, :home_page, :masthead, :thumbnail, :filters, update_only: true
49
52
  accepts_nested_attributes_for :contact_emails, reject_if: proc { |attr| attr['email'].blank? }
50
53
  accepts_nested_attributes_for :roles, allow_destroy: true, reject_if: proc { |attr| attr['user_key'].blank? }
51
54
 
52
55
  before_save :sanitize_description, if: :description_changed?
53
- before_create :build_home_page
54
- before_create :add_site_reference
55
- after_create :initialize_config
56
- after_create :initialize_browse
57
- after_create :initialize_main_navigation
58
56
  include Spotlight::DefaultThumbnailable
59
57
 
60
58
  def main_about_page
@@ -75,7 +73,9 @@ module Spotlight
75
73
  end
76
74
 
77
75
  def solr_data
78
- Spotlight::Engine.config.exhibit_filter.call(self)
76
+ filters.each_with_object({}) do |filter, hash|
77
+ hash.merge! filter.to_hash
78
+ end
79
79
  end
80
80
 
81
81
  def reindex_later
@@ -104,33 +104,8 @@ module Spotlight
104
104
 
105
105
  protected
106
106
 
107
- def add_site_reference
108
- self.site ||= Spotlight::Site.instance
109
- end
110
-
111
- def initialize_config
112
- self.blacklight_configuration ||= Spotlight::BlacklightConfiguration.create!
113
- end
114
-
115
- def initialize_browse
116
- return unless searches.blank?
117
-
118
- searches.create title: 'All Exhibit Items',
119
- long_description: 'All items in this exhibit.'
120
- end
121
-
122
- def initialize_main_navigation
123
- default_main_navigations.each_with_index do |nav_type, weight|
124
- main_navigations.create nav_type: nav_type, weight: weight
125
- end
126
- end
127
-
128
107
  def sanitize_description
129
108
  self.description = ::Rails::Html::FullSanitizer.new.sanitize(description)
130
109
  end
131
-
132
- def default_main_navigations
133
- Spotlight::Engine.config.exhibit_main_navigation.dup
134
- end
135
110
  end
136
111
  end
@@ -0,0 +1,26 @@
1
+ module Spotlight
2
+ # A configurable solr filter for the exhibit
3
+ class Filter < ActiveRecord::Base
4
+ belongs_to :exhibit
5
+
6
+ validates :field, :value, presence: true
7
+
8
+ def to_hash
9
+ return {} unless field
10
+
11
+ { field => cast_value }
12
+ end
13
+
14
+ private
15
+
16
+ def cast_value
17
+ return value unless field
18
+
19
+ if field.ends_with? Spotlight::Engine.config.solr_fields.boolean_suffix
20
+ ActiveRecord::Type::Boolean.new.type_cast_from_database(value)
21
+ else
22
+ value
23
+ end
24
+ end
25
+ end
26
+ end
@@ -168,12 +168,14 @@ module Spotlight
168
168
  end
169
169
 
170
170
  def write_to_index(batch)
171
+ return unless write?
171
172
  blacklight_solr.update params: { commitWithin: 500 },
172
173
  data: batch.to_json,
173
174
  headers: { 'Content-Type' => 'application/json' }
174
175
  end
175
176
 
176
177
  def commit
178
+ return unless write?
177
179
  blacklight_solr.commit
178
180
  rescue => e
179
181
  Rails.logger.warn "Unable to commit to solr: #{e}"
@@ -54,8 +54,10 @@ module Spotlight
54
54
  masthead && masthead.display?
55
55
  end
56
56
 
57
+ # rubocop:disable Metrics/MethodLength
57
58
  def set_default_thumbnail
58
59
  self.thumbnail ||= begin
60
+ return unless Spotlight::Engine.config.full_image_field
59
61
  doc = documents.detect { |x| x.first(Spotlight::Engine.config.full_image_field) }
60
62
  if doc
61
63
  create_thumbnail(
@@ -66,6 +68,7 @@ module Spotlight
66
68
  end
67
69
  end
68
70
  end
71
+ # rubocop:enable Metrics/MethodLength
69
72
 
70
73
  def search_params
71
74
  search_builder.with(query_params.with_indifferent_access).merge(facet: false, fl: default_search_fields)
@@ -91,7 +94,7 @@ module Spotlight
91
94
  blacklight_config.index.title_field,
92
95
  blacklight_config.index.thumbnail_field,
93
96
  Spotlight::Engine.config.full_image_field
94
- ]
97
+ ].compact
95
98
  end
96
99
 
97
100
  def should_generate_new_friendly_id?
@@ -1,33 +1,10 @@
1
1
  <div class="col-xs-12 col-sm-4 exhibit-card">
2
2
  <div class="flipper">
3
3
  <div class="card-front card-face">
4
- <%= image_tag(exhibit.thumbnail.image.square.url) if exhibit.thumbnail.present? %>
5
- <% unless exhibit.published? %>
6
- <div class="label label-warning unpublished"><%= t('.unpublished') %></div>
7
- <% end %>
8
- <h2 class="card-title"><%= exhibit.title %></h2>
4
+ <%= render 'exhibit_card_front', exhibit: exhibit %>
9
5
  </div>
10
6
  <div class="card-back card-face">
11
- <h2 class="card-title">
12
- <%= exhibit.title %>
13
- </h2>
14
- <p class="subtitle">
15
- <%= exhibit.subtitle %>
16
- </p>
17
- <% if exhibit.description %>
18
- <p class="description">
19
- <%= exhibit.description.truncate(168, omission: "") %>
20
- <% if exhibit.description.length > 168 %>
21
- <%= content_tag(:span,"&hellip;".html_safe, class: "visible-sm visible-md") %>
22
- <span class="hidden-sm hidden-md">
23
- <%= exhibit.description.slice(168, exhibit.description.length) %>
24
- </span>
25
- <% end %>
26
- </p>
27
- <% end %>
28
- <div class="visit-exhibit center-btn">
29
- <%= link_to t(:'.visit_exhibit'), exhibit, class: "btn btn-primary", role: "button" %>
30
- </div>
7
+ <%= render 'exhibit_card_back', exhibit: exhibit %>
31
8
  </div>
32
9
  </div>
33
10
  </div>
@@ -0,0 +1,23 @@
1
+ <h2 class="card-title">
2
+ <%= exhibit.title %>
3
+ </h2>
4
+
5
+ <p class="subtitle">
6
+ <%= exhibit.subtitle %>
7
+ </p>
8
+
9
+ <% if exhibit.description %>
10
+ <p class="description">
11
+ <%= exhibit.description.truncate(168, omission: "") %>
12
+ <% if exhibit.description.length > 168 %>
13
+ <%= content_tag(:span,"&hellip;".html_safe, class: "visible-sm visible-md") %>
14
+ <span class="hidden-sm hidden-md">
15
+ <%= exhibit.description.slice(168, exhibit.description.length) %>
16
+ </span>
17
+ <% end %>
18
+ </p>
19
+ <% end %>
20
+
21
+ <div class="visit-exhibit center-btn">
22
+ <%= link_to t(:'.visit_exhibit'), exhibit, class: "btn btn-primary", role: "button" %>
23
+ </div>
@@ -0,0 +1,11 @@
1
+ <% if exhibit.thumbnail.present? %>
2
+ <%= image_tag(exhibit.thumbnail.image.square.url) %>
3
+ <% else %>
4
+ <%= image_tag 'spotlight/default_thumbnail.jpg', class: 'default-thumbnail' %>
5
+ <% end %>
6
+
7
+ <% unless exhibit.published? %>
8
+ <div class="label label-warning unpublished"><%= t('.unpublished') %></div>
9
+ <% end %>
10
+
11
+ <h2 class="card-title"><%= exhibit.title %></h2>
@@ -0,0 +1,5 @@
1
+ <% exhibits.each_slice(3).each do |row| %>
2
+ <div class="row"><!-- start main content row -->
3
+ <%= render collection: row, partial: 'exhibit_card', as: 'exhibit' %>
4
+ </div>
5
+ <% end %>
@@ -3,6 +3,7 @@
3
3
  <%= f.text_field :title %>
4
4
  <%= f.text_field :subtitle %>
5
5
  <%= f.text_area :description %>
6
+ <%= f.text_field :tag_list, value: f.object.tag_list.to_s %>
6
7
  <%= f.form_group(:contact_emails, label: { text: nil, class: nil }, help: nil) do %>
7
8
  <%= f.fields_for :contact_emails do |contact| %>
8
9
  <%= render partial: 'contact', locals: {contact: contact} %>
@@ -21,4 +22,4 @@
21
22
  </div>
22
23
  </div>
23
24
  </div>
24
- <% end %>
25
+ <% end %>
@@ -2,6 +2,7 @@
2
2
  <div class="row col-md-12">
3
3
  <%= f.text_field :title, label: t(:'.fields.title.label'), help: t(:'.fields.title.help_block') %>
4
4
  <%= f.text_field :slug, label: t(:'.fields.slug.label'), help: t(:'.fields.slug.help_block') %>
5
+ <%= f.text_field :tag_list %>
5
6
 
6
7
  <%= render 'initial_resources_form', locals: { f: f } %>
7
8
 
@@ -11,4 +12,4 @@
11
12
  </div>
12
13
  </div>
13
14
  </div>
14
- <% end %>
15
+ <% end %>
@@ -6,6 +6,11 @@
6
6
  <li role="presentation" class="active">
7
7
  <a href="#basic" aria-controls="basic" role="tab" data-toggle="tab"><%= t(:'.basic_settings.heading') %></a>
8
8
  </li>
9
+ <% if can? :edit, current_exhibit.filters.first_or_initialize %>
10
+ <li role="presentation">
11
+ <a href="#filter" aria-controls="filter" role="tab" data-toggle="tab"><%= t(:'spotlight.exhibits.filter.heading') %></a>
12
+ </li>
13
+ <% end %>
9
14
  <% if can? :import, current_exhibit %>
10
15
  <li role="presentation">
11
16
  <a href="#import" aria-controls="import" role="tab" data-toggle="tab"><%= t(:'spotlight.exhibits.import.heading') %></a>
@@ -26,6 +31,7 @@
26
31
  <div role="tabpanel" class="tab-pane active" id="basic">
27
32
  <%= render 'form' %>
28
33
  </div>
34
+ <%= render 'spotlight/filters/form' if can? :edit, current_exhibit.filters.first_or_initialize %>
29
35
  <%= render 'import' if can? :import, current_exhibit %>
30
36
  <%= render 'export' if can? :export, current_exhibit %>
31
37
  <%= render 'delete' if can? :destroy, current_exhibit %>
@@ -2,7 +2,7 @@
2
2
  <% if (current_user && current_user.exhibits.any?) || can?(:manage, Spotlight::Exhibit) %>
3
3
  <ul class="nav nav-tabs" role="tablist">
4
4
  <li role="presentation" class="active"><a href="#published" aria-controls="published" role="tab" data-toggle="tab"><%= t('.published') %></a></li>
5
- <% if can?(:manage, Spotlight::Exhibit) %>
5
+ <% if can?(:manage, Spotlight::Exhibit) && @exhibits.unpublished.accessible_by(current_ability).any? %>
6
6
  <li role="presentation"><a href="#unpublished" aria-controls="unpublished" role="tab" data-toggle="tab"><%= t('.unpublished') %></a></li>
7
7
  <% end %>
8
8
  <% if current_user && current_user.exhibits.any? %>
@@ -18,31 +18,19 @@
18
18
  <% end %>
19
19
 
20
20
  <%= cache cache_key_for_spotlight_exhibits do %>
21
- <% @exhibits.published.each_slice(3).each do |row| %>
22
- <div class="row"><!-- start main content row -->
23
- <%= render collection: row, partial: 'exhibit_card', as: 'exhibit' %>
24
- </div>
25
- <% end %>
21
+ <%= render 'exhibits', exhibits: @exhibits.published %>
26
22
  <% end %>
27
23
  </div>
28
24
 
29
25
  <% if @exhibits.unpublished.accessible_by(current_ability).any? %>
30
26
  <div role="tabpanel" class="tab-pane" id="unpublished">
31
- <% @exhibits.unpublished.accessible_by(current_ability).each_slice(3).each do |row| %>
32
- <div class="row"><!-- start main content row -->
33
- <%= render collection: row, partial: 'exhibit_card', as: 'exhibit' %>
34
- </div>
35
- <% end %>
27
+ <%= render 'exhibits', exhibits: @exhibits.unpublished.accessible_by(current_ability) %>
36
28
  </div>
37
29
  <% end %>
38
30
 
39
31
  <% if current_user && current_user.exhibits.any? %>
40
32
  <div role="tabpanel" class="tab-pane" id="user">
41
- <% current_user.exhibits.each_slice(3).each do |row| %>
42
- <div class="row"><!-- start main content row -->
43
- <%= render collection: row, partial: 'exhibit_card', as: 'exhibit' %>
44
- </div>
45
- <% end %>
33
+ <%= render 'exhibits', exhibits: current_user.exhibits %>
46
34
  </div>
47
35
  <% end %>
48
36
  </div>
@@ -0,0 +1,13 @@
1
+ <div role="tabpanel" class="tab-pane" id="filter">
2
+ <%= bootstrap_form_for [@exhibit, @exhibit.filters.first_or_initialize], layout: :horizontal, label_col: 'col-md-2', control_col: 'col-md-10', html: {class: "row"} do |f| %>
3
+ <%= f.text_field :field %>
4
+ <%= f.text_field :value %>
5
+
6
+ <div class="form-actions">
7
+ <div class="primary-actions">
8
+ <%= f.submit nil, class: 'btn btn-primary' %>
9
+ </div>
10
+ </div>
11
+ <% end %>
12
+ </div>
13
+
@@ -54,6 +54,7 @@ en:
54
54
  batch_error: "There was an error updating the requested pages."
55
55
  destroyed: "The %{model} was deleted."
56
56
  exhibit: *spotlight_default
57
+ filter: *spotlight_default
57
58
  search: *spotlight_default
58
59
  site: *spotlight_default
59
60
  contact_form:
@@ -77,6 +78,11 @@ en:
77
78
  label:
78
79
  solr_document:
79
80
  exhibit_tag_list: "Tags"
81
+ spotlight/exhibit:
82
+ tag_list: 'Tags'
83
+ spotlight/filter:
84
+ field: 'Field'
85
+ value: 'Value'
80
86
  activerecord:
81
87
  models:
82
88
  spotlight:
@@ -91,6 +97,9 @@ en:
91
97
  display_title: "Show title"
92
98
  "spotlight/masthead":
93
99
  display: "Show background image in masthead"
100
+ help:
101
+ spotlight/exhibit:
102
+ tag_list: "Enter tags separated by commas."
94
103
  blacklight:
95
104
  search:
96
105
  fields:
@@ -353,6 +362,8 @@ en:
353
362
  visit_exhibit: "Visit exhibit"
354
363
  new:
355
364
  header: Create a new exhibit
365
+ filter:
366
+ heading: Filter items
356
367
  import:
357
368
  heading: Import data
358
369
  instructions: You can import an exhibit JSON file exported from this application to use that data file to define this exhibit.
@@ -23,6 +23,8 @@ Spotlight::Engine.routes.draw do
23
23
  resource :search_configuration, only: [:show, :edit, :update]
24
24
  resource :view_configuration, only: [:show]
25
25
 
26
+ resources :filters, only: [:create, :update]
27
+
26
28
  blacklight_for :catalog, only: [:export]
27
29
 
28
30
  resources :catalog do
@@ -0,0 +1,17 @@
1
+ class CreateSpotlightExhibitFilters < ActiveRecord::Migration
2
+ def change
3
+ create_table :spotlight_filters do |t|
4
+ t.string :field
5
+ t.string :value
6
+ t.references :exhibit, index: true, foreign_key: true
7
+
8
+ t.timestamps null: false
9
+ end
10
+
11
+ reversible do |change|
12
+ change.up do
13
+ Spotlight::Exhibit.all.each { |exhibit| exhibit.send(:initialize_filter) }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -47,6 +47,10 @@ module Spotlight
47
47
  FactoryGirl.definition_file_paths << File.expand_path('../../../spec/factories', __FILE__) if defined?(FactoryGirl)
48
48
  end
49
49
 
50
+ initializer 'spotlight.assets.precompile' do |app|
51
+ app.config.assets.precompile += %w(spotlight/default_thumbnail.jpg)
52
+ end
53
+
50
54
  def self.user_class
51
55
  Spotlight::Engine.config.user_class.constantize
52
56
  end
@@ -75,6 +79,10 @@ module Spotlight
75
79
 
76
80
  # Filter resources by exhibit by default
77
81
  config.filter_resources_by_exhibit = true
82
+
83
+ # Should Spotlight write to solr? If set to false, Spotlight will not initiate indexing.
84
+ config.writable_index = true
85
+
78
86
  # The allowed file extensions for uploading non-repository items.
79
87
  config.allowed_upload_extensions = %w(jpg jpeg png)
80
88
 
@@ -85,14 +93,10 @@ module Spotlight
85
93
  config.solr_fields.string_suffix = '_ssim'.freeze
86
94
  config.solr_fields.text_suffix = '_tesim'.freeze
87
95
 
88
- # A lambda expression that filters the solr index per exhibit
89
- config.exhibit_filter = lambda do |exhibit|
90
- { :"#{config.solr_fields.prefix}spotlight_exhibit_slug_#{exhibit.slug}#{config.solr_fields.boolean_suffix}" => true }
91
- end
92
-
93
96
  config.resource_global_id_field = :"#{config.solr_fields.prefix}spotlight_resource_id#{config.solr_fields.string_suffix}"
94
97
 
95
98
  # The solr field that original (largest) images will be stored.
99
+ # Set to nil if you don't want to pull thumbnails from the index
96
100
  config.full_image_field = :full_image_url_ssm
97
101
  config.thumbnail_field = :thumbnail_url_ssm
98
102
  config.square_image_field = :thumbnail_square_url_ssm
@@ -1,4 +1,4 @@
1
1
  #
2
2
  module Spotlight
3
- VERSION = '0.14.2'
3
+ VERSION = '0.15.0'
4
4
  end
@@ -4,14 +4,14 @@ namespace :spotlight do
4
4
  puts 'Creating an initial admin user.'
5
5
  u = prompt_to_create_user
6
6
 
7
- Spotlight::Role.create(user: u, exhibit: nil, role: 'admin')
7
+ Spotlight::Role.create(user: u, resource: nil, role: 'admin')
8
8
  puts 'User created.'
9
9
  end
10
10
 
11
11
  desc 'Add application-wide admin privileges to a user'
12
12
  task admin: :environment do
13
13
  u = prompt_to_create_user
14
- Spotlight::Role.create(user: u, exhibit: nil, role: 'admin')
14
+ Spotlight::Role.create(user: u, resource: nil, role: 'admin')
15
15
  end
16
16
 
17
17
  desc 'Create a new exhibit'
@@ -25,7 +25,7 @@ namespace :spotlight do
25
25
 
26
26
  u = prompt_to_create_user
27
27
 
28
- Spotlight::Role.create(user: u, exhibit: exhibit, role: 'admin')
28
+ Spotlight::Role.create(user: u, resource: exhibit, role: 'admin')
29
29
  puts 'Exhibit created.'
30
30
  end
31
31
 
@@ -85,7 +85,7 @@ describe Spotlight::ExhibitsController, type: :controller do
85
85
 
86
86
  it 'is successful' do
87
87
  expect do
88
- post :create, exhibit: { title: 'Some Title', slug: 'custom-slug' }
88
+ post :create, exhibit: { title: 'Some Title', slug: 'custom-slug', tag_list: '2014, R. Buckminster Fuller' }
89
89
  end.to change { Spotlight::Exhibit.count }.by(1)
90
90
 
91
91
  exhibit = Spotlight::Exhibit.last
@@ -93,6 +93,7 @@ describe Spotlight::ExhibitsController, type: :controller do
93
93
 
94
94
  expect(exhibit.title).to eq 'Some Title'
95
95
  expect(exhibit.slug).to eq 'custom-slug'
96
+ expect(exhibit.tags.map(&:name)).to eq ['2014', 'R. Buckminster Fuller']
96
97
 
97
98
  expect(user.exhibits).to include exhibit
98
99
  end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spotlight::FiltersController do
4
+ routes { Spotlight::Engine.routes }
5
+
6
+ describe '#create' do
7
+ let(:exhibit) { FactoryGirl.create(:exhibit) }
8
+ let(:exhibit_filter) { exhibit.filters.first }
9
+
10
+ before do
11
+ allow(Spotlight::Engine.config).to receive(:filter_resources_by_exhibit).and_return(false)
12
+ end
13
+
14
+ context 'when not signed in' do
15
+ it 'is not successful' do
16
+ post :create, exhibit_id: exhibit, filter: { field: 'foo_ssi', value: 'bar_ssi' }
17
+ expect(:response).to redirect_to main_app.new_user_session_path
18
+ end
19
+ end
20
+
21
+ context 'when signed in as a site admin' do
22
+ before { sign_in user }
23
+ let(:user) { FactoryGirl.create(:site_admin) }
24
+
25
+ it 'is successful' do
26
+ post :create, exhibit_id: exhibit, filter: { field: 'foo_ssi', value: 'bar' }
27
+ expect(:response).to redirect_to edit_exhibit_path(exhibit, anchor: 'filter')
28
+ expect(assigns[:exhibit].solr_data).to eq('foo_ssi' => 'bar')
29
+ end
30
+
31
+ it 'valids filter values' do
32
+ post :create, exhibit_id: exhibit, filter: { field: 'foo_ssi', value: '' }
33
+ expect(:response).to redirect_to edit_exhibit_path(exhibit, anchor: 'filter')
34
+ expect(flash[:alert]).to include "Value can't be blank"
35
+ end
36
+ end
37
+ end
38
+
39
+ describe '#update' do
40
+ let(:exhibit) { FactoryGirl.create(:exhibit) }
41
+ let(:exhibit_filter) { exhibit.filters.first }
42
+
43
+ before do
44
+ allow(Spotlight::Engine.config).to receive(:filter_resources_by_exhibit).and_return(true)
45
+ end
46
+
47
+ context 'when not signed in' do
48
+ it 'is not successful' do
49
+ patch :update, exhibit_id: exhibit, id: exhibit_filter, filter: { field: 'foo_ssi', value: 'bar_ssi' }
50
+ expect(:response).to redirect_to main_app.new_user_session_path
51
+ end
52
+ end
53
+
54
+ context 'when signed in as a site admin' do
55
+ before { sign_in user }
56
+ let(:user) { FactoryGirl.create(:site_admin) }
57
+
58
+ it 'is successful' do
59
+ patch :update, exhibit_id: exhibit, id: exhibit_filter, filter: { field: 'foo_ssi', value: 'bar' }
60
+ expect(:response).to redirect_to edit_exhibit_path(exhibit, anchor: 'filter')
61
+ expect(assigns[:exhibit].solr_data).to eq('foo_ssi' => 'bar')
62
+ end
63
+
64
+ it 'valids filter values' do
65
+ patch :update, exhibit_id: exhibit, id: exhibit_filter, filter: { field: 'foo_ssi', value: '' }
66
+ expect(:response).to redirect_to edit_exhibit_path(exhibit, anchor: 'filter')
67
+ expect(flash[:alert]).to include "Value can't be blank"
68
+ end
69
+ end
70
+ end
71
+ end
@@ -21,7 +21,7 @@ describe Spotlight::SolrController, type: :controller do
21
21
  let(:solr) { double }
22
22
  before { sign_in admin }
23
23
  before do
24
- expect(controller).to receive(:blacklight_solr).and_return(solr)
24
+ allow(controller).to receive(:blacklight_solr).and_return(solr)
25
25
  end
26
26
 
27
27
  describe 'POST update' do
@@ -37,6 +37,18 @@ describe Spotlight::SolrController, type: :controller do
37
37
  expect(doc).to include 'a' => 1
38
38
  end
39
39
 
40
+ context 'when the index is not writable' do
41
+ before do
42
+ allow(Spotlight::Engine.config).to receive_messages(writable_index: false)
43
+ end
44
+
45
+ it 'raises an error' do
46
+ post :update, { a: 1 }.to_json, content_type: :json, exhibit_id: exhibit
47
+
48
+ expect(response.code).to eq '409'
49
+ end
50
+ end
51
+
40
52
  it 'enriches the request with exhibit solr data' do
41
53
  doc = {}
42
54
  expect(solr).to receive(:update) do |arr|
@@ -67,9 +67,27 @@ describe Spotlight::Catalog::AccessControlsEnforcement do
67
67
  allow(scope).to receive(:can?).and_return(true)
68
68
 
69
69
  subject.apply_exhibit_resources_filter(solr_request)
70
+
70
71
  expect(solr_request).to include :fq
71
72
  expect(solr_request[:fq]).to include "spotlight_exhibit_slug_#{exhibit.slug}_bsi:true"
72
73
  end
73
74
  end
75
+
76
+ context 'with a custom exhibit filter' do
77
+ let(:filter) { exhibit.filters.first_or_initialize }
78
+
79
+ before do
80
+ filter.update(field: 'author_ssim', value: 'Coyne, Justin')
81
+ end
82
+
83
+ it 'filters resources to just those identified by the exhibit filter' do
84
+ allow(scope).to receive(:can?).and_return(true)
85
+
86
+ subject.apply_exhibit_resources_filter(solr_request)
87
+
88
+ expect(solr_request).to include :fq
89
+ expect(solr_request[:fq]).to include '{!raw f=author_ssim}Coyne, Justin'
90
+ end
91
+ end
74
92
  end
75
93
  end
@@ -127,10 +127,36 @@ describe Spotlight::Exhibit, type: :model do
127
127
  end
128
128
 
129
129
  describe '#solr_data' do
130
- subject { FactoryGirl.create(:exhibit) }
130
+ let(:exhibit) { FactoryGirl.create(:exhibit) }
131
+ subject { exhibit.solr_data }
132
+
133
+ context 'when not filtering by exhibit' do
134
+ before do
135
+ allow(Spotlight::Engine.config).to receive(:filter_resources_by_exhibit).and_return(false)
136
+ end
131
137
 
132
- it 'provides a solr field with the exhibit slug' do
133
- expect(subject.solr_data).to include(:"spotlight_exhibit_slug_#{subject.slug}_bsi" => true)
138
+ it 'is blank' do
139
+ expect(subject).to be_blank
140
+ end
141
+ end
142
+
143
+ context 'when no filters have been defined' do
144
+ before do
145
+ allow(Spotlight::Engine.config).to receive(:filter_resources_by_exhibit).and_return(true)
146
+ end
147
+
148
+ it 'provides a solr field with the exhibit slug' do
149
+ expect(subject).to include("spotlight_exhibit_slug_#{exhibit.slug}_bsi" => true)
150
+ end
151
+ end
152
+
153
+ context 'with a filter' do
154
+ before do
155
+ exhibit.filters.create(field: 'orcid_ssim', value: '123')
156
+ end
157
+ it 'uses the provided filter' do
158
+ expect(subject).to include('orcid_ssim' => '123')
159
+ end
134
160
  end
135
161
  end
136
162
 
@@ -175,10 +201,15 @@ describe Spotlight::Exhibit, type: :model do
175
201
  describe '#solr_documents' do
176
202
  let(:blacklight_config) { Blacklight::Configuration.new }
177
203
  let(:slug) { 'some_slug' }
204
+ let(:filter) do
205
+ Spotlight::Filter.new(field: subject.send(:default_filter_field),
206
+ value: subject.send(:default_filter_value))
207
+ end
178
208
 
179
209
  before do
180
210
  allow(subject).to receive(:blacklight_config).and_return(blacklight_config)
181
211
  allow(subject).to receive(:slug).and_return(slug)
212
+ allow(subject).to receive(:filters).and_return([filter])
182
213
  end
183
214
 
184
215
  it 'enumerates the documents in the exhibit' do
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spotlight::Filter do
4
+ context 'with a simple string field' do
5
+ subject { described_class.new(field: 'x', value: 'y') }
6
+
7
+ it 'passes the value through' do
8
+ expect(subject.to_hash).to eq 'x' => 'y'
9
+ end
10
+ end
11
+
12
+ context 'with a boolean field' do
13
+ subject { described_class.new(field: 'x_bsi', value: 'true') }
14
+
15
+ it 'casts the value to a boolean' do
16
+ expect(subject.to_hash).to eq 'x_bsi' => true
17
+ end
18
+ end
19
+ end
@@ -49,6 +49,17 @@ describe Spotlight::Resource, type: :model do
49
49
  subject.reindex
50
50
  end
51
51
 
52
+ context 'when the index is not writable' do
53
+ before do
54
+ allow(Spotlight::Engine.config).to receive_messages(writable_index: false)
55
+ end
56
+
57
+ it "doesn't write" do
58
+ expect(subject.send(:blacklight_solr)).not_to receive(:update)
59
+ subject.reindex
60
+ end
61
+ end
62
+
52
63
  context 'with a resource that creates multiple solr documents' do
53
64
  let(:solr_response) { [{ id: 1 }, { id: 2 }] }
54
65
 
@@ -37,6 +37,17 @@ describe Spotlight::Search, type: :model do
37
37
  expect(subject.thumbnail).not_to be_nil
38
38
  expect(subject.thumbnail.image.path).to end_with 'default.jpg'
39
39
  end
40
+
41
+ context 'when full_image_field is nil' do
42
+ before do
43
+ allow(Spotlight::Engine.config).to receive_messages(full_image_field: nil)
44
+ end
45
+ it "doesn't query solr" do
46
+ expect(subject).not_to receive(:documents)
47
+ subject.set_default_thumbnail
48
+ expect(subject.thumbnail).to be_nil
49
+ end
50
+ end
40
51
  end
41
52
  end
42
53
 
@@ -15,6 +15,17 @@ describe Spotlight::SolrDocument::AtomicUpdates, type: :model do
15
15
  allow(subject).to receive_messages(to_solr: { id: 'doc_id', a: 1, b: 2 })
16
16
  end
17
17
 
18
+ context 'when the index is not writable' do
19
+ before do
20
+ allow(Spotlight::Engine.config).to receive_messages(writable_index: false)
21
+ end
22
+
23
+ it "doesn't write" do
24
+ expect(blacklight_solr).not_to receive(:update)
25
+ subject.reindex
26
+ end
27
+ end
28
+
18
29
  it 'sends an atomic update request' do
19
30
  expected = {
20
31
  params: { commitWithin: 500 },
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'spotlight/exhibits/_exhibit_card_front.html.erb', type: :view do
4
+ let(:exhibit) { FactoryGirl.create(:exhibit) }
5
+ let(:p) { 'spotlight/exhibits/exhibit_card_front' }
6
+
7
+ context 'for an exhibit without a thumbnail' do
8
+ before do
9
+ exhibit.update(thumbnail_id: nil)
10
+ end
11
+
12
+ it 'has a placeholder thumbnail' do
13
+ render p, exhibit: exhibit
14
+
15
+ expect(rendered).to have_selector 'img.default-thumbnail'
16
+ end
17
+ end
18
+
19
+ it 'has a thumbnail' do
20
+ render p, exhibit: exhibit
21
+
22
+ expect(rendered).to have_selector 'img'
23
+ end
24
+
25
+ it 'has a title' do
26
+ render p, exhibit: exhibit
27
+
28
+ expect(rendered).to have_selector '.card-title', text: exhibit.title
29
+ end
30
+
31
+ context 'for an unpublished exhibit' do
32
+ before do
33
+ exhibit.update(published: false)
34
+ end
35
+
36
+ it 'has an unpublished banner' do
37
+ render p, exhibit: exhibit
38
+
39
+ expect(rendered).to have_selector '.label.unpublished', text: 'Unpublished'
40
+ end
41
+ end
42
+ end
@@ -9,6 +9,7 @@ module Spotlight
9
9
  allow(view).to receive_messages(can?: true)
10
10
  allow(view).to receive_messages(import_exhibit_path: '/')
11
11
  allow(view).to receive_messages(get_exhibit_path: '/')
12
+ allow(view).to receive_messages(exhibit_filters_path: '/')
12
13
  end
13
14
 
14
15
  it 'renders the edit page form' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blacklight-spotlight
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.2
4
+ version: 0.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Beer
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2015-12-18 00:00:00.000000000 Z
14
+ date: 2016-01-04 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rails
@@ -713,6 +713,7 @@ files:
713
713
  - app/assets/images/spotlight/blocks/rule.png
714
714
  - app/assets/images/spotlight/blocks/text-on.png
715
715
  - app/assets/images/spotlight/blocks/text.png
716
+ - app/assets/images/spotlight/default_thumbnail.jpg
716
717
  - app/assets/javascripts/spotlight/add_new_page_button.js
717
718
  - app/assets/javascripts/spotlight/appearance.js
718
719
  - app/assets/javascripts/spotlight/application.js
@@ -813,6 +814,7 @@ files:
813
814
  - app/controllers/spotlight/dashboards_controller.rb
814
815
  - app/controllers/spotlight/exhibits_controller.rb
815
816
  - app/controllers/spotlight/feature_pages_controller.rb
817
+ - app/controllers/spotlight/filters_controller.rb
816
818
  - app/controllers/spotlight/home_pages_controller.rb
817
819
  - app/controllers/spotlight/lock_controller.rb
818
820
  - app/controllers/spotlight/metadata_configurations_controller.rb
@@ -848,6 +850,7 @@ files:
848
850
  - app/models/concerns/spotlight/blacklight_configuration_defaults.rb
849
851
  - app/models/concerns/spotlight/default_thumbnailable.rb
850
852
  - app/models/concerns/spotlight/exhibit_analytics.rb
853
+ - app/models/concerns/spotlight/exhibit_defaults.rb
851
854
  - app/models/concerns/spotlight/exhibit_documents.rb
852
855
  - app/models/concerns/spotlight/image_derivatives.rb
853
856
  - app/models/concerns/spotlight/resources/open_graph.rb
@@ -884,6 +887,7 @@ files:
884
887
  - app/models/spotlight/feature_page.rb
885
888
  - app/models/spotlight/featured_image.rb
886
889
  - app/models/spotlight/field_metadata.rb
890
+ - app/models/spotlight/filter.rb
887
891
  - app/models/spotlight/home_page.rb
888
892
  - app/models/spotlight/lock.rb
889
893
  - app/models/spotlight/main_navigation.rb
@@ -978,6 +982,9 @@ files:
978
982
  - app/views/spotlight/exhibits/_contact.html.erb
979
983
  - app/views/spotlight/exhibits/_delete.html.erb
980
984
  - app/views/spotlight/exhibits/_exhibit_card.html.erb
985
+ - app/views/spotlight/exhibits/_exhibit_card_back.html.erb
986
+ - app/views/spotlight/exhibits/_exhibit_card_front.html.erb
987
+ - app/views/spotlight/exhibits/_exhibits.html.erb
981
988
  - app/views/spotlight/exhibits/_export.html.erb
982
989
  - app/views/spotlight/exhibits/_form.html.erb
983
990
  - app/views/spotlight/exhibits/_import.html.erb
@@ -993,6 +1000,7 @@ files:
993
1000
  - app/views/spotlight/feature_pages/_sidebar.html.erb
994
1001
  - app/views/spotlight/featured_images/_form.html.erb
995
1002
  - app/views/spotlight/featured_images/_upload_form.html.erb
1003
+ - app/views/spotlight/filters/_form.html.erb
996
1004
  - app/views/spotlight/home_pages/_edit_page_link.html.erb
997
1005
  - app/views/spotlight/home_pages/_empty.html.erb
998
1006
  - app/views/spotlight/home_pages/_page_options.html.erb
@@ -1109,6 +1117,7 @@ files:
1109
1117
  - db/migrate/20151211131415_add_site_to_spotlight_exhibits.rb
1110
1118
  - db/migrate/20151215141516_change_roles_to_support_polymorphic_associations.rb
1111
1119
  - db/migrate/20151215192845_add_index_status_to_resources.rb
1120
+ - db/migrate/20151217211019_create_spotlight_exhibit_filters.rb
1112
1121
  - lib/blacklight/spotlight.rb
1113
1122
  - lib/generators/spotlight/install_generator.rb
1114
1123
  - lib/generators/spotlight/templates/catalog_controller.rb
@@ -1138,6 +1147,7 @@ files:
1138
1147
  - spec/controllers/spotlight/dashboards_controller_spec.rb
1139
1148
  - spec/controllers/spotlight/exhibits_controller_spec.rb
1140
1149
  - spec/controllers/spotlight/feature_pages_controller_spec.rb
1150
+ - spec/controllers/spotlight/filters_controller_spec.rb
1141
1151
  - spec/controllers/spotlight/home_pages_controller_spec.rb
1142
1152
  - spec/controllers/spotlight/metadata_configurations_controller_spec.rb
1143
1153
  - spec/controllers/spotlight/resources/upload_controller_spec.rb
@@ -1246,6 +1256,7 @@ files:
1246
1256
  - spec/models/spotlight/feature_page_spec.rb
1247
1257
  - spec/models/spotlight/featured_image_spec.rb
1248
1258
  - spec/models/spotlight/field_metadata_spec.rb
1259
+ - spec/models/spotlight/filter_spec.rb
1249
1260
  - spec/models/spotlight/home_page_spec.rb
1250
1261
  - spec/models/spotlight/image_derivatives_spec.rb
1251
1262
  - spec/models/spotlight/main_navigation_spec.rb
@@ -1299,6 +1310,7 @@ files:
1299
1310
  - spec/views/spotlight/contacts/edit.html.erb_spec.rb
1300
1311
  - spec/views/spotlight/dashboards/_analytics.html.erb_spec.rb
1301
1312
  - spec/views/spotlight/dashboards/analytics.html.erb_spec.rb
1313
+ - spec/views/spotlight/exhibits/_exhibit_card_front.html.erb_spec.rb
1302
1314
  - spec/views/spotlight/exhibits/edit.html.erb_spec.rb
1303
1315
  - spec/views/spotlight/exhibits/index.html.erb_spec.rb
1304
1316
  - spec/views/spotlight/feature_pages/_empty.html.erb_spec.rb
@@ -1378,6 +1390,7 @@ test_files:
1378
1390
  - spec/controllers/spotlight/dashboards_controller_spec.rb
1379
1391
  - spec/controllers/spotlight/exhibits_controller_spec.rb
1380
1392
  - spec/controllers/spotlight/feature_pages_controller_spec.rb
1393
+ - spec/controllers/spotlight/filters_controller_spec.rb
1381
1394
  - spec/controllers/spotlight/home_pages_controller_spec.rb
1382
1395
  - spec/controllers/spotlight/metadata_configurations_controller_spec.rb
1383
1396
  - spec/controllers/spotlight/resources/upload_controller_spec.rb
@@ -1486,6 +1499,7 @@ test_files:
1486
1499
  - spec/models/spotlight/feature_page_spec.rb
1487
1500
  - spec/models/spotlight/featured_image_spec.rb
1488
1501
  - spec/models/spotlight/field_metadata_spec.rb
1502
+ - spec/models/spotlight/filter_spec.rb
1489
1503
  - spec/models/spotlight/home_page_spec.rb
1490
1504
  - spec/models/spotlight/image_derivatives_spec.rb
1491
1505
  - spec/models/spotlight/main_navigation_spec.rb
@@ -1539,6 +1553,7 @@ test_files:
1539
1553
  - spec/views/spotlight/contacts/edit.html.erb_spec.rb
1540
1554
  - spec/views/spotlight/dashboards/_analytics.html.erb_spec.rb
1541
1555
  - spec/views/spotlight/dashboards/analytics.html.erb_spec.rb
1556
+ - spec/views/spotlight/exhibits/_exhibit_card_front.html.erb_spec.rb
1542
1557
  - spec/views/spotlight/exhibits/edit.html.erb_spec.rb
1543
1558
  - spec/views/spotlight/exhibits/index.html.erb_spec.rb
1544
1559
  - spec/views/spotlight/feature_pages/_empty.html.erb_spec.rb