blacklight-spotlight 0.14.2 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
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