blacklight 9.0.0.beta8 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.env +3 -3
- data/.github/matrix.json +18 -34
- data/.github/workflows/lint.yml +1 -1
- data/.github/workflows/test.yml +4 -3
- data/.rubocop.yml +72 -7
- data/.rubocop_todo.yml +142 -268
- data/Gemfile +1 -1
- data/README.md +4 -11
- data/VERSION +1 -1
- data/app/assets/builds/blacklight.css +37 -23
- data/app/assets/images/blacklight/logo-dark.svg +184 -0
- data/app/assets/images/blacklight/logo.svg +184 -0
- data/app/assets/javascripts/blacklight/blacklight.esm.js +134 -48
- data/app/assets/javascripts/blacklight/blacklight.esm.js.map +1 -1
- data/app/assets/javascripts/blacklight/blacklight.js +134 -48
- data/app/assets/javascripts/blacklight/blacklight.js.map +1 -1
- data/app/assets/stylesheets/blacklight/_balanced_list.scss +1 -1
- data/app/assets/stylesheets/blacklight/_bootstrap_overrides.scss +17 -0
- data/app/assets/stylesheets/blacklight/_constraints.scss +2 -2
- data/app/assets/stylesheets/blacklight/_facets.scss +14 -10
- data/app/assets/stylesheets/blacklight/_header.scss +0 -4
- data/app/assets/stylesheets/blacklight/blacklight_defaults.scss +5 -9
- data/app/components/blacklight/facets/filters_component.rb +1 -1
- data/app/components/blacklight/facets/inclusive_constraint_component.html.erb +1 -1
- data/app/components/blacklight/facets/suggest_component.html.erb +17 -12
- data/app/components/blacklight/icons/checkmark_component.rb +16 -0
- data/app/components/blacklight/icons/circle_half_component.rb +16 -0
- data/app/components/blacklight/icons/moon_stars_component.rb +17 -0
- data/app/components/blacklight/icons/sun_component.rb +16 -0
- data/app/components/blacklight/metadata_field_component.rb +1 -1
- data/app/components/blacklight/search_navbar_component.html.erb +1 -1
- data/app/components/blacklight/theme_switcher_component.html.erb +31 -0
- data/app/components/blacklight/theme_switcher_component.rb +6 -0
- data/app/components/blacklight/top_navbar_component.html.erb +1 -1
- data/app/controllers/concerns/blacklight/bookmarks.rb +8 -14
- data/app/controllers/concerns/blacklight/catalog.rb +12 -14
- data/app/controllers/concerns/blacklight/controller.rb +54 -11
- data/app/controllers/concerns/blacklight/guest_user.rb +51 -0
- data/app/controllers/concerns/blacklight/search_context.rb +1 -1
- data/app/controllers/concerns/blacklight/search_history.rb +3 -1
- data/app/controllers/concerns/blacklight/searchable.rb +18 -0
- data/app/helpers/blacklight/layout_helper_behavior.rb +7 -0
- data/app/javascript/blacklight-frontend/checkbox_submit.js +2 -2
- data/app/javascript/blacklight-frontend/color_theme_switcher.js +85 -0
- data/app/javascript/blacklight-frontend/facet_suggest.js +4 -1
- data/app/javascript/blacklight-frontend/index.js +2 -0
- data/app/models/concerns/blacklight/document/dublin_core.rb +2 -2
- data/app/models/concerns/blacklight/document.rb +1 -1
- data/app/models/concerns/blacklight/user.rb +36 -0
- data/app/presenters/blacklight/field_presenter.rb +1 -1
- data/app/presenters/blacklight/rendering/microdata.rb +1 -0
- data/app/services/blacklight/bookmarks_search_builder.rb +4 -4
- data/app/services/blacklight/search_service.rb +50 -35
- data/app/values/blacklight/types.rb +1 -1
- data/app/views/catalog/_email_form.html.erb +3 -3
- data/app/views/catalog/_home_text.html.erb +1 -1
- data/app/views/catalog/_sms_form.html.erb +4 -4
- data/app/views/catalog/index.rss.builder +4 -4
- data/app/views/catalog/opensearch.json.jbuilder +3 -0
- data/app/views/catalog/opensearch.xml.builder +2 -2
- data/app/views/layouts/blacklight/base.html.erb +13 -1
- data/app/views/shared/_user_util_links.html.erb +18 -5
- data/blacklight.gemspec +3 -3
- data/config/locales/blacklight.ar.yml +5 -0
- data/config/locales/blacklight.ca.yml +5 -0
- data/config/locales/blacklight.de.yml +6 -2
- data/config/locales/blacklight.en.yml +5 -0
- data/config/locales/blacklight.es.yml +5 -0
- data/config/locales/blacklight.fr.yml +5 -0
- data/config/locales/blacklight.hu.yml +5 -0
- data/config/locales/blacklight.it.yml +6 -2
- data/config/locales/blacklight.nl.yml +5 -0
- data/config/locales/blacklight.pt-BR.yml +6 -2
- data/config/locales/blacklight.sq.yml +5 -0
- data/config/locales/blacklight.zh.yml +5 -0
- data/lib/blacklight/component.rb +1 -1
- data/lib/blacklight/configuration/action_config_map_entry.rb +31 -0
- data/lib/blacklight/configuration/display_field.rb +1 -0
- data/lib/blacklight/configuration/field.rb +6 -6
- data/lib/blacklight/configuration/fields.rb +1 -1
- data/lib/blacklight/configuration/sort_field.rb +2 -2
- data/lib/blacklight/configuration.rb +30 -23
- data/lib/blacklight/parameters.rb +1 -1
- data/lib/blacklight/routes/searchable.rb +1 -1
- data/lib/blacklight/search_builder.rb +208 -10
- data/lib/blacklight/search_state.rb +4 -0
- data/lib/blacklight/solr/request.rb +8 -1
- data/lib/blacklight/solr/response/group_response.rb +1 -1
- data/lib/blacklight/solr/response/spelling.rb +8 -8
- data/lib/blacklight/solr/response.rb +2 -21
- data/lib/blacklight/solr/search_builder_behavior.rb +59 -16
- data/lib/blacklight.rb +2 -2
- data/lib/generators/blacklight/assets/importmap_generator.rb +2 -2
- data/lib/generators/blacklight/assets_generator.rb +1 -1
- data/lib/generators/blacklight/controller_generator.rb +1 -1
- data/lib/generators/blacklight/document_generator.rb +1 -1
- data/lib/generators/blacklight/install_generator.rb +6 -4
- data/lib/generators/blacklight/models_generator.rb +1 -1
- data/lib/generators/blacklight/search_builder_generator.rb +1 -1
- data/lib/generators/blacklight/solr_generator.rb +1 -1
- data/lib/generators/blacklight/templates/catalog_controller.rb +21 -2
- data/lib/generators/blacklight/templates/solr/conf/schema.xml +264 -178
- data/lib/generators/blacklight/templates/solr/conf/solrconfig.xml +6 -14
- data/lib/generators/blacklight/test_support_generator.rb +1 -1
- data/lib/generators/blacklight/user_generator.rb +66 -5
- data/lib/railties/blacklight.rake +4 -4
- data/package.json +2 -2
- data/spec/components/blacklight/document/action_component_spec.rb +1 -1
- data/spec/components/blacklight/document/group_component_spec.rb +1 -1
- data/spec/components/blacklight/document_component_spec.rb +9 -9
- data/spec/components/blacklight/facet_component_spec.rb +2 -2
- data/spec/components/blacklight/facets/filters_component_spec.rb +1 -1
- data/spec/components/blacklight/facets/index_navigation_component_spec.rb +1 -2
- data/spec/components/blacklight/facets/list_component_spec.rb +1 -1
- data/spec/components/blacklight/facets/suggest_component_spec.rb +2 -3
- data/spec/components/blacklight/metadata_field_component_spec.rb +26 -0
- data/spec/components/blacklight/skip_link_component_spec.rb +2 -2
- data/spec/controllers/application_controller_spec.rb +71 -0
- data/spec/controllers/bookmarks_controller_spec.rb +18 -5
- data/spec/controllers/catalog_controller_spec.rb +7 -5
- data/spec/features/advanced_search_spec.rb +29 -16
- data/spec/features/alternate_controller_spec.rb +4 -4
- data/spec/features/axe_spec.rb +34 -22
- data/spec/features/bookmarks_spec.rb +13 -13
- data/spec/features/dark_mode_spec.rb +42 -0
- data/spec/features/did_you_mean_spec.rb +27 -27
- data/spec/features/facet_missing_spec.rb +3 -3
- data/spec/features/facets_spec.rb +9 -0
- data/spec/features/record_view_spec.rb +14 -14
- data/spec/features/search_context_spec.rb +8 -8
- data/spec/features/search_filters_spec.rb +16 -16
- data/spec/features/search_formats_spec.rb +2 -2
- data/spec/features/search_history_spec.rb +11 -11
- data/spec/features/search_pagination_spec.rb +17 -17
- data/spec/features/search_results_spec.rb +3 -3
- data/spec/features/search_sort_spec.rb +4 -4
- data/spec/features/search_spec.rb +20 -20
- data/spec/helpers/blacklight/url_helper_behavior_spec.rb +15 -15
- data/spec/helpers/blacklight_helper_spec.rb +1 -1
- data/spec/helpers/catalog_helper_spec.rb +7 -7
- data/spec/integration/generators/blacklight/user_generator_spec.rb +60 -0
- data/spec/lib/blacklight/search_state/pivot_filter_field_spec.rb +1 -1
- data/spec/models/blacklight/configurable_spec.rb +1 -1
- data/spec/models/blacklight/configuration_spec.rb +0 -19
- data/spec/models/blacklight/document/dublin_core_spec.rb +2 -2
- data/spec/models/blacklight/facet_paginator_spec.rb +1 -1
- data/spec/models/blacklight/search_builder_spec.rb +26 -4
- data/spec/models/blacklight/solr/repository_spec.rb +10 -2
- data/spec/models/blacklight/solr/search_builder_behavior_spec.rb +165 -7
- data/spec/models/blacklight/user_spec.rb +32 -5
- data/spec/models/bookmark_spec.rb +1 -1
- data/spec/models/record_mailer_spec.rb +8 -8
- data/spec/models/search_spec.rb +1 -1
- data/spec/models/solr_document_spec.rb +4 -0
- data/spec/presenters/blacklight/index_presenter_spec.rb +1 -0
- data/spec/presenters/blacklight/rendering/pipeline_spec.rb +1 -0
- data/spec/presenters/blacklight/show_presenter_spec.rb +1 -0
- data/spec/presenters/blacklight/thumbnail_presenter_spec.rb +1 -0
- data/spec/services/blacklight/search_service_spec.rb +7 -7
- data/spec/spec_helper.rb +3 -12
- data/spec/support/features/session_helpers.rb +13 -4
- data/spec/support/features/turbo_helpers.rb +13 -0
- data/spec/support/features.rb +2 -0
- data/spec/support/user_helpers.rb +24 -0
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +1 -1
- data/spec/views/catalog/_document.html.erb_spec.rb +4 -4
- data/spec/views/catalog/_paginate_compact.html.erb_spec.rb +2 -2
- data/spec/views/catalog/_sort_and_per_page.html.erb_spec.rb +5 -5
- data/spec/views/catalog/index.html.erb_spec.rb +2 -2
- data/spec/views/catalog/show.html.erb_spec.rb +3 -4
- data/spec/views/shared/_user_util_links.html.erb_spec.rb +56 -0
- data/template.demo.rb +2 -1
- metadata +30 -29
- data/.hound.yml +0 -4
- data/.jshintrc +0 -22
- data/app/assets/images/blacklight/logo.png +0 -0
- data/app/controllers/concerns/blacklight/facetable.rb +0 -34
- data/app/models/facet_search_builder.rb +0 -5
- data/app/services/blacklight/facet_search_service.rb +0 -44
- data/lib/blacklight/abstract_search_builder.rb +0 -154
- data/lib/blacklight/facet_search_builder.rb +0 -18
- data/lib/blacklight/solr/facet_search_builder_behavior.rb +0 -62
- data/spec/models/blacklight/facet_search_builder_spec.rb +0 -19
- data/spec/models/blacklight/solr/facet_search_builder_behavior_spec.rb +0 -929
|
@@ -9,11 +9,12 @@ module Blacklight::Catalog
|
|
|
9
9
|
include Blacklight::Configurable
|
|
10
10
|
include Blacklight::SearchContext
|
|
11
11
|
include Blacklight::Searchable
|
|
12
|
-
include Blacklight::Facetable
|
|
13
12
|
|
|
14
13
|
# The following code is executed when someone includes blacklight::catalog in their
|
|
15
14
|
# own controller.
|
|
16
15
|
included do
|
|
16
|
+
allow_unauthenticated_access raise: false if respond_to?(:allow_unauthenticated_access)
|
|
17
|
+
|
|
17
18
|
if respond_to? :helper_method
|
|
18
19
|
helper_method :sms_mappings, :has_search_parameters?
|
|
19
20
|
end
|
|
@@ -38,7 +39,7 @@ module Blacklight::Catalog
|
|
|
38
39
|
# get a single document from the index
|
|
39
40
|
# to add responses for formats other than html or json see _Blacklight::Document::Export_
|
|
40
41
|
def show
|
|
41
|
-
@document =
|
|
42
|
+
@document = retrieve_document(params[:id])
|
|
42
43
|
|
|
43
44
|
respond_to do |format|
|
|
44
45
|
format.html { @search_context = setup_next_and_previous_documents }
|
|
@@ -55,7 +56,7 @@ module Blacklight::Catalog
|
|
|
55
56
|
def raw
|
|
56
57
|
raise(ActionController::RoutingError, 'Not Found') unless blacklight_config.raw_endpoint.enabled
|
|
57
58
|
|
|
58
|
-
@document =
|
|
59
|
+
@document = retrieve_document(params[:id])
|
|
59
60
|
render json: @document
|
|
60
61
|
end
|
|
61
62
|
|
|
@@ -82,11 +83,11 @@ module Blacklight::Catalog
|
|
|
82
83
|
raise ActionController::RoutingError, 'Not Found' unless @facet
|
|
83
84
|
|
|
84
85
|
@response = if params[:query_fragment].present?
|
|
85
|
-
|
|
86
|
+
search_service.facet_suggest_response(@facet.key, params[:query_fragment])
|
|
86
87
|
else
|
|
87
|
-
|
|
88
|
+
search_service.facet_field_response(@facet.key)
|
|
88
89
|
end
|
|
89
|
-
|
|
90
|
+
# @display_facet is a Blacklight::Solr::Response::Facets::FacetField
|
|
90
91
|
@display_facet = @response.aggregations[@facet.field]
|
|
91
92
|
|
|
92
93
|
# @presenter is a Blacklight::FacetFieldPresenter
|
|
@@ -108,7 +109,10 @@ module Blacklight::Catalog
|
|
|
108
109
|
def opensearch
|
|
109
110
|
respond_to do |format|
|
|
110
111
|
format.xml { render layout: false }
|
|
111
|
-
format.json
|
|
112
|
+
format.json do
|
|
113
|
+
@response = retrieve_search_results
|
|
114
|
+
@presenter = json_presenter(@response)
|
|
115
|
+
end
|
|
112
116
|
end
|
|
113
117
|
end
|
|
114
118
|
|
|
@@ -121,7 +125,7 @@ module Blacklight::Catalog
|
|
|
121
125
|
# @return [Array] first value is a Blacklight::Solr::Response and the second
|
|
122
126
|
# is a list of documents
|
|
123
127
|
def action_documents
|
|
124
|
-
@documents =
|
|
128
|
+
@documents = retrieve_documents(params[:id])
|
|
125
129
|
raise Blacklight::Exceptions::RecordNotFound if @documents.blank?
|
|
126
130
|
|
|
127
131
|
@documents
|
|
@@ -146,12 +150,6 @@ module Blacklight::Catalog
|
|
|
146
150
|
blacklight_config.index.json_presenter_class.new(repository_response, blacklight_config)
|
|
147
151
|
end
|
|
148
152
|
|
|
149
|
-
# This method may be overridden to customize search behavior.
|
|
150
|
-
# @return [Blacklight::Solr::Response] the solr response object
|
|
151
|
-
def retrieve_search_results
|
|
152
|
-
search_service.search_results
|
|
153
|
-
end
|
|
154
|
-
|
|
155
153
|
#
|
|
156
154
|
# non-routable methods ->
|
|
157
155
|
#
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
module Blacklight::Controller
|
|
6
6
|
extend ActiveSupport::Concern
|
|
7
7
|
|
|
8
|
+
include Blacklight::GuestUser
|
|
9
|
+
|
|
8
10
|
included do
|
|
9
11
|
include ActiveSupport::Callbacks
|
|
10
12
|
|
|
@@ -17,6 +19,7 @@ module Blacklight::Controller
|
|
|
17
19
|
|
|
18
20
|
if respond_to? :helper_method
|
|
19
21
|
helper_method :current_user_session, :current_user, :current_or_guest_user
|
|
22
|
+
helper_method :blacklight_account_path, :blacklight_login_path, :blacklight_login_url, :blacklight_logout_link_options, :blacklight_logout_path
|
|
20
23
|
|
|
21
24
|
# extra head content
|
|
22
25
|
helper_method :has_user_authentication_provider?
|
|
@@ -28,8 +31,7 @@ module Blacklight::Controller
|
|
|
28
31
|
# Which class to use for the search state. You can subclass SearchState if you
|
|
29
32
|
# want to override any of the methods (e.g. SearchState#url_for_document)
|
|
30
33
|
# TODO: move to Searchable
|
|
31
|
-
class_attribute :search_state_class
|
|
32
|
-
self.search_state_class = Blacklight::SearchState
|
|
34
|
+
class_attribute :search_state_class, default: Blacklight::SearchState
|
|
33
35
|
end
|
|
34
36
|
|
|
35
37
|
# @private
|
|
@@ -56,7 +58,7 @@ module Blacklight::Controller
|
|
|
56
58
|
end
|
|
57
59
|
|
|
58
60
|
# This must be on every controller that uses the layout, because it is used in
|
|
59
|
-
# the header to draw Blacklight::SearchNavbarComponent
|
|
61
|
+
# the header to draw Blacklight::SearchNavbarComponent
|
|
60
62
|
# @return [Blacklight::SearchState] a memoized instance of the parameter state.
|
|
61
63
|
def search_state
|
|
62
64
|
@search_state ||= search_state_class.new(params, blacklight_config, self)
|
|
@@ -71,8 +73,8 @@ module Blacklight::Controller
|
|
|
71
73
|
end
|
|
72
74
|
|
|
73
75
|
def search_action_path *args
|
|
74
|
-
if args.first.is_a?
|
|
75
|
-
args.first[:only_path] = true
|
|
76
|
+
if args.first.is_a?(Hash) && args.first[:only_path].nil?
|
|
77
|
+
args.first[:only_path] = true
|
|
76
78
|
end
|
|
77
79
|
|
|
78
80
|
search_action_url(*args)
|
|
@@ -86,10 +88,10 @@ module Blacklight::Controller
|
|
|
86
88
|
|
|
87
89
|
# Here's a stub implementation we'll add if it isn't provided for us
|
|
88
90
|
def current_or_guest_user
|
|
89
|
-
if defined? super
|
|
90
|
-
|
|
91
|
+
if defined?(super) && (user = super)
|
|
92
|
+
user
|
|
91
93
|
elsif has_user_authentication_provider?
|
|
92
|
-
current_user
|
|
94
|
+
current_user || guest_user
|
|
93
95
|
end
|
|
94
96
|
end
|
|
95
97
|
alias blacklight_current_or_guest_user current_or_guest_user
|
|
@@ -98,13 +100,13 @@ module Blacklight::Controller
|
|
|
98
100
|
#
|
|
99
101
|
#
|
|
100
102
|
def has_user_authentication_provider?
|
|
101
|
-
respond_to?
|
|
103
|
+
respond_to?(:current_user) || blacklight_login_path.present? || blacklight_logout_path.present?
|
|
102
104
|
end
|
|
103
105
|
|
|
104
106
|
##
|
|
105
107
|
# When a user logs in, transfer any saved searches or bookmarks to the current_user
|
|
106
108
|
def transfer_guest_to_user
|
|
107
|
-
return unless respond_to?(:current_user) &&
|
|
109
|
+
return unless respond_to?(:current_user) && current_user && guest_user
|
|
108
110
|
|
|
109
111
|
current_user_searches = current_user.searches.pluck(:query_params)
|
|
110
112
|
current_user_bookmarks = current_user.bookmarks.pluck(:document_id)
|
|
@@ -134,10 +136,51 @@ module Blacklight::Controller
|
|
|
134
136
|
|
|
135
137
|
redirect_to(root_url) && return unless has_user_authentication_provider?
|
|
136
138
|
|
|
137
|
-
|
|
139
|
+
store_location_for_rails_authentication
|
|
140
|
+
redirect_to(root_url) && return unless blacklight_login_url
|
|
141
|
+
|
|
142
|
+
redirect_to blacklight_login_url(referer: request.fullpath)
|
|
138
143
|
end
|
|
139
144
|
|
|
140
145
|
def determine_layout
|
|
141
146
|
'blacklight'
|
|
142
147
|
end
|
|
148
|
+
|
|
149
|
+
def blacklight_login_path(options = {})
|
|
150
|
+
if respond_to?(:new_user_session_path)
|
|
151
|
+
new_user_session_path(options)
|
|
152
|
+
elsif respond_to?(:new_session_path)
|
|
153
|
+
new_session_path(options)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def blacklight_login_url(options = {})
|
|
158
|
+
if respond_to?(:new_user_session_url)
|
|
159
|
+
new_user_session_url(options)
|
|
160
|
+
elsif respond_to?(:new_session_url)
|
|
161
|
+
new_session_url(options)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def blacklight_logout_path
|
|
166
|
+
if respond_to?(:destroy_user_session_path)
|
|
167
|
+
destroy_user_session_path
|
|
168
|
+
elsif respond_to?(:session_path)
|
|
169
|
+
session_path
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def blacklight_logout_link_options
|
|
174
|
+
return {} unless respond_to?(:session_path)
|
|
175
|
+
|
|
176
|
+
{ data: { turbo_method: :delete } }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def blacklight_account_path
|
|
180
|
+
edit_user_registration_path if respond_to?(:edit_user_registration_path)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def store_location_for_rails_authentication
|
|
184
|
+
session[:return_to_after_authenticating] = request.url if respond_to?(:new_session_url)
|
|
185
|
+
end
|
|
143
186
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Blacklight
|
|
4
|
+
# Provides a persisted "guest" user for anonymous visitors so they can
|
|
5
|
+
# accumulate bookmarks and searches that are transferred to a real account
|
|
6
|
+
# upon sign-in (see Blacklight::Controller#transfer_guest_to_user).
|
|
7
|
+
#
|
|
8
|
+
# This is the fallback used by Rails' built-in authentication. When Devise
|
|
9
|
+
# together with the +devise-guests+ gem is used, that pair provides its own
|
|
10
|
+
# +#guest_user+ / +#current_or_guest_user+ (and knows how to build a valid
|
|
11
|
+
# Devise guest, e.g. skipping password validation). They sit higher in the
|
|
12
|
+
# ancestor chain than this concern, so +#guest_user+ defers to +super+ when
|
|
13
|
+
# available and only falls back to the implementation below otherwise. The
|
|
14
|
+
# helper methods below are intentionally named to avoid colliding with
|
|
15
|
+
# +devise-guests+' own +#create_guest_user+.
|
|
16
|
+
module GuestUser
|
|
17
|
+
extend ActiveSupport::Concern
|
|
18
|
+
|
|
19
|
+
# Returns a persisted guest user for the current session, creating one on
|
|
20
|
+
# first access. Keyed off the session so it survives across requests.
|
|
21
|
+
# Returns +nil+ if a guest cannot be created (e.g. no user model).
|
|
22
|
+
def guest_user
|
|
23
|
+
return super if defined?(super)
|
|
24
|
+
return @blacklight_guest_user if defined?(@blacklight_guest_user) && @blacklight_guest_user
|
|
25
|
+
|
|
26
|
+
@blacklight_guest_user = find_blacklight_guest_user || create_blacklight_guest_user
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def find_blacklight_guest_user
|
|
32
|
+
return unless (id = session[:blacklight_guest_user_id])
|
|
33
|
+
|
|
34
|
+
blacklight_guest_user_class.find_by(id: id)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def create_blacklight_guest_user
|
|
38
|
+
user = blacklight_guest_user_class.create_guest_user!
|
|
39
|
+
session[:blacklight_guest_user_id] = user.id
|
|
40
|
+
user
|
|
41
|
+
rescue ActiveRecord::RecordInvalid
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# The model class that guest users are created from. Defaults to +User+,
|
|
46
|
+
# consistent with Blacklight::TokenBasedUser. Override to use another model.
|
|
47
|
+
def blacklight_guest_user_class
|
|
48
|
+
::User
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -25,7 +25,7 @@ module Blacklight::SearchContext
|
|
|
25
25
|
|
|
26
26
|
# Returns a list of Searches from the ids in the user's history.
|
|
27
27
|
def searches_from_history
|
|
28
|
-
session[:history].blank? ? ::Search.none : ::Search.where(id: session[:history]).order(
|
|
28
|
+
session[:history].blank? ? ::Search.none : ::Search.where(id: session[:history]).order(updated_at: :desc)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
# GET previous and next document json for the document specified by
|
|
@@ -7,6 +7,8 @@ module Blacklight
|
|
|
7
7
|
include Blacklight::SearchContext
|
|
8
8
|
|
|
9
9
|
included do
|
|
10
|
+
allow_unauthenticated_access raise: false if respond_to?(:allow_unauthenticated_access)
|
|
11
|
+
|
|
10
12
|
copy_blacklight_config_from(CatalogController)
|
|
11
13
|
end
|
|
12
14
|
|
|
@@ -23,7 +25,7 @@ module Blacklight
|
|
|
23
25
|
flash[:error] = I18n.t('blacklight.search_history.clear.failure')
|
|
24
26
|
end
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
redirect_back_or_to(blacklight.search_history_path)
|
|
27
29
|
end
|
|
28
30
|
end
|
|
29
31
|
end
|
|
@@ -24,6 +24,24 @@ module Blacklight::Searchable
|
|
|
24
24
|
search_service_class.new(config: blacklight_config, search_state: search_state, **search_service_context)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# This method may be overridden to customize search behavior.
|
|
28
|
+
# @return [Blacklight::Solr::Response] the solr response object
|
|
29
|
+
def retrieve_search_results
|
|
30
|
+
search_service.search_results
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# This method may be overridden to customize search behavior.
|
|
34
|
+
# @return [Blacklight::Document] the solr document object
|
|
35
|
+
def retrieve_document(id)
|
|
36
|
+
search_service.fetch(id)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# This method may be overridden to customize search behavior.
|
|
40
|
+
# @return [Array<Blacklight::Document>] an array of solr document objects
|
|
41
|
+
def retrieve_documents(ids)
|
|
42
|
+
search_service.fetch(Array(ids))
|
|
43
|
+
end
|
|
44
|
+
|
|
27
45
|
# Override this method on the class that includes Blacklight::Searchable to provide more context to the search service if necessary.
|
|
28
46
|
# For example, if your search builder needs to be aware of the current user, override this method to return a hash including the current user.
|
|
29
47
|
# Then the search builder could use some property about the current user to construct a constraint on the search.
|
|
@@ -106,5 +106,12 @@ module Blacklight
|
|
|
106
106
|
def extra_body_classes
|
|
107
107
|
@extra_body_classes ||= ["blacklight-#{controller.controller_name}", "blacklight-#{[controller.controller_name, controller.action_name].join('-')}"]
|
|
108
108
|
end
|
|
109
|
+
|
|
110
|
+
##
|
|
111
|
+
# True if dark mode support is enabled in the configuration.
|
|
112
|
+
# @see Blacklight::Configuration#dark_mode_support
|
|
113
|
+
def dark_mode_support?
|
|
114
|
+
blacklight_config.dark_mode_support
|
|
115
|
+
end
|
|
109
116
|
end
|
|
110
117
|
end
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
attribute (HTML5-style doc-*) that contains the id/primary key
|
|
11
11
|
of the object in question -- used by plugin for a unique value for
|
|
12
12
|
DOM id's.
|
|
13
|
-
Uses HTML for a checkbox compatible with Bootstrap
|
|
13
|
+
Uses HTML for a checkbox compatible with Bootstrap.
|
|
14
14
|
new CheckboxSubmit(document.querySelector('form.something')).render()
|
|
15
15
|
*/
|
|
16
16
|
export default class CheckboxSubmit {
|
|
@@ -36,7 +36,7 @@ export default class CheckboxSubmit {
|
|
|
36
36
|
}).then((json) => {
|
|
37
37
|
this.labelTarget.removeAttribute('disabled')
|
|
38
38
|
this.checkboxTarget.removeAttribute('disabled')
|
|
39
|
-
// For accessibility return keyboard focus
|
|
39
|
+
// For accessibility return keyboard focus
|
|
40
40
|
// back to the checkbox after form submission
|
|
41
41
|
this.checkboxTarget.focus()
|
|
42
42
|
this.updateStateFor(!this.checked)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Color mode toggler for Blacklight
|
|
3
|
+
* Based on Bootstrap's color mode toggler (https://getbootstrap.com/docs/5.3/customize/color-modes/#javascript)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import Core from 'blacklight-frontend/core'
|
|
7
|
+
|
|
8
|
+
const ColorThemeSwitcher = (() => {
|
|
9
|
+
'use strict'
|
|
10
|
+
|
|
11
|
+
const getStoredTheme = () => localStorage.getItem('theme')
|
|
12
|
+
const setStoredTheme = theme => localStorage.setItem('theme', theme)
|
|
13
|
+
|
|
14
|
+
const getPreferredTheme = () => {
|
|
15
|
+
const storedTheme = getStoredTheme()
|
|
16
|
+
if (storedTheme) {
|
|
17
|
+
return storedTheme
|
|
18
|
+
}
|
|
19
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const setTheme = theme => {
|
|
23
|
+
if (theme === 'auto') {
|
|
24
|
+
document.documentElement.setAttribute('data-bs-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
|
|
25
|
+
} else {
|
|
26
|
+
document.documentElement.setAttribute('data-bs-theme', theme)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const showActiveTheme = (theme, focus = false) => {
|
|
31
|
+
const themeSwitcher = document.querySelector('#bl-theme-switcher')
|
|
32
|
+
if (!themeSwitcher) return
|
|
33
|
+
|
|
34
|
+
// Reset all dropdown items
|
|
35
|
+
document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
|
|
36
|
+
element.classList.remove('active')
|
|
37
|
+
element.setAttribute('aria-pressed', 'false')
|
|
38
|
+
const check = element.querySelector('.bl-theme-check')
|
|
39
|
+
if (check) check.classList.add('d-none')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
// Activate the selected item
|
|
43
|
+
const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`)
|
|
44
|
+
if (btnToActive) {
|
|
45
|
+
btnToActive.classList.add('active')
|
|
46
|
+
btnToActive.setAttribute('aria-pressed', 'true')
|
|
47
|
+
const check = btnToActive.querySelector('.bl-theme-check')
|
|
48
|
+
if (check) check.classList.remove('d-none')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Swap the toggle button icon
|
|
52
|
+
themeSwitcher.querySelectorAll('.bl-theme-icon').forEach(icon => icon.classList.add('d-none'))
|
|
53
|
+
const activeIcon = themeSwitcher.querySelector(`.bl-theme-icon[data-bl-theme-icon="${theme}"]`)
|
|
54
|
+
if (activeIcon) activeIcon.classList.remove('d-none')
|
|
55
|
+
|
|
56
|
+
themeSwitcher.setAttribute('aria-label', `Toggle theme (${theme})`)
|
|
57
|
+
|
|
58
|
+
if (focus) {
|
|
59
|
+
themeSwitcher.focus()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
|
64
|
+
const storedTheme = getStoredTheme()
|
|
65
|
+
if (storedTheme !== 'light' && storedTheme !== 'dark') {
|
|
66
|
+
setTheme(getPreferredTheme())
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
document.addEventListener('click', e => {
|
|
71
|
+
const btn = e.target.closest('[data-bs-theme-value]')
|
|
72
|
+
if (!btn) return
|
|
73
|
+
|
|
74
|
+
const theme = btn.getAttribute('data-bs-theme-value')
|
|
75
|
+
setStoredTheme(theme)
|
|
76
|
+
setTheme(theme)
|
|
77
|
+
showActiveTheme(theme, true)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
Core.onLoad(() => showActiveTheme(getPreferredTheme()))
|
|
81
|
+
|
|
82
|
+
return { setTheme, getPreferredTheme, showActiveTheme }
|
|
83
|
+
})()
|
|
84
|
+
|
|
85
|
+
export default ColorThemeSwitcher
|
|
@@ -16,10 +16,13 @@ const FacetSuggest = async (e) => {
|
|
|
16
16
|
|
|
17
17
|
// Drop facet.page so a filtered suggestion list will always start on page 1
|
|
18
18
|
url.searchParams.delete('facet.page');
|
|
19
|
+
// add our queryFragment for facet filtering
|
|
20
|
+
url.searchParams.append('query_fragment', queryFragment);
|
|
21
|
+
|
|
19
22
|
const facetSearchParams = url.searchParams.toString();
|
|
20
23
|
const basePathComponent = url.pathname.split('/')[1];
|
|
21
24
|
|
|
22
|
-
const urlToFetch = `/${basePathComponent}/facet_suggest/${facetField}
|
|
25
|
+
const urlToFetch = `/${basePathComponent}/facet_suggest/${facetField}?${facetSearchParams}`;
|
|
23
26
|
|
|
24
27
|
const response = await fetch(urlToFetch);
|
|
25
28
|
if (response.ok) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import BookmarkToggle from 'blacklight-frontend/bookmark_toggle'
|
|
2
2
|
import ButtonFocus from 'blacklight-frontend/button_focus'
|
|
3
|
+
import ColorThemeSwitcher from 'blacklight-frontend/color_theme_switcher'
|
|
3
4
|
import FacetSuggest from 'blacklight-frontend/facet_suggest'
|
|
4
5
|
import Modal from 'blacklight-frontend/modal'
|
|
5
6
|
import SearchContext from 'blacklight-frontend/search_context'
|
|
@@ -8,6 +9,7 @@ import Core from 'blacklight-frontend/core'
|
|
|
8
9
|
export default {
|
|
9
10
|
BookmarkToggle,
|
|
10
11
|
ButtonFocus,
|
|
12
|
+
ColorThemeSwitcher,
|
|
11
13
|
FacetSuggest,
|
|
12
14
|
Modal,
|
|
13
15
|
SearchContext,
|
|
@@ -36,8 +36,8 @@ module Blacklight::Document::DublinCore
|
|
|
36
36
|
xml.target!
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
alias export_as_xml export_as_oai_dc_xml
|
|
40
|
+
alias export_as_dc_xml export_as_oai_dc_xml
|
|
41
41
|
|
|
42
42
|
private
|
|
43
43
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Blacklight::User
|
|
4
4
|
extend ActiveSupport::Concern
|
|
5
|
+
|
|
5
6
|
# SEE ALSO: The lib/blacklight/generator/user_generator.rb class for where this
|
|
6
7
|
# is generated into the hosting application.
|
|
7
8
|
included do
|
|
@@ -11,6 +12,41 @@ module Blacklight::User
|
|
|
11
12
|
has_many :searches, dependent: :destroy, as: :user
|
|
12
13
|
end
|
|
13
14
|
|
|
15
|
+
class_methods do
|
|
16
|
+
# The column that holds the user's login/identifier. Rails' authentication
|
|
17
|
+
# generator uses +email_address+, while Devise and others use +email+.
|
|
18
|
+
def blacklight_email_column
|
|
19
|
+
column_names.include?("email_address") ? :email_address : :email
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Create a persisted "guest" user for an anonymous visitor, so they can
|
|
23
|
+
# accumulate bookmarks and searches that are transferred to a real account
|
|
24
|
+
# upon sign-in. Works whether the model uses +email+ or +email_address+,
|
|
25
|
+
# and supplies a random password when the model authenticates with a
|
|
26
|
+
# password (either +has_secure_password+'s +password_digest+ column or
|
|
27
|
+
# Devise's +encrypted_password+ column).
|
|
28
|
+
def create_guest_user!
|
|
29
|
+
attributes = { blacklight_email_column => "guest_#{Time.current.to_i}_#{SecureRandom.hex(4)}@example.com" }
|
|
30
|
+
|
|
31
|
+
if authenticates_with_password?
|
|
32
|
+
password = SecureRandom.hex(16)
|
|
33
|
+
attributes[:password] = password
|
|
34
|
+
attributes[:password_confirmation] = password
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
create!(attributes)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# True when the model stores a password (Rails' +has_secure_password+ via
|
|
41
|
+
# +password_digest+, or Devise's +database_authenticatable+ via
|
|
42
|
+
# +encrypted_password+). In either case the model exposes a virtual
|
|
43
|
+
# +password+ / +password_confirmation+ attribute that must be populated to
|
|
44
|
+
# satisfy validation.
|
|
45
|
+
def authenticates_with_password?
|
|
46
|
+
column_names.intersect?(%w[password_digest encrypted_password])
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
14
50
|
def bookmarks_for_documents documents = []
|
|
15
51
|
if documents.any?
|
|
16
52
|
bookmarks.where(document_type: documents.first.class.base_class.to_s, document_id: documents.map(&:id))
|
|
@@ -31,7 +31,7 @@ module Blacklight
|
|
|
31
31
|
|
|
32
32
|
attr_reader :view_context, :document, :field_config, :except_operations, :options
|
|
33
33
|
|
|
34
|
-
delegate :key, :component, to: :field_config
|
|
34
|
+
delegate :key, :component, :layout_component, to: :field_config
|
|
35
35
|
|
|
36
36
|
# @return [Array<String>]
|
|
37
37
|
def render
|
|
@@ -6,17 +6,17 @@ module Blacklight
|
|
|
6
6
|
##
|
|
7
7
|
# Filters the query to only include the bookmarked items
|
|
8
8
|
#
|
|
9
|
-
# @param [
|
|
9
|
+
# @param [Blacklight::Solr::Request] request_parameters
|
|
10
10
|
#
|
|
11
11
|
# @return [void]
|
|
12
|
-
def bookmarked(
|
|
13
|
-
solr_parameters[:fq] ||= []
|
|
12
|
+
def bookmarked(request_parameters)
|
|
14
13
|
bookmarks = @scope.context.fetch(:bookmarks)
|
|
15
14
|
return unless bookmarks
|
|
16
15
|
|
|
17
16
|
document_ids = bookmarks.collect { |b| b.document_id.to_s }
|
|
18
|
-
|
|
17
|
+
request_parameters.append_ids_filter(blacklight_config.document_model.unique_key, document_ids)
|
|
19
18
|
end
|
|
19
|
+
|
|
20
20
|
self.default_processor_chain += [:bookmarked]
|
|
21
21
|
end
|
|
22
22
|
end
|