-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
796 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
app/assets/stylesheets/geoblacklight_admin/modules/_pagy.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
.pagy { | ||
a:not(.gap) { | ||
&:not([href]) { /* disabled links */ | ||
margin-top: 0.4rem; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,373 @@ | ||
# frozen_string_literal: true | ||
|
||
module Blacklight::Catalog | ||
include Pagy::Backend | ||
extend ActiveSupport::Concern | ||
|
||
# MimeResponds is part of ActionController::Base, but not ActionController::API | ||
include ActionController::MimeResponds | ||
|
||
Deprecation.silence(Blacklight::Base) do | ||
include Blacklight::Base | ||
end | ||
|
||
include Blacklight::Facet | ||
include Blacklight::Searchable | ||
|
||
extend Deprecation | ||
|
||
# The following code is executed when someone includes blacklight::catalog in their | ||
# own controller. | ||
included do | ||
if respond_to? :helper_method | ||
helper_method :sms_mappings, :has_search_parameters?, :facet_limit_for | ||
end | ||
|
||
helper Blacklight::Facet if respond_to? :helper | ||
|
||
# The index action will more than likely throw this one. | ||
# Example: when the standard query parser is used, and a user submits a "bad" query. | ||
rescue_from Blacklight::Exceptions::InvalidRequest, with: :handle_request_error | ||
|
||
record_search_parameters | ||
end | ||
|
||
# get search results from the solr index | ||
def index | ||
(@response, deprecated_document_list) = search_service.search_results | ||
|
||
@document_list = ActiveSupport::Deprecation::DeprecatedObjectProxy.new( | ||
deprecated_document_list, | ||
"The @document_list instance variable is deprecated; use @response.documents instead.", | ||
ActiveSupport::Deprecation.new("8.0", "blacklight") | ||
) | ||
|
||
@pagy = Pagy.new( | ||
count: @response.total_count, | ||
page: params[:page], | ||
limit: params[:per_page] | ||
) | ||
|
||
respond_to do |format| | ||
format.html { store_preferred_view } | ||
format.rss { render layout: false } | ||
format.atom { render layout: false } | ||
format.json do | ||
@presenter = Blacklight::JsonPresenter.new(@response, | ||
blacklight_config) | ||
end | ||
additional_response_formats(format) | ||
document_export_formats(format) | ||
end | ||
end | ||
|
||
# get a single document from the index | ||
# to add responses for formats other than html or json see _Blacklight::Document::Export_ | ||
def show | ||
deprecated_response, @document = search_service.fetch(params[:id]) | ||
@response = ActiveSupport::Deprecation::DeprecatedObjectProxy.new( | ||
deprecated_response, | ||
"The @response instance variable is deprecated; use @document.response instead.", | ||
ActiveSupport::Deprecation.new("8.0", "blacklight") | ||
) | ||
|
||
respond_to do |format| | ||
format.html { @search_context = setup_next_and_previous_documents } | ||
format.json | ||
additional_export_formats(@document, format) | ||
end | ||
end | ||
|
||
def advanced_search | ||
(@response, _deprecated_document_list) = blacklight_advanced_search_form_search_service.search_results | ||
end | ||
|
||
# get a single document from the index | ||
def raw | ||
raise(ActionController::RoutingError, "Not Found") unless blacklight_config.raw_endpoint.enabled | ||
|
||
_, @document = search_service.fetch(params[:id]) | ||
render json: @document | ||
end | ||
|
||
# updates the search counter (allows the show view to paginate) | ||
def track | ||
search_session["counter"] = params[:counter] | ||
search_session["id"] = params[:search_id] | ||
search_session["per_page"] = params[:per_page] | ||
search_session["document_id"] = params[:document_id] | ||
|
||
if params[:redirect] && (params[:redirect].starts_with?("/") || params[:redirect] =~ URI::DEFAULT_PARSER.make_regexp) | ||
uri = URI.parse(params[:redirect]) | ||
path = uri.query ? "#{uri.path}?#{uri.query}" : uri.path | ||
redirect_to path, status: :see_other | ||
else | ||
redirect_to({action: :show, id: params[:id]}, status: :see_other) | ||
end | ||
end | ||
|
||
# displays values and pagination links for a single facet field | ||
def facet | ||
@facet = blacklight_config.facet_fields[params[:id]] | ||
raise ActionController::RoutingError, "Not Found" unless @facet | ||
|
||
@response = search_service.facet_field_response(@facet.key) | ||
@display_facet = @response.aggregations[@facet.field] | ||
|
||
@presenter = (@facet.presenter || Blacklight::FacetFieldPresenter).new(@facet, @display_facet, view_context) | ||
@pagination = @presenter.paginator | ||
|
||
@pagy = Pagy.new( | ||
count: @response.total_count, | ||
page_param: "facet.page", | ||
page: params["facet.page"], | ||
limit: 20 | ||
) | ||
|
||
respond_to do |format| | ||
format.html do | ||
# Draw the partial for the "more" facet modal window: | ||
return render layout: false if request.xhr? | ||
# Otherwise draw the facet selector for users who have javascript disabled. | ||
end | ||
format.json | ||
end | ||
end | ||
|
||
# method to serve up XML OpenSearch description and JSON autocomplete response | ||
def opensearch | ||
respond_to do |format| | ||
format.xml { render layout: false } | ||
format.json { render json: search_service.opensearch_response } | ||
end | ||
end | ||
|
||
def suggest | ||
respond_to do |format| | ||
format.json do | ||
render json: suggestions_service.suggestions | ||
end | ||
end | ||
end | ||
|
||
# @return [Array] first value is a Blacklight::Solr::Response and the second | ||
# is a list of documents | ||
def action_documents | ||
deprecated_response, @documents = search_service.fetch(Array(params[:id])) | ||
raise Blacklight::Exceptions::RecordNotFound if @documents.blank? | ||
|
||
[deprecated_response, @documents] | ||
end | ||
|
||
def action_success_redirect_path | ||
search_state.url_for_document(blacklight_config.document_model.new(id: params[:id])) | ||
end | ||
|
||
## | ||
# Check if any search parameters have been set | ||
# @return [Boolean] | ||
def has_search_parameters? | ||
params[:search_field].present? || search_state.has_constraints? | ||
end | ||
|
||
# TODO: deprecate this constant with #facet_limit_for | ||
DEFAULT_FACET_LIMIT = 10 | ||
|
||
# Look up facet limit for given facet_field. Will look at config, and | ||
# if config is 'true' will look up from Solr @response if available. If | ||
# no limit is available, returns nil. Used from #add_facetting_to_solr | ||
# to supply f.fieldname.facet.limit values in solr request (no @response | ||
# available), and used in display (with @response available) to create | ||
# a facet paginator with the right limit. | ||
def facet_limit_for(facet_field) | ||
facet = blacklight_config.facet_fields[facet_field] | ||
return if facet.blank? | ||
|
||
if facet.limit && @response && @response.aggregations[facet.field] | ||
limit = @response.aggregations[facet.field].limit | ||
|
||
if limit.nil? # we didn't get or a set a limit, so infer one. | ||
facet.limit if facet.limit != true | ||
elsif limit == -1 # limit -1 is solr-speak for unlimited | ||
nil | ||
else | ||
limit.to_i - 1 # we added 1 to find out if we needed to paginate | ||
end | ||
elsif facet.limit | ||
(facet.limit == true) ? DEFAULT_FACET_LIMIT : facet.limit | ||
end | ||
end | ||
deprecation_deprecate facet_limit_for: "moving to private logic in Blacklight::FacetFieldPresenter" | ||
|
||
private | ||
|
||
# | ||
# non-routable methods -> | ||
# | ||
|
||
def render_sms_action?(_config, _options) | ||
sms_mappings.present? | ||
end | ||
|
||
## | ||
# If the params specify a view, then store it in the session. If the params | ||
# do not specifiy the view, set the view parameter to the value stored in the | ||
# session. This enables a user with a session to do subsequent searches and have | ||
# them default to the last used view. | ||
def store_preferred_view | ||
session[:preferred_view] = params[:view] if params[:view] | ||
end | ||
|
||
## | ||
# Render additional response formats for the index action, as provided by the | ||
# blacklight configuration | ||
# @param [Hash] format | ||
# @note Make sure your format has a well known mime-type or is registered in config/initializers/mime_types.rb | ||
# @example | ||
# config.index.respond_to.txt = Proc.new { render plain: "A list of docs." } | ||
def additional_response_formats(format) | ||
blacklight_config.view_config(action_name: :index).respond_to.each do |key, config| | ||
format.send key do | ||
case config | ||
when false | ||
raise ActionController::RoutingError, "Not Found" | ||
when Hash | ||
render config | ||
when Proc | ||
instance_exec(&config) | ||
when Symbol, String | ||
send config | ||
else | ||
render({}) | ||
end | ||
end | ||
end | ||
end | ||
|
||
## | ||
# Render additional export formats for the show action, as provided by | ||
# the document extension framework. See _Blacklight::Document::Export_ | ||
def additional_export_formats(document, format) | ||
document.export_formats.each_key do |format_name| | ||
format.send(format_name.to_sym) { render body: document.export_as(format_name), layout: false } | ||
end | ||
end | ||
|
||
## | ||
# Try to render a response from the document export formats available | ||
def document_export_formats(format) | ||
format.any do | ||
format_name = params.fetch(:format, "").to_sym | ||
if @response.export_formats.include? format_name | ||
render_document_export_format format_name | ||
else | ||
raise ActionController::UnknownFormat | ||
end | ||
end | ||
end | ||
|
||
## | ||
# Render the document export formats for a response | ||
# First, try to render an appropriate template (e.g. index.endnote.erb) | ||
# If that fails, just concatenate the document export responses with a newline. | ||
def render_document_export_format format_name | ||
render | ||
rescue ActionView::MissingTemplate | ||
render plain: @response.documents.map { |x| x.export_as(format_name) if x.exports_as? format_name }.compact.join("\n"), layout: false | ||
end | ||
|
||
# Overrides the Blacklight::Controller provided #search_action_url. | ||
# By default, any search action from a Blacklight::Catalog controller | ||
# should use the current controller when constructing the route. | ||
def search_action_url options = {} | ||
options = options.to_h if options.is_a? Blacklight::SearchState | ||
url_for(options.reverse_merge(action: "index")) | ||
end | ||
|
||
# Email Action (this will render the appropriate view on GET requests and process the form and send the email on POST requests) | ||
def email_action documents | ||
mail = RecordMailer.email_record(documents, {to: params[:to], message: params[:message], config: blacklight_config}, url_options) | ||
if mail.respond_to? :deliver_now | ||
mail.deliver_now | ||
else | ||
mail.deliver | ||
end | ||
end | ||
|
||
# SMS action (this will render the appropriate view on GET requests and process the form and send the email on POST requests) | ||
def sms_action documents | ||
to = "#{params[:to].gsub(/[^\d]/, "")}@#{params[:carrier]}" | ||
mail = RecordMailer.sms_record(documents, {to: to, config: blacklight_config}, url_options) | ||
if mail.respond_to? :deliver_now | ||
mail.deliver_now | ||
else | ||
mail.deliver | ||
end | ||
end | ||
|
||
def validate_sms_params | ||
if params[:to].blank? | ||
flash[:error] = I18n.t("blacklight.sms.errors.to.blank") | ||
elsif params[:carrier].blank? | ||
flash[:error] = I18n.t("blacklight.sms.errors.carrier.blank") | ||
elsif params[:to].gsub(/[^\d]/, "").length != 10 | ||
flash[:error] = I18n.t("blacklight.sms.errors.to.invalid", to: params[:to]) | ||
elsif !sms_mappings.value?(params[:carrier]) | ||
flash[:error] = I18n.t("blacklight.sms.errors.carrier.invalid") | ||
end | ||
|
||
flash[:error].blank? | ||
end | ||
|
||
def sms_mappings | ||
Blacklight::Engine.config.blacklight.sms_mappings | ||
end | ||
|
||
def validate_email_params | ||
if params[:to].blank? | ||
flash[:error] = I18n.t("blacklight.email.errors.to.blank") | ||
elsif !params[:to].match(Blacklight::Engine.config.blacklight.email_regexp) | ||
flash[:error] = I18n.t("blacklight.email.errors.to.invalid", to: params[:to]) | ||
end | ||
|
||
flash[:error].blank? | ||
end | ||
|
||
def start_new_search_session? | ||
action_name == "index" | ||
end | ||
|
||
def determine_layout | ||
(action_name == "show") ? "catalog_result" : super | ||
end | ||
|
||
# when a method throws a Blacklight::Exceptions::InvalidRequest, this method is executed. | ||
def handle_request_error(exception) | ||
# Rails own code will catch and give usual Rails error page with stack trace | ||
raise exception if Rails.env.development? || Rails.env.test? | ||
|
||
flash_notice = I18n.t("blacklight.search.errors.request_error") | ||
|
||
# If there are errors coming from the index page, we want to trap those sensibly | ||
|
||
if flash[:notice] == flash_notice | ||
logger&.error "Cowardly aborting rsolr_request_error exception handling, because we redirected to a page that raises another exception" | ||
raise exception | ||
end | ||
|
||
logger&.error exception | ||
|
||
flash[:notice] = flash_notice | ||
redirect_to search_action_url | ||
end | ||
|
||
def blacklight_advanced_search_form_search_service | ||
form_search_state = search_state_class.new(blacklight_advanced_search_form_params, blacklight_config, self) | ||
|
||
search_service_class.new(config: blacklight_config, search_state: form_search_state, user_params: form_search_state.to_h, **search_service_context) | ||
end | ||
|
||
def blacklight_advanced_search_form_params | ||
{} | ||
end | ||
end |
Oops, something went wrong.