blacklight 9.0.0 → 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 +12 -18
- 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 +2 -9
- 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 +128 -45
- data/app/assets/javascripts/blacklight/blacklight.esm.js.map +1 -1
- data/app/assets/javascripts/blacklight/blacklight.js +128 -45
- 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/inclusive_constraint_component.html.erb +1 -1
- 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 +9 -10
- 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/color_theme_switcher.js +85 -0
- 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 +46 -33
- 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 -19
- data/lib/blacklight/parameters.rb +1 -1
- data/lib/blacklight/search_builder.rb +58 -19
- 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 +3 -9
- 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 +20 -1
- 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/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 +1 -2
- 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/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 +15 -3
- data/spec/models/blacklight/solr/repository_spec.rb +10 -2
- data/spec/models/blacklight/solr/search_builder_behavior_spec.rb +3 -5
- 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 -22
- data/.hound.yml +0 -4
- data/.jshintrc +0 -22
- data/app/assets/images/blacklight/logo.png +0 -0
|
@@ -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
|
|
@@ -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
|
|
@@ -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
|
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
# SearchService returns search results from the repository
|
|
4
4
|
module Blacklight
|
|
5
5
|
class SearchService
|
|
6
|
+
# @params [Blacklight::Configuration] config
|
|
7
|
+
# @params [Blacklight::SearchState] search_state
|
|
8
|
+
# @params [Class] search_builder_class a class that inherits from Blacklight::SearchBuilder
|
|
9
|
+
# @params [Hash] context any data the search builder needs to access. For example, the current user.
|
|
6
10
|
def initialize(config:, search_state:, search_builder_class: config.search_builder_class, **context)
|
|
7
11
|
@blacklight_config = config
|
|
8
12
|
@search_state = search_state
|
|
9
|
-
@user_params = @search_state.params
|
|
13
|
+
@user_params = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(@search_state.params,
|
|
14
|
+
'Use @search_state.params instead of @user_params',
|
|
15
|
+
Blacklight.deprecation)
|
|
10
16
|
@search_builder_class = search_builder_class
|
|
11
17
|
@context = context
|
|
12
18
|
end
|
|
@@ -15,11 +21,7 @@ module Blacklight
|
|
|
15
21
|
attr_reader :blacklight_config, :context
|
|
16
22
|
|
|
17
23
|
def search_builder
|
|
18
|
-
search_builder_class.new(self)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def search_state_class
|
|
22
|
-
@search_state.class
|
|
24
|
+
search_builder_class.new(self, blacklight_config: blacklight_config)
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
# Fetch query results from solr
|
|
@@ -71,11 +73,24 @@ module Blacklight
|
|
|
71
73
|
|
|
72
74
|
# Get the previous and next document from a search result
|
|
73
75
|
# @return [Blacklight::Solr::Response, Array<Blacklight::SolrDocument>] the solr response and a list of the first and last document
|
|
74
|
-
def previous_and_next_documents_for_search(index, request_params,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
def previous_and_next_documents_for_search(index, request_params, params: nil, **extra_controller_params)
|
|
77
|
+
unless params
|
|
78
|
+
new_state = request_params.is_a?(Blacklight::SearchState) ? request_params : Blacklight::SearchState.new(request_params, blacklight_config)
|
|
79
|
+
builder = search_builder.with(new_state)
|
|
80
|
+
|
|
81
|
+
search_service_params = Blacklight.deprecation.silence do
|
|
82
|
+
previous_and_next_document_params(index)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
builder = if search_service_params.empty?
|
|
86
|
+
builder.for_previous_and_next_documents(index).merge(extra_controller_params)
|
|
87
|
+
else
|
|
88
|
+
Blacklight.deprecation.warn("SearchService#previous_and_next_document_params returned parameters. Implement customizations in SearchBuilder#for_previous_and_next_document instead")
|
|
89
|
+
builder.start(search_service_params.delete(:start)).rows(search_service_params.delete(:rows)).merge(extra_controller_params).merge(search_service_params)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
response = repository.search(params: params || builder)
|
|
79
94
|
document_list = response.documents
|
|
80
95
|
|
|
81
96
|
# only get the previous doc if there is one
|
|
@@ -90,14 +105,16 @@ module Blacklight
|
|
|
90
105
|
# the second item is an other array. This second array contains
|
|
91
106
|
# all of the field values for each of the documents...
|
|
92
107
|
# where the field is the "field" argument passed in.
|
|
108
|
+
# @deprecated
|
|
93
109
|
def opensearch_response(field = nil, extra_controller_params = {})
|
|
94
110
|
field ||= blacklight_config.view_config(:opensearch).title_field
|
|
95
111
|
|
|
96
112
|
query = search_builder.with(search_state).merge(solr_opensearch_params(field)).merge(extra_controller_params)
|
|
97
|
-
response = repository.search(query)
|
|
113
|
+
response = repository.search(params: query)
|
|
98
114
|
|
|
99
115
|
[search_state.query_param, response.documents.flat_map { |doc| doc[field] }.uniq]
|
|
100
116
|
end
|
|
117
|
+
Blacklight.deprecation.deprecate_methods Blacklight::SearchService, opensearch_response: "The opensearch_response method is deprecated without replacement."
|
|
101
118
|
|
|
102
119
|
private
|
|
103
120
|
|
|
@@ -113,34 +130,20 @@ module Blacklight
|
|
|
113
130
|
|
|
114
131
|
##
|
|
115
132
|
# Opensearch autocomplete parameters for plucking a field's value from the results
|
|
133
|
+
# @deprecated
|
|
116
134
|
def solr_opensearch_params(field)
|
|
117
135
|
solr_params = {}
|
|
118
136
|
solr_params[:rows] ||= 10
|
|
119
137
|
solr_params[:fl] = field || blacklight_config.view_config(:opensearch).title_field
|
|
120
138
|
solr_params
|
|
121
139
|
end
|
|
140
|
+
Blacklight.deprecation.deprecate_methods Blacklight::SearchService, solr_opensearch_params: "The solr_opensearch_params method is deprecated without replacement."
|
|
122
141
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
# out of a result set.
|
|
126
|
-
def previous_and_next_document_params(index, window = 1)
|
|
127
|
-
solr_params = blacklight_config.document_pagination_params.dup
|
|
128
|
-
|
|
129
|
-
if solr_params.empty?
|
|
130
|
-
solr_params[:fl] = blacklight_config.document_model.unique_key
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
if index > 0
|
|
134
|
-
solr_params[:start] = index - window # get one before
|
|
135
|
-
solr_params[:rows] = (2 * window) + 1 # and one after
|
|
136
|
-
else
|
|
137
|
-
solr_params[:start] = 0 # there is no previous doc
|
|
138
|
-
solr_params[:rows] = 2 * window # but there should be one after
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
solr_params[:facet] = false
|
|
142
|
-
solr_params
|
|
142
|
+
def previous_and_next_document_params(*, **)
|
|
143
|
+
{}
|
|
143
144
|
end
|
|
145
|
+
Blacklight.deprecation.deprecate_methods(Blacklight::SearchService,
|
|
146
|
+
previous_and_next_document_params: "Use SearchBuilder#for_previous_and_next_documents instead of SearchService#previous_and_next_document_params")
|
|
144
147
|
|
|
145
148
|
##
|
|
146
149
|
# Retrieve a set of documents by id
|
|
@@ -149,11 +152,20 @@ module Blacklight
|
|
|
149
152
|
def fetch_many(ids, extra_controller_params)
|
|
150
153
|
extra_controller_params ||= {}
|
|
151
154
|
|
|
155
|
+
requested_rows = extra_controller_params.delete(:rows)
|
|
156
|
+
if requested_rows
|
|
157
|
+
Blacklight.deprecation.warn("Passing :rows to fetch_many is deprecated. Create an issue in blacklight if you see this warning. " \
|
|
158
|
+
"Otherwise, :rows will be ignored in Blackight 10")
|
|
159
|
+
else
|
|
160
|
+
requested_rows = ids.count
|
|
161
|
+
end
|
|
162
|
+
|
|
152
163
|
query = search_builder
|
|
153
164
|
.with(search_state)
|
|
154
165
|
.where(blacklight_config.document_model.unique_key => ids)
|
|
155
166
|
.merge(blacklight_config.fetch_many_document_params)
|
|
156
167
|
.merge(extra_controller_params)
|
|
168
|
+
query.rows(requested_rows)
|
|
157
169
|
|
|
158
170
|
# find_many was introduced in Blacklight 8.4. Before that, we used the
|
|
159
171
|
# regular search method (possibly with a find-many specific `qt` parameter).
|
|
@@ -162,7 +174,8 @@ module Blacklight
|
|
|
162
174
|
solr_response = if repository.respond_to?(:find_many)
|
|
163
175
|
repository.find_many(query)
|
|
164
176
|
else
|
|
165
|
-
|
|
177
|
+
Blacklight.deprecation.warn("Repository#find_many is not implemented. Falling back to Repository#search.")
|
|
178
|
+
repository.search(params: query)
|
|
166
179
|
end
|
|
167
180
|
|
|
168
181
|
solr_response.documents
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
<div class="modal-body">
|
|
8
8
|
<%= render '/shared/flash_msg' %>
|
|
9
|
-
<div class="row">
|
|
9
|
+
<div class="row mb-3">
|
|
10
10
|
<label class="col-form-label col-sm-2" for="to">
|
|
11
11
|
<%= t('blacklight.email.form.to') %>
|
|
12
12
|
</label>
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
</div>
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
|
-
<div class="row">
|
|
18
|
+
<div class="row mb-3">
|
|
19
19
|
<label class="col-form-label col-sm-2" for="message">
|
|
20
20
|
<%= t('blacklight.email.form.message') %>
|
|
21
21
|
</label>
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
</div>
|
|
26
26
|
|
|
27
27
|
<% @documents&.each do |doc| %>
|
|
28
|
-
<%=hidden_field_tag "id[]", doc.id %>
|
|
28
|
+
<%= hidden_field_tag "id[]", doc.id %>
|
|
29
29
|
<% end %>
|
|
30
30
|
<%- if params[:sort] -%>
|
|
31
31
|
<%= hidden_field_tag "sort", params[:sort] %>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<div class="text-center p-5 mb-4 bg-
|
|
1
|
+
<div class="text-center p-5 mb-4 bg-body-tertiary border rounded-3">
|
|
2
2
|
<h1 class="fw-bold"><%= t('blacklight.welcome') %></h1>
|
|
3
3
|
|
|
4
4
|
<p class="lead">Blacklight is a multi-institutional open-source collaboration building a better discovery platform framework.</p>
|