blacklight 8.0.0.beta4 → 8.0.0.beta5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +3 -1
  3. data/VERSION +1 -1
  4. data/app/assets/javascripts/blacklight/blacklight.esm.js +5 -5
  5. data/app/assets/javascripts/blacklight/blacklight.esm.js.map +1 -1
  6. data/app/assets/javascripts/blacklight/blacklight.js +5 -5
  7. data/app/assets/javascripts/blacklight/blacklight.js.map +1 -1
  8. data/app/components/blacklight/document_component.rb +26 -19
  9. data/app/components/blacklight/document_metadata_component.html.erb +4 -2
  10. data/app/components/blacklight/document_metadata_component.rb +8 -2
  11. data/app/components/blacklight/metadata_field_layout_component.rb +7 -4
  12. data/app/components/blacklight/metadata_field_plain_text_layout_component.rb +17 -0
  13. data/app/components/blacklight/response/sort_component.rb +1 -1
  14. data/app/components/blacklight/response/spellcheck_component.rb +1 -1
  15. data/app/components/blacklight/response/view_type_button_component.rb +1 -1
  16. data/app/components/blacklight/response/view_type_component.rb +1 -1
  17. data/app/components/blacklight/search_context/server_applied_params_component.html.erb +4 -0
  18. data/app/components/blacklight/search_context/server_applied_params_component.rb +13 -0
  19. data/app/components/blacklight/{search_context_component.html.erb → search_context/server_item_pagination_component.html.erb} +0 -0
  20. data/app/components/blacklight/search_context/server_item_pagination_component.rb +71 -0
  21. data/app/components/blacklight/system/dropdown_component.rb +1 -1
  22. data/app/components/blacklight/system/flash_message_component.rb +1 -1
  23. data/app/components/blacklight/system/modal_component.rb +1 -1
  24. data/app/controllers/concerns/blacklight/bookmarks.rb +1 -1
  25. data/app/controllers/concerns/blacklight/search_context.rb +46 -6
  26. data/app/helpers/blacklight/blacklight_helper_behavior.rb +0 -96
  27. data/app/helpers/blacklight/catalog_helper_behavior.rb +27 -54
  28. data/app/helpers/blacklight/component_helper_behavior.rb +0 -11
  29. data/app/helpers/blacklight/document_helper_behavior.rb +76 -0
  30. data/app/helpers/blacklight/layout_helper_behavior.rb +59 -0
  31. data/app/helpers/blacklight/url_helper_behavior.rb +11 -3
  32. data/app/javascript/blacklight/modal.js +5 -5
  33. data/app/models/record_mailer.rb +19 -15
  34. data/app/presenters/blacklight/document_presenter.rb +13 -11
  35. data/app/presenters/blacklight/index_presenter.rb +1 -1
  36. data/app/presenters/blacklight/rendering/abstract_step.rb +4 -0
  37. data/app/presenters/blacklight/rendering/join.rb +7 -1
  38. data/app/presenters/blacklight/rendering/link_to_facet.rb +1 -1
  39. data/app/presenters/blacklight/rendering/microdata.rb +1 -1
  40. data/app/services/blacklight/search_service.rb +2 -1
  41. data/app/views/catalog/_document.atom.builder +1 -1
  42. data/app/views/catalog/_document.html.erb +2 -7
  43. data/app/views/catalog/_document_list.html.erb +2 -1
  44. data/app/views/catalog/_show_main_content.html.erb +3 -9
  45. data/app/views/catalog/index.html.erb +3 -0
  46. data/app/views/catalog/show.html.erb +1 -6
  47. data/app/views/record_mailer/email_record.text.erb +4 -3
  48. data/app/views/record_mailer/sms_record.text.erb +4 -4
  49. data/config/locales/blacklight.ar.yml +0 -6
  50. data/config/locales/blacklight.ca.yml +0 -6
  51. data/config/locales/blacklight.de.yml +0 -6
  52. data/config/locales/blacklight.en.yml +0 -6
  53. data/config/locales/blacklight.es.yml +0 -6
  54. data/config/locales/blacklight.fr.yml +0 -6
  55. data/config/locales/blacklight.hu.yml +0 -6
  56. data/config/locales/blacklight.it.yml +0 -6
  57. data/config/locales/blacklight.nl.yml +0 -6
  58. data/config/locales/blacklight.pt-BR.yml +0 -6
  59. data/config/locales/blacklight.sq.yml +0 -6
  60. data/config/locales/blacklight.zh.yml +0 -6
  61. data/lib/blacklight/configuration/session_tracking_config.rb +45 -0
  62. data/lib/blacklight/configuration.rb +8 -4
  63. data/lib/blacklight/routes/searchable.rb +1 -0
  64. data/lib/generators/blacklight/templates/solr_document.rb +0 -6
  65. data/package.json +1 -1
  66. data/spec/components/blacklight/document_component_spec.rb +9 -4
  67. data/spec/components/blacklight/search_context/server_applied_params_component_spec.rb +29 -0
  68. data/spec/components/blacklight/{search_context_component_spec.rb → search_context/server_item_pagination_component_spec.rb} +15 -4
  69. data/spec/controllers/bookmarks_controller_spec.rb +1 -1
  70. data/spec/controllers/catalog_controller_spec.rb +17 -5
  71. data/spec/features/component_template_override_spec.rb +8 -0
  72. data/spec/features/modal_spec.rb +12 -0
  73. data/spec/helpers/blacklight/url_helper_behavior_spec.rb +1 -1
  74. data/spec/helpers/blacklight_helper_spec.rb +0 -56
  75. data/spec/helpers/catalog_helper_spec.rb +56 -0
  76. data/spec/lib/blacklight/configuration/session_tracking_config_spec.rb +38 -0
  77. data/spec/models/record_mailer_spec.rb +36 -23
  78. data/spec/presenters/blacklight/document_presenter_spec.rb +1 -0
  79. data/spec/presenters/blacklight/field_presenter_spec.rb +2 -2
  80. data/spec/presenters/blacklight/index_presenter_spec.rb +1 -0
  81. data/spec/presenters/blacklight/show_presenter_spec.rb +1 -0
  82. data/spec/presenters/pipeline_spec.rb +13 -2
  83. data/spec/routing/catalog_routing_spec.rb +4 -0
  84. data/spec/test_app_templates/lib/generators/test_app_generator.rb +8 -0
  85. data/spec/views/catalog/_document.html.erb_spec.rb +1 -1
  86. data/spec/views/catalog/_document_list.html.erb_spec.rb +7 -2
  87. metadata +19 -12
  88. data/app/components/blacklight/search_context_component.rb +0 -68
  89. data/app/models/concerns/blacklight/document/email.rb +0 -27
  90. data/app/models/concerns/blacklight/document/sms.rb +0 -25
  91. data/spec/models/blacklight/document/email_spec.rb +0 -57
  92. data/spec/models/blacklight/document/sms_spec.rb +0 -58
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blacklight
4
+ module SearchContext
5
+ class ServerItemPaginationComponent < Blacklight::Component
6
+ with_collection_parameter :search_context
7
+
8
+ def initialize(search_context:, search_session:, current_document:)
9
+ @search_context = search_context
10
+ @search_session = search_session
11
+ @current_document_id = current_document.id
12
+ end
13
+
14
+ def render?
15
+ @search_context.present? && (@search_context[:prev] || @search_context[:next]) && (@search_session['document_id'] == @current_document_id)
16
+ end
17
+
18
+ ##
19
+ # Displays "showing X of Y items" message.
20
+ #
21
+ # @return [String]
22
+ def item_page_entry_info
23
+ t('blacklight.search.entry_pagination_info.other', current: number_with_delimiter(count),
24
+ total: number_with_delimiter(total),
25
+ count: total).html_safe
26
+ end
27
+
28
+ def link_to_previous_document(previous_document = nil, classes: 'previous', **link_opts)
29
+ previous_document ||= @search_context[:prev]
30
+ link_opts = session_tracking_params(previous_document, count - 1, per_page: per_page, search_id: search_id).merge(class: classes, rel: 'prev').merge(link_opts)
31
+ link_to_unless previous_document.nil?, raw(t('views.pagination.previous')), url_for_document(previous_document), link_opts do
32
+ tag.span raw(t('views.pagination.previous')), class: 'previous'
33
+ end
34
+ end
35
+
36
+ def link_to_next_document(next_document = nil, classes: 'next', **link_opts)
37
+ next_document ||= @search_context[:next]
38
+ link_opts = session_tracking_params(next_document, count + 1, per_page: per_page, search_id: search_id).merge(class: classes, rel: 'next').merge(link_opts)
39
+ link_to_unless next_document.nil?, raw(t('views.pagination.next')), url_for_document(next_document), link_opts do
40
+ tag.span raw(t('views.pagination.next')), class: 'next'
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def count
47
+ @search_session['counter'].to_i
48
+ end
49
+
50
+ def per_page
51
+ @search_session['per_page']
52
+ end
53
+
54
+ def total
55
+ @search_session['total'].to_i
56
+ end
57
+
58
+ def search_id
59
+ @search_session['id'] || helpers.current_search_session&.id
60
+ end
61
+
62
+ def session_tracking_params(...)
63
+ helpers.session_tracking_params(...)
64
+ end
65
+
66
+ def url_for_document(...)
67
+ helpers.search_state.url_for_document(...)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Blacklight
4
4
  module System
5
- class DropdownComponent < ViewComponent::Base
5
+ class DropdownComponent < Blacklight::Component
6
6
  renders_one :button, (lambda do |classes:, label:|
7
7
  button_tag class: classes, aria: { expanded: false }, data: { toggle: 'dropdown', 'bs-toggle': 'dropdown' } do
8
8
  safe_join([label, content_tag(:span, '', class: 'caret')])
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Blacklight
4
4
  module System
5
- class FlashMessageComponent < ViewComponent::Base
5
+ class FlashMessageComponent < Blacklight::Component
6
6
  renders_one :message
7
7
 
8
8
  with_collection_parameter :message
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Blacklight
4
4
  module System
5
- class ModalComponent < ViewComponent::Base
5
+ class ModalComponent < Blacklight::Component
6
6
  renders_one :prefix
7
7
  renders_one :header
8
8
  renders_one :title
@@ -15,7 +15,7 @@ module Blacklight::Bookmarks
15
15
 
16
16
  before_action :verify_user
17
17
 
18
- blacklight_config.track_search_session = false
18
+ blacklight_config.track_search_session.storage = false
19
19
  blacklight_config.http_method = Blacklight::Engine.config.blacklight.bookmarks_http_method
20
20
  blacklight_config.add_results_collection_tool(:clear_bookmarks_widget)
21
21
 
@@ -28,6 +28,28 @@ module Blacklight::SearchContext
28
28
  session[:history].blank? ? ::Search.none : ::Search.where(id: session[:history]).order("updated_at desc")
29
29
  end
30
30
 
31
+ # GET previous and next document json for the document specified by
32
+ # the counter param in current search
33
+ def page_links
34
+ counter_param = params.delete(:counter)
35
+ @page_link_data = {}
36
+ if counter_param
37
+ index = counter_param.to_i - 1
38
+ response, documents = search_service.previous_and_next_documents_for_search index, search_state.reset_search
39
+ if documents.detect(&:present?)
40
+ @page_link_data[:prev] = page_links_document_path(documents.first, index)
41
+ @page_link_data[:next] = page_links_document_path(documents.last, index + 2)
42
+ end
43
+ if response&.total && response.total.positive?
44
+ @page_link_data[:counterRaw] = counter_param
45
+ @page_link_data[:counterDelimited] = helpers.number_with_delimiter(counter_param.to_i)
46
+ @page_link_data[:totalRaw] = response.total
47
+ @page_link_data[:totalDelimited] = helpers.number_with_delimiter(response.total)
48
+ end
49
+ end
50
+ render json: @page_link_data
51
+ end
52
+
31
53
  private
32
54
 
33
55
  # sets up the session[:search] hash if it doesn't already exist
@@ -85,6 +107,8 @@ module Blacklight::SearchContext
85
107
  end
86
108
 
87
109
  def find_or_initialize_search_session_from_params params
110
+ return unless blacklight_config.track_search_session.storage == 'server'
111
+
88
112
  params_copy = params.reject { |k, v| nonpersisted_search_session_params.include?(k.to_sym) || v.blank? }
89
113
 
90
114
  return if params_copy.reject { |k, _v| [:action, :controller].include? k.to_sym }.blank?
@@ -127,15 +151,31 @@ module Blacklight::SearchContext
127
151
  # calls setup_previous_document then setup_next_document.
128
152
  # used in the show action for single view pagination.
129
153
  def setup_next_and_previous_documents
130
- if search_session['counter'] && current_search_session
131
- index = search_session['counter'].to_i - 1
132
- response, documents = search_service.previous_and_next_documents_for_search index, search_state.reset(current_search_session.query_params)
154
+ return { counter: params[:counter] } if setup_next_and_previous_on_client?
155
+ return nil unless setup_next_and_previous_on_server?
133
156
 
134
- search_session['total'] = response.total
135
- { prev: documents.first, next: documents.last }
136
- end
157
+ index = search_session['counter'].to_i - 1
158
+ response, documents = search_service.previous_and_next_documents_for_search index, search_state.reset(current_search_session.query_params)
159
+
160
+ search_session['total'] = response.total
161
+ { prev: documents.first, next: documents.last }
137
162
  rescue Blacklight::Exceptions::InvalidRequest => e
138
163
  logger&.warn "Unable to setup next and previous documents: #{e}"
139
164
  nil
140
165
  end
166
+
167
+ def setup_next_and_previous_on_server?
168
+ search_session['counter'] && current_search_session && blacklight_config.track_search_session.storage == 'server'
169
+ end
170
+
171
+ def setup_next_and_previous_on_client?
172
+ params[:counter] && blacklight_config.track_search_session.storage == 'client'
173
+ end
174
+
175
+ def page_links_document_path(document, counter)
176
+ return nil unless document
177
+ return search_state.url_for_document(document, counter: counter) if blacklight_config.view_config(:show).route
178
+
179
+ solr_document_path(document, counter: counter)
180
+ end
141
181
  end
@@ -6,8 +6,6 @@ module Blacklight::BlacklightHelperBehavior
6
6
  include Blacklight::LayoutHelperBehavior
7
7
  include Blacklight::IconHelperBehavior
8
8
 
9
- # @!group Layout helpers
10
-
11
9
  ##
12
10
  # Get the name of this application from an i18n string
13
11
  # key: blacklight.application_name
@@ -23,63 +21,6 @@ module Blacklight::BlacklightHelperBehavior
23
21
  end
24
22
  end
25
23
 
26
- ##
27
- # Get the page's HTML title
28
- #
29
- # @return [String]
30
- def render_page_title
31
- (content_for(:page_title) if content_for?(:page_title)) || @page_title || application_name
32
- end
33
-
34
- ##
35
- # Create <link rel="alternate"> links from a documents dynamically
36
- # provided export formats.
37
- #
38
- # Returns empty string if no links available.
39
- #
40
- # @param [SolrDocument] document
41
- # @param [Hash] options
42
- # @option options [Boolean] :unique ensures only one link is output for every
43
- # content type, e.g. as required by atom
44
- # @option options [Array<String>] :exclude array of format shortnames to not include in the output
45
- # @return [String]
46
- def render_link_rel_alternates(document = @document, options = {})
47
- return if document.nil?
48
-
49
- document_presenter(document).link_rel_alternates(options)
50
- end
51
-
52
- ##
53
- # Render classes for the <body> element
54
- # @return [String]
55
- def render_body_class
56
- extra_body_classes.join " "
57
- end
58
-
59
- ##
60
- # List of classes to be applied to the <body> element
61
- # @see render_body_class
62
- # @return [Array<String>]
63
- def extra_body_classes
64
- @extra_body_classes ||= ["blacklight-#{controller.controller_name}", "blacklight-#{[controller.controller_name, controller.action_name].join('-')}"]
65
- end
66
-
67
- ##
68
- # Get the current "view type" (and ensure it is a valid type)
69
- #
70
- # @param [Hash] query_params the query parameters to check
71
- # @return [Symbol]
72
- def document_index_view_type query_params = params
73
- view_param = query_params[:view]
74
- view_param ||= session[:preferred_view]
75
- if view_param && document_index_views.key?(view_param.to_sym)
76
- view_param.to_sym
77
- else
78
- default_document_index_view_type
79
- end
80
- end
81
-
82
- # @!group Search result helpers
83
24
  ##
84
25
  # Render a partial of an arbitrary format inside a
85
26
  # template of a different format. (e.g. render an HTML
@@ -97,45 +38,8 @@ module Blacklight::BlacklightHelperBehavior
97
38
  nil
98
39
  end
99
40
 
100
- ##
101
- # Should we render a grouped response (because the response
102
- # contains a grouped response instead of the normal response)
103
- #
104
- # Default to false if there's no response object available (sometimes the case
105
- # for tests, but might happen in other circumstances too..)
106
- # @return [Boolean]
107
- def render_grouped_response? response = @response
108
- response&.grouped?
109
- end
110
-
111
- ##
112
- # Returns a document presenter for the given document
113
- def document_presenter(document)
114
- document_presenter_class(document).new(document, self)
115
- end
116
-
117
- ##
118
- # Override this method if you want to use a differnet presenter for your documents
119
- # @param [Blacklight::Document] _document optional, here for extension + backwards compatibility only
120
- def document_presenter_class(_document = nil)
121
- case action_name
122
- when 'show', 'citation'
123
- blacklight_config.view_config(:show, action_name: action_name).document_presenter_class
124
- else
125
- blacklight_config.view_config(document_index_view_type, action_name: action_name).document_presenter_class
126
- end
127
- end
128
-
129
41
  # @return [Class]
130
42
  def search_bar_presenter_class
131
43
  blacklight_config.view_config(action_name: :index).search_bar_presenter_class
132
44
  end
133
-
134
- # @!group Layout helpers
135
- ##
136
- # Open Search discovery tag for HTML <head> links
137
- # @return [String]
138
- def opensearch_description_tag title, href
139
- tag :link, href: href, title: title, type: "application/opensearchdescription+xml", rel: "search"
140
- end
141
45
  end
@@ -4,6 +4,7 @@
4
4
  module Blacklight::CatalogHelperBehavior
5
5
  include Blacklight::ConfigurationHelperBehavior
6
6
  include Blacklight::ComponentHelperBehavior
7
+ include Blacklight::DocumentHelperBehavior
7
8
  include Blacklight::FacetsHelperBehavior
8
9
  include Blacklight::RenderPartialsHelperBehavior
9
10
 
@@ -113,40 +114,6 @@ module Blacklight::CatalogHelperBehavior
113
114
  (@response.rows if @response && @response.rows > 0) || params.fetch(:per_page, blacklight_config.default_per_page).to_i
114
115
  end
115
116
 
116
- ##
117
- # Get the classes to add to a document's div
118
- #
119
- # @param [Blacklight::Document] document
120
- # @return [String]
121
- def render_document_class(document = @document)
122
- types = document_presenter(document).display_type
123
- return if types.blank?
124
-
125
- Array(types).compact.map do |t|
126
- "#{document_class_prefix}#{t.try(:parameterize) || t}"
127
- end.join(' ')
128
- end
129
-
130
- ##
131
- # Return a prefix for the document classes infered from the document
132
- # @see #render_document_class
133
- # @return [String]
134
- def document_class_prefix
135
- 'blacklight-'
136
- end
137
-
138
- ##
139
- # Render the sidebar partial for a document
140
- # This is used as an integration point by downstream apps to add to the
141
- # default sidebar.
142
- # See: https://github.com/geoblacklight/geoblacklight/blob/7d3c31c7af3362879b97e2c1351a2496c728c59c/app/helpers/blacklight_helper.rb#L7
143
- #
144
- # @param [SolrDocument] document
145
- # @return [String]
146
- def render_document_sidebar_partial(document)
147
- render 'show_sidebar', document: document
148
- end
149
-
150
117
  ##
151
118
  # Should we display the sort and per page widget?
152
119
  #
@@ -167,26 +134,6 @@ module Blacklight::CatalogHelperBehavior
167
134
  response.limit_value > 0
168
135
  end
169
136
 
170
- ##
171
- # return the Bookmarks on a set of documents (all bookmarks on the page)
172
- # @private
173
- # @return [Enumerable<Bookmark>]
174
- def current_bookmarks
175
- @current_bookmarks ||= begin
176
- documents = @document.presence || @response.documents
177
- current_or_guest_user.bookmarks_for_documents(Array(documents)).to_a
178
- end
179
- end
180
- private :current_bookmarks
181
-
182
- ##
183
- # Check if the document is in the user's bookmarks
184
- # @param [Blacklight::Document] document
185
- # @return [Boolean]
186
- def bookmarked? document
187
- current_bookmarks.any? { |x| x.document_id == document.id && x.document_type == document.class }
188
- end
189
-
190
137
  # Render an html <title> appropriate string for a selected facet field and values
191
138
  #
192
139
  # @see #render_search_to_page_title
@@ -233,6 +180,32 @@ module Blacklight::CatalogHelperBehavior
233
180
  constraints.join(' / ')
234
181
  end
235
182
 
183
+ ##
184
+ # Should we render a grouped response (because the response
185
+ # contains a grouped response instead of the normal response)
186
+ #
187
+ # Default to false if there's no response object available (sometimes the case
188
+ # for tests, but might happen in other circumstances too..)
189
+ # @return [Boolean]
190
+ def render_grouped_response? response = @response
191
+ response&.grouped?
192
+ end
193
+
194
+ ##
195
+ # Get the current "view type" (and ensure it is a valid type)
196
+ #
197
+ # @param [Hash] query_params the query parameters to check
198
+ # @return [Symbol]
199
+ def document_index_view_type query_params = params || {}
200
+ view_param = query_params[:view]
201
+ view_param ||= session[:preferred_view] if respond_to?(:session)
202
+ if view_param && document_index_views.key?(view_param.to_sym)
203
+ view_param.to_sym
204
+ else
205
+ default_document_index_view_type
206
+ end
207
+ end
208
+
236
209
  private
237
210
 
238
211
  # @param [String] format
@@ -2,17 +2,6 @@
2
2
 
3
3
  module Blacklight
4
4
  module ComponentHelperBehavior
5
- ##
6
- # Render "document actions" area for navigation header
7
- # (normally renders "Saved Searches", "History", "Bookmarks")
8
- # These things are added by add_nav_action
9
- #
10
- # @param [Hash] options
11
- # @return [String]
12
- def render_nav_actions(options = {}, &block)
13
- render_filtered_partials(blacklight_config.navbar.partials, options, &block)
14
- end
15
-
16
5
  ##
17
6
  # Render "document actions" area for search results view
18
7
  # (normally renders next to title in the list view)
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Helper methods for catalog-like controllers that work with documents
4
+ module Blacklight::DocumentHelperBehavior
5
+ ##
6
+ # Get the classes to add to a document's div
7
+ #
8
+ # @param [Blacklight::Document] document
9
+ # @return [String]
10
+ def render_document_class(document = @document)
11
+ types = document_presenter(document).display_type
12
+ return if types.blank?
13
+
14
+ Array(types).compact.map do |t|
15
+ "#{document_class_prefix}#{t.try(:parameterize) || t}"
16
+ end.join(' ')
17
+ end
18
+
19
+ ##
20
+ # Return a prefix for the document classes infered from the document
21
+ # @see #render_document_class
22
+ # @return [String]
23
+ def document_class_prefix
24
+ 'blacklight-'
25
+ end
26
+
27
+ ##
28
+ # Render the sidebar partial for a document
29
+ # This is used as an integration point by downstream apps to add to the
30
+ # default sidebar.
31
+ # See: https://github.com/geoblacklight/geoblacklight/blob/7d3c31c7af3362879b97e2c1351a2496c728c59c/app/helpers/blacklight_helper.rb#L7
32
+ #
33
+ # @param [SolrDocument] document
34
+ # @return [String]
35
+ def render_document_sidebar_partial(document)
36
+ render 'show_sidebar', document: document
37
+ end
38
+
39
+ ##
40
+ # return the Bookmarks on a set of documents (all bookmarks on the page)
41
+ # @private
42
+ # @return [Enumerable<Bookmark>]
43
+ def current_bookmarks
44
+ @current_bookmarks ||= begin
45
+ documents = @document.presence || @response.documents
46
+ current_or_guest_user.bookmarks_for_documents(Array(documents)).to_a
47
+ end
48
+ end
49
+ private :current_bookmarks
50
+
51
+ ##
52
+ # Check if the document is in the user's bookmarks
53
+ # @param [Blacklight::Document] document
54
+ # @return [Boolean]
55
+ def bookmarked? document
56
+ current_bookmarks.any? { |x| x.document_id == document.id && x.document_type == document.class }
57
+ end
58
+
59
+ ##
60
+ # Returns a document presenter for the given document
61
+ def document_presenter(document)
62
+ document_presenter_class(document).new(document, self)
63
+ end
64
+
65
+ ##
66
+ # Override this method if you want to use a differnet presenter for your documents
67
+ # @param [Blacklight::Document] _document optional, here for extension + backwards compatibility only
68
+ def document_presenter_class(_document = nil)
69
+ case action_name
70
+ when 'show', 'citation'
71
+ blacklight_config.view_config(:show, action_name: action_name).document_presenter_class
72
+ else
73
+ blacklight_config.view_config(document_index_view_type, action_name: action_name).document_presenter_class
74
+ end
75
+ end
76
+ end
@@ -47,5 +47,64 @@ module Blacklight
47
47
  def container_classes
48
48
  'container'
49
49
  end
50
+
51
+ ##
52
+ # Render "document actions" area for navigation header
53
+ # (normally renders "Saved Searches", "History", "Bookmarks")
54
+ # These things are added by add_nav_action
55
+ #
56
+ # @param [Hash] options
57
+ # @return [String]
58
+ def render_nav_actions(options = {}, &block)
59
+ render_filtered_partials(blacklight_config.navbar.partials, options, &block)
60
+ end
61
+
62
+ ##
63
+ # Open Search discovery tag for HTML <head> links
64
+ # @return [String]
65
+ def opensearch_description_tag title, href
66
+ tag :link, href: href, title: title, type: "application/opensearchdescription+xml", rel: "search"
67
+ end
68
+
69
+ ##
70
+ # Get the page's HTML title
71
+ #
72
+ # @return [String]
73
+ def render_page_title
74
+ (content_for(:page_title) if content_for?(:page_title)) || @page_title || application_name
75
+ end
76
+
77
+ ##
78
+ # Create <link rel="alternate"> links from a documents dynamically
79
+ # provided export formats.
80
+ #
81
+ # Returns empty string if no links available.
82
+ #
83
+ # @param [SolrDocument] document
84
+ # @param [Hash] options
85
+ # @option options [Boolean] :unique ensures only one link is output for every
86
+ # content type, e.g. as required by atom
87
+ # @option options [Array<String>] :exclude array of format shortnames to not include in the output
88
+ # @return [String]
89
+ def render_link_rel_alternates(document = @document, options = {})
90
+ return if document.nil?
91
+
92
+ document_presenter(document).link_rel_alternates(options)
93
+ end
94
+
95
+ ##
96
+ # Render classes for the <body> element
97
+ # @return [String]
98
+ def render_body_class
99
+ extra_body_classes.join " "
100
+ end
101
+
102
+ ##
103
+ # List of classes to be applied to the <body> element
104
+ # @see render_body_class
105
+ # @return [Array<String>]
106
+ def extra_body_classes
107
+ @extra_body_classes ||= ["blacklight-#{controller.controller_name}", "blacklight-#{[controller.controller_name, controller.action_name].join('-')}"]
108
+ end
50
109
  end
51
110
  end
@@ -43,16 +43,22 @@ module Blacklight::UrlHelperBehavior
43
43
  # session_tracking_params(SolrDocument.new(id: 123), 7)
44
44
  # => { data: { context_href: '/catalog/123/track?counter=7&search_id=999' } }
45
45
  def session_tracking_params document, counter, per_page: search_session['per_page'], search_id: current_search_session&.id
46
- path = session_tracking_path(document, per_page: params.fetch(:per_page, per_page), counter: counter, search_id: search_id, document_id: document&.id)
46
+ path_params = { per_page: params.fetch(:per_page, per_page), counter: counter, search_id: search_id }
47
+ if blacklight_config.track_search_session.storage == 'server'
48
+ path_params[:document_id] = document&.id
49
+ path_params[:search_id] = search_id
50
+ end
51
+ path = session_tracking_path(document, path_params)
47
52
  return {} if path.nil?
48
53
 
49
- { data: { context_href: path } }
54
+ context_method = blacklight_config.track_search_session.storage == 'client' ? 'get' : 'post'
55
+ { data: { context_href: path, context_method: context_method } }
50
56
  end
51
57
 
52
58
  ##
53
59
  # Get the URL for tracking search sessions across pages using polymorphic routing
54
60
  def session_tracking_path document, params = {}
55
- return if document.nil? || !blacklight_config&.track_search_session
61
+ return if document.nil? || !blacklight_config.track_search_session.storage
56
62
 
57
63
  if main_app.respond_to?(controller_tracking_method)
58
64
  return main_app.public_send(controller_tracking_method, params.merge(id: document))
@@ -63,6 +69,8 @@ module Blacklight::UrlHelperBehavior
63
69
  end
64
70
 
65
71
  def controller_tracking_method
72
+ return blacklight_config.track_search_session.url_helper if blacklight_config.track_search_session.url_helper
73
+
66
74
  "track_#{controller_name}_path"
67
75
  end
68
76
 
@@ -32,7 +32,7 @@
32
32
 
33
33
  <div data-blacklight-modal="container">
34
34
  <div class="modal-header">
35
- <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
35
+ <button type="button" class="close" data-bl-dismiss="modal" aria-hidden="true">×</button>
36
36
  <h3 class="modal-title">Request Placed</h3>
37
37
  </div>
38
38
 
@@ -43,7 +43,7 @@
43
43
 
44
44
 
45
45
  <div class="modal-footer">
46
- <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
46
+ <button type="button" class="btn btn-secondary" data-bl-dismiss="modal">Close</button>
47
47
  </div>
48
48
  </div>
49
49
 
@@ -89,7 +89,7 @@ const Modal = (() => {
89
89
 
90
90
  const contents = `<div class="modal-header">
91
91
  <div class="modal-title">There was a problem with your request.</div>
92
- <button type="button" class="blacklight-modal-close btn-close close" data-dismiss="modal" data-bs-dismiss="modal" aria-label="Close">
92
+ <button type="button" class="blacklight-modal-close btn-close close" data-bl-dismiss="modal" aria-label="Close">
93
93
  <span aria-hidden="true" class="visually-hidden">&times;</span>
94
94
  </button>
95
95
  </div>
@@ -139,9 +139,9 @@ const Modal = (() => {
139
139
  // into one selector with a comma, so if something matches BOTH selectors, it
140
140
  // still only gets the event handler called once.
141
141
  document.addEventListener('click', (e) => {
142
- if (e.target.matches(`${modal.triggerLinkSelector}, ${modal.preserveLinkSelector}`))
142
+ if (e.target.closest(`${modal.triggerLinkSelector}, ${modal.preserveLinkSelector}`))
143
143
  modal.modalAjaxLinkClick(e)
144
- else if (e.target.matches('[data-bl-dismiss="modal"]'))
144
+ else if (e.target.closest('[data-bl-dismiss="modal"]'))
145
145
  modal.hide()
146
146
  })
147
147
  };