blacklight-spotlight 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/spotlight/reindex_monitor.js +109 -0
  3. data/app/assets/stylesheets/spotlight/_blacklight_configuration.scss +9 -8
  4. data/app/assets/stylesheets/spotlight/_bootstrap_overrides.scss +15 -7
  5. data/app/assets/stylesheets/spotlight/_header.scss +67 -90
  6. data/app/assets/stylesheets/spotlight/_variables.scss +4 -0
  7. data/app/controllers/spotlight/attachments_controller.rb +2 -5
  8. data/app/controllers/spotlight/browse_controller.rb +15 -3
  9. data/app/controllers/spotlight/resources_controller.rb +4 -0
  10. data/app/controllers/spotlight/roles_controller.rb +1 -1
  11. data/app/controllers/spotlight/sites_controller.rb +50 -0
  12. data/app/helpers/spotlight/application_helper.rb +2 -1
  13. data/app/helpers/spotlight/navbar_helper.rb +1 -1
  14. data/app/jobs/spotlight/reindex_job.rb +17 -8
  15. data/app/models/concerns/spotlight/user.rb +8 -4
  16. data/app/models/spotlight/ability.rb +5 -4
  17. data/app/models/spotlight/exhibit.rb +15 -1
  18. data/app/models/spotlight/reindex_progress.rb +107 -0
  19. data/app/models/spotlight/resource.rb +4 -0
  20. data/app/models/spotlight/role.rb +2 -2
  21. data/app/models/spotlight/site.rb +16 -0
  22. data/app/views/devise/mailer/invitation_instructions.html.erb +1 -1
  23. data/app/views/layouts/spotlight/spotlight.html.erb +1 -1
  24. data/app/views/shared/_breadcrumbs.html.erb +1 -1
  25. data/app/views/shared/_exhibit_navbar.html.erb +6 -7
  26. data/app/views/shared/_masthead.html.erb +32 -0
  27. data/app/views/shared/_site_sidebar.html.erb +16 -0
  28. data/app/views/spotlight/about_pages/_sidebar.html.erb +4 -2
  29. data/app/views/spotlight/browse/_search_title.html.erb +1 -1
  30. data/app/views/spotlight/browse/index.html.erb +6 -4
  31. data/app/views/spotlight/browse/show.html.erb +6 -8
  32. data/app/views/spotlight/catalog/_admin_header.html.erb +15 -12
  33. data/app/views/spotlight/catalog/_reindex_progress_panel.html.erb +21 -0
  34. data/app/views/spotlight/exhibits/index.html.erb +4 -2
  35. data/app/views/spotlight/featured_images/_upload_form.html.erb +17 -0
  36. data/app/views/spotlight/home_pages/_empty.html.erb +1 -1
  37. data/app/views/spotlight/metadata_configurations/_metadata_field.html.erb +2 -2
  38. data/app/views/spotlight/metadata_configurations/edit.html.erb +3 -3
  39. data/app/views/spotlight/shared/_configuration_sidebar.html.erb +2 -2
  40. data/app/views/spotlight/shared/_curation_sidebar.html.erb +1 -1
  41. data/app/views/spotlight/sites/_exhibit.html.erb +18 -0
  42. data/app/views/spotlight/sites/edit.html.erb +40 -0
  43. data/app/views/spotlight/sites/edit_exhibits.html.erb +32 -0
  44. data/config/locales/spotlight.en.yml +30 -2
  45. data/config/routes.rb +5 -0
  46. data/db/migrate/20151210073829_create_spotlight_site.rb +9 -0
  47. data/db/migrate/20151211131415_add_site_to_spotlight_exhibits.rb +23 -0
  48. data/db/migrate/20151215141516_change_roles_to_support_polymorphic_associations.rb +40 -0
  49. data/db/migrate/20151215192845_add_index_status_to_resources.rb +6 -0
  50. data/lib/spotlight/controller.rb +18 -7
  51. data/lib/spotlight/engine.rb +37 -35
  52. data/lib/spotlight/version.rb +1 -1
  53. data/spec/controllers/spotlight/contacts_controller_spec.rb +2 -2
  54. data/spec/controllers/spotlight/home_pages_controller_spec.rb +2 -4
  55. data/spec/controllers/spotlight/resources_controller_spec.rb +14 -0
  56. data/spec/controllers/spotlight/roles_controller_spec.rb +1 -1
  57. data/spec/controllers/spotlight/sites_controller_spec.rb +48 -0
  58. data/spec/controllers/spotlight/versions_controller_spec.rb +1 -2
  59. data/spec/factories/roles.rb +1 -1
  60. data/spec/factories/users.rb +3 -3
  61. data/spec/features/add_contacts_spec.rb +2 -2
  62. data/spec/features/browse_category_spec.rb +65 -0
  63. data/spec/features/edit_contact_spec.rb +2 -2
  64. data/spec/features/exhibit_masthead_spec.rb +100 -0
  65. data/spec/features/javascript/reindex_monitor_spec.rb +22 -0
  66. data/spec/features/site_masthead_spec.rb +17 -52
  67. data/spec/helpers/spotlight/application_helper_spec.rb +16 -0
  68. data/spec/lib/spotlight/controller_spec.rb +34 -8
  69. data/spec/models/spotlight/ability_spec.rb +3 -4
  70. data/spec/models/spotlight/exhibit_spec.rb +23 -0
  71. data/spec/models/spotlight/reindex_progress_spec.rb +104 -0
  72. data/spec/models/spotlight/site_spec.rb +9 -0
  73. data/spec/views/shared/_exhibit_navbar.html.erb_spec.rb +3 -9
  74. data/spec/views/shared/_masthead.html.erb_spec.rb +83 -0
  75. data/spec/views/spotlight/browse/show.html.erb_spec.rb +2 -2
  76. data/spec/views/spotlight/catalog/admin.html.erb_spec.rb +1 -0
  77. data/spec/views/spotlight/roles/index.html.erb_spec.rb +1 -1
  78. data/spec/views/spotlight/sites/edit_exhibits.html.erb_spec.rb +29 -0
  79. metadata +33 -14
  80. data/app/views/shared/_exhibit_masthead.html.erb +0 -20
  81. data/app/views/shared/_exhibit_masthead_and_navbar.html.erb +0 -9
  82. data/app/views/shared/_home_sidebar.html.erb +0 -1
  83. data/app/views/spotlight/attachments/_form.html.erb +0 -29
  84. data/app/views/spotlight/attachments/edit.html.erb +0 -6
  85. data/app/views/spotlight/attachments/index.html.erb +0 -31
  86. data/app/views/spotlight/attachments/new.html.erb +0 -5
  87. data/app/views/spotlight/attachments/show.html.erb +0 -19
  88. data/spec/views/shared/_exhibit_masthead.html.erb_spec.rb +0 -70
  89. data/spec/views/shared/_exhibit_masthead_and_navbar.html.erb_spec.rb +0 -36
@@ -39,6 +39,10 @@ module Spotlight
39
39
  end
40
40
  # rubocop:enable Metrics/MethodLength
41
41
 
42
+ def monitor
43
+ render json: current_exhibit.reindex_progress
44
+ end
45
+
42
46
  def reindex_all
43
47
  @exhibit.reindex_later
44
48
 
@@ -42,7 +42,7 @@ module Spotlight
42
42
 
43
43
  def invite
44
44
  user = Spotlight::Engine.user_class.invite!(email: invite_params[:user], skip_invitation: true) # don't deliver the invitation yet
45
- role = Spotlight::Role.create(exhibit: current_exhibit, user: user, role: invite_params[:role])
45
+ role = Spotlight::Role.create(resource: current_exhibit, user: user, role: invite_params[:role])
46
46
  if role.save
47
47
  user.deliver_invitation # now deliver it when we have saved the role
48
48
  redirect_to :back, notice: t(:'helpers.submit.role.updated')
@@ -0,0 +1,50 @@
1
+ module Spotlight
2
+ ##
3
+ # Global site configuration
4
+ class SitesController < Spotlight::ApplicationController
5
+ before_action :authenticate_user!
6
+ before_action :load_site
7
+ load_and_authorize_resource
8
+
9
+ def edit
10
+ end
11
+
12
+ def edit_exhibits
13
+ end
14
+
15
+ def update
16
+ if @site.update(site_params)
17
+ redirect_to exhibits_path, notice: t(:'helpers.submit.site.updated', model: @site.class.model_name.human.downcase)
18
+ else
19
+ flash[:alert] = @site.errors.full_messages.join('<br>'.html_safe)
20
+ render action: :edit
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def load_site
27
+ @site ||= Spotlight::Site.instance
28
+ end
29
+
30
+ def site_params
31
+ params.require(:site).permit(
32
+ :title,
33
+ :subtitle,
34
+ masthead_attributes: masthead_params,
35
+ exhibits_attributes: [:id, :weight]
36
+ )
37
+ end
38
+
39
+ def masthead_params
40
+ [
41
+ :display,
42
+ :source,
43
+ :image,
44
+ :remote_image_url,
45
+ :document_global_id,
46
+ :image_crop_x, :image_crop_y, :image_crop_w, :image_crop_h
47
+ ]
48
+ end
49
+ end
50
+ end
@@ -9,7 +9,8 @@ module Spotlight
9
9
  ##
10
10
  # Give the application name a chance to include the exhibit title
11
11
  def application_name
12
- name = super
12
+ name = current_site.title if current_site.title.present?
13
+ name ||= super
13
14
 
14
15
  if current_exhibit
15
16
  t :'spotlight.application_name', exhibit: current_exhibit.title, application_name: name
@@ -7,7 +7,7 @@ module Spotlight
7
7
  # if the exhibit is not searchable, we're not in an exhibit, or the top-level
8
8
  # exhibit masthead isn't being used (e.g. on a browse category)
9
9
  def should_render_spotlight_search_bar?
10
- exhibit_masthead? && current_exhibit && current_exhibit.searchable?
10
+ current_exhibit && current_exhibit.searchable?
11
11
  end
12
12
  end
13
13
  end
@@ -4,16 +4,25 @@ module Spotlight
4
4
  class ReindexJob < ActiveJob::Base
5
5
  queue_as :default
6
6
 
7
+ before_enqueue do |job|
8
+ resource_list(job.arguments.first).each(&:waiting!)
9
+ end
10
+
7
11
  def perform(exhibit_or_resources)
8
- resources = if exhibit_or_resources.is_a? Spotlight::Exhibit
9
- exhibit_or_resources.resources.find_each
10
- elsif exhibit_or_resources.is_a? Enumerable
11
- exhibit_or_resources
12
- else
13
- Array(exhibit_or_resources)
14
- end
12
+ resource_list(exhibit_or_resources).each(&:reindex)
13
+ end
14
+
15
+ private
15
16
 
16
- resources.each(&:reindex)
17
+ def resource_list(exhibit_or_resources)
18
+ case
19
+ when exhibit_or_resources.is_a?(Spotlight::Exhibit)
20
+ exhibit_or_resources.resources.find_each
21
+ when exhibit_or_resources.is_a?(Enumerable)
22
+ exhibit_or_resources
23
+ else
24
+ Array(exhibit_or_resources)
25
+ end
17
26
  end
18
27
  end
19
28
  end
@@ -5,21 +5,25 @@ module Spotlight
5
5
  extend ActiveSupport::Concern
6
6
  included do
7
7
  has_many :roles, class_name: 'Spotlight::Role', dependent: :destroy
8
- has_many :exhibits, class_name: 'Spotlight::Exhibit', through: :roles
8
+ has_many :exhibits, class_name: 'Spotlight::Exhibit', through: :roles, source: 'resource', source_type: 'Spotlight::Exhibit'
9
9
 
10
10
  before_create :add_default_roles
11
11
  end
12
12
 
13
13
  def superadmin?
14
- admin_roles.where(exhibit_id: nil).any?
14
+ roles.where(role: 'admin', resource: Spotlight::Site.instance).any?
15
+ end
16
+
17
+ def exhibit_roles
18
+ roles.where(resource_type: 'Spotlight::Exhibit')
15
19
  end
16
20
 
17
21
  def admin_roles
18
- roles.where(role: 'admin')
22
+ exhibit_roles.where(role: 'admin')
19
23
  end
20
24
 
21
25
  def add_default_roles
22
- roles.build role: 'admin' unless self.class.any?
26
+ roles.build role: 'admin', resource: Spotlight::Site.instance unless self.class.any?
23
27
  end
24
28
 
25
29
  def invite_pending?
@@ -13,8 +13,9 @@ module Spotlight
13
13
  can :manage, :all if user.superadmin?
14
14
 
15
15
  # exhibit admin
16
- can [:update, :import, :export, :destroy], Spotlight::Exhibit, id: user.admin_roles.pluck(:exhibit_id)
17
- can :manage, [Spotlight::BlacklightConfiguration, Spotlight::Role], exhibit_id: user.admin_roles.pluck(:exhibit_id)
16
+ can [:update, :import, :export, :destroy], Spotlight::Exhibit, id: user.admin_roles.pluck(:resource_id)
17
+ can :manage, Spotlight::BlacklightConfiguration, exhibit_id: user.admin_roles.pluck(:resource_id)
18
+ can :manage, Spotlight::Role, resource_id: user.admin_roles.pluck(:resource_id), resource_type: 'Spotlight::Exhibit'
18
19
 
19
20
  can :manage, PaperTrail::Version if user.roles.any?
20
21
 
@@ -25,11 +26,11 @@ module Spotlight
25
26
  Spotlight::Resource,
26
27
  Spotlight::Page,
27
28
  Spotlight::Contact,
28
- Spotlight::CustomField], exhibit_id: user.roles.pluck(:exhibit_id)
29
+ Spotlight::CustomField], exhibit_id: user.exhibit_roles.pluck(:resource_id)
29
30
 
30
31
  can :manage, Spotlight::Lock, by: user
31
32
 
32
- can [:read, :curate, :tag], Spotlight::Exhibit, id: user.roles.pluck(:exhibit_id)
33
+ can [:read, :curate, :tag], Spotlight::Exhibit, id: user.exhibit_roles.pluck(:resource_id)
33
34
 
34
35
  # public
35
36
  can :read, Spotlight::HomePage
@@ -29,7 +29,7 @@ module Spotlight
29
29
  has_many :main_navigations, dependent: :delete_all
30
30
  has_many :owned_taggings, class_name: 'ActsAsTaggableOn::Tagging', as: :tagger
31
31
  has_many :resources
32
- has_many :roles, dependent: :delete_all
32
+ has_many :roles, as: :resource, dependent: :delete_all
33
33
  has_many :searches, dependent: :destroy, extend: FriendlyId::FinderMethods
34
34
  has_many :solr_document_sidecars, dependent: :delete_all
35
35
  has_many :users, through: :roles, class_name: Spotlight::Engine.config.user_class
@@ -38,6 +38,7 @@ module Spotlight
38
38
  has_one :blacklight_configuration, class_name: 'Spotlight::BlacklightConfiguration', dependent: :delete
39
39
  has_one :home_page
40
40
 
41
+ belongs_to :site
41
42
  belongs_to :masthead, dependent: :destroy
42
43
  belongs_to :thumbnail, class_name: 'Spotlight::FeaturedImage', dependent: :destroy
43
44
 
@@ -49,6 +50,7 @@ module Spotlight
49
50
 
50
51
  before_save :sanitize_description, if: :description_changed?
51
52
  before_create :build_home_page
53
+ before_create :add_site_reference
52
54
  after_create :initialize_config
53
55
  after_create :initialize_browse
54
56
  after_create :initialize_main_navigation
@@ -93,8 +95,20 @@ module Spotlight
93
95
  self.thumbnail ||= searches.first.try(:thumbnail)
94
96
  end
95
97
 
98
+ def requested_by
99
+ roles.first.user if roles.first
100
+ end
101
+
102
+ def reindex_progress
103
+ @reindex_progress ||= ReindexProgress.new(resources.order('updated_at')) if resources
104
+ end
105
+
96
106
  protected
97
107
 
108
+ def add_site_reference
109
+ self.site ||= Spotlight::Site.instance
110
+ end
111
+
98
112
  def initialize_config
99
113
  self.blacklight_configuration ||= Spotlight::BlacklightConfiguration.create!
100
114
  end
@@ -0,0 +1,107 @@
1
+ module Spotlight
2
+ ##
3
+ # ReindexProgress is a class that models the progress of reindexing a list of resources
4
+ class ReindexProgress
5
+ def initialize(resource_list)
6
+ @resources = if resource_list.present?
7
+ resource_list
8
+ else
9
+ null_resources
10
+ end
11
+ end
12
+
13
+ def in_progress?
14
+ return unless finished
15
+ any_waiting? || finished > Spotlight::Engine.config.reindex_progress_window.minutes.ago
16
+ end
17
+
18
+ def started
19
+ @started ||= resources.first.indexed_at
20
+ end
21
+
22
+ def finished
23
+ @finished ||= completed_resources.last.updated_at
24
+ end
25
+
26
+ def total
27
+ @total ||= resources.map(&:last_indexed_estimate).sum
28
+ end
29
+
30
+ def completed
31
+ @completed ||= completed_resources.map(&:last_indexed_count).sum
32
+ end
33
+
34
+ def errored?
35
+ resources.any?(&:errored?)
36
+ end
37
+
38
+ def as_json(*)
39
+ {
40
+ in_progress: in_progress?,
41
+ started: localized_start_time,
42
+ total: total,
43
+ completed: completed,
44
+ updated_at: localized_finish_time,
45
+ errored: errored?
46
+ }
47
+ end
48
+
49
+ private
50
+
51
+ attr_reader :resources
52
+
53
+ def any_waiting?
54
+ resources.any?(&:waiting?)
55
+ end
56
+
57
+ def localized_start_time
58
+ return unless started
59
+ I18n.l(started, format: :short)
60
+ end
61
+
62
+ def localized_finish_time
63
+ return unless finished
64
+ I18n.l(finished, format: :short)
65
+ end
66
+
67
+ def completed_resources
68
+ if resources.try(:completed).present?
69
+ resources.completed
70
+ else
71
+ null_resources
72
+ end
73
+ end
74
+
75
+ def null_resources
76
+ [NullResource.new]
77
+ end
78
+
79
+ ##
80
+ # A NullObject for use in the absense of resources
81
+ class NullResource
82
+ def updated_at
83
+ nil
84
+ end
85
+
86
+ def indexed_at
87
+ nil
88
+ end
89
+
90
+ def last_indexed_estimate
91
+ 0
92
+ end
93
+
94
+ def last_indexed_count
95
+ 0
96
+ end
97
+
98
+ def waiting?
99
+ false
100
+ end
101
+
102
+ def errored?
103
+ false
104
+ end
105
+ end
106
+ end
107
+ end
@@ -18,8 +18,11 @@ module Spotlight
18
18
  :last_index_elapsed_time,
19
19
  :last_indexed_finished], coder: JSON
20
20
 
21
+ enum index_status: [:waiting, :completed, :errored]
22
+
21
23
  around_index :reindex_with_logging
22
24
  after_index :commit
25
+ after_index :completed!
23
26
 
24
27
  def becomes_provider
25
28
  klass = Spotlight::ResourceProvider.for_resource(self)
@@ -46,6 +49,7 @@ module Spotlight
46
49
  ##
47
50
  # Enqueue an asynchronous reindexing job for this resource
48
51
  def reindex_later
52
+ waiting!
49
53
  Spotlight::ReindexJob.perform_later(self)
50
54
  end
51
55
 
@@ -3,7 +3,7 @@ module Spotlight
3
3
  # Exhibit authorization roles
4
4
  class Role < ActiveRecord::Base
5
5
  ROLES = %w(admin curator)
6
- belongs_to :exhibit
6
+ belongs_to :resource, polymorphic: true
7
7
  belongs_to :user, class_name: Spotlight::Engine.config.user_class, autosave: true
8
8
  validates :role, inclusion: { in: ROLES }
9
9
  validates :user_key, presence: true
@@ -35,7 +35,7 @@ module Spotlight
35
35
  # validates :user, uniqueness: { scope: :exhibit}
36
36
  # but it puts the error message on the user_key instead of user so that the form will render correctly
37
37
  def user_must_be_unique
38
- errors.add(:user_key, 'already a member of this exhibit') if Spotlight::Role.where(exhibit_id: exhibit_id, user_id: user.id).where.not(id: id).any?
38
+ errors.add(:user_key, 'already a member of this exhibit') if Spotlight::Role.where(resource: resource, user: user).where.not(id: id).any?
39
39
  end
40
40
  end
41
41
  end
@@ -0,0 +1,16 @@
1
+ module Spotlight
2
+ ##
3
+ # Global spotlight configuration
4
+ class Site < ActiveRecord::Base
5
+ has_many :exhibits
6
+ has_many :roles, as: :resource
7
+
8
+ belongs_to :masthead, dependent: :destroy
9
+
10
+ accepts_nested_attributes_for :masthead, :exhibits
11
+
12
+ def self.instance
13
+ first || create
14
+ end
15
+ end
16
+ end
@@ -1,6 +1,6 @@
1
1
  <p><%= t("spotlight.invitation_mailer.invitation_instructions.hello", email: @resource.email) %></p>
2
2
 
3
- <p><%= t("spotlight.invitation_mailer.invitation_instructions.someone_invited_you", role: @resource.roles.first.role, exhibit_name: @resource.roles.first.exhibit.title, url: spotlight.exhibit_home_page_url(@resource.roles.first.exhibit)) %></p>
3
+ <p><%= t("spotlight.invitation_mailer.invitation_instructions.someone_invited_you", role: @resource.roles.first.role, exhibit_name: @resource.roles.first.resource.title, url: spotlight.exhibit_home_page_url(@resource.roles.first.resource)) %></p>
4
4
 
5
5
  <p><%= link_to t("spotlight.invitation_mailer.invitation_instructions.accept"), accept_invitation_url(@resource, :invitation_token => @token) %></p>
6
6
 
@@ -31,7 +31,7 @@
31
31
  </head>
32
32
  <body class="<%= render_body_class %>">
33
33
  <%= render partial: 'shared/header_navbar' %>
34
- <%= render partial: 'shared/exhibit_masthead_and_navbar' %>
34
+ <%= render partial: 'shared/masthead' %>
35
35
  <%= content_for?(:header_content) ? yield(:header_content) : "" %>
36
36
 
37
37
  <%= render partial: 'shared/ajax_modal' %>
@@ -1,4 +1,4 @@
1
- <div class="container">
1
+ <div class="container breadcrumbs-container">
2
2
  <div class="row">
3
3
  <div class="col-md-12">
4
4
  <%= render_breadcrumbs builder: Spotlight::BootstrapBreadcrumbsBuilder %>
@@ -1,14 +1,13 @@
1
- <div id="exhibit-navbar" class="navbar navbar-default<%= ' page-masthead' unless exhibit_masthead? %>" role="navigation">
1
+ <div id="exhibit-navbar" class="exhibit-navbar navbar navbar-default" role="navigation">
2
2
  <div class="container">
3
- <% unless exhibit_masthead? %>
3
+ <% if resource_masthead? %>
4
4
  <%= link_to(current_exhibit.title, spotlight.exhibit_path(current_exhibit), class: 'navbar-brand') %>
5
5
  <% end %>
6
+
6
7
  <ul class="nav navbar-nav">
7
- <% if current_exhibit %>
8
- <li class="<%= "active" if current_page?(current_exhibit) %>"><%= link_to t(:'spotlight.curation.nav.home'), current_exhibit %></li>
9
- <% current_exhibit.main_navigations.displayable.each do |navigation| %>
10
- <%= render partial: "shared/#{navigation.nav_type}_navbar", locals: {navigation: navigation} %>
11
- <% end %>
8
+ <li class="<%= "active" if current_page?(current_exhibit) %>"><%= link_to t(:'spotlight.curation.nav.home'), current_exhibit %></li>
9
+ <% current_exhibit.main_navigations.displayable.each do |navigation| %>
10
+ <%= render partial: "shared/#{navigation.nav_type}_navbar", locals: { navigation: navigation } %>
12
11
  <% end %>
13
12
  </ul>
14
13
  <% if should_render_spotlight_search_bar? %>