app/helpers/blacklight/catalog_helper_behavior.rb
# frozen_string_literal: true
# Helper methods for catalog-like controllers
module Blacklight::CatalogHelperBehavior
include Blacklight::ConfigurationHelperBehavior
include Blacklight::ComponentHelperBehavior
include Blacklight::DocumentHelperBehavior
include Blacklight::FacetsHelperBehavior
include Blacklight::RenderPartialsHelperBehavior
# @param [Hash] options
# @option options :route_set the route scope to use when constructing the link
# @return [String]
def rss_feed_link_tag(options = {})
auto_discovery_link_tag(:rss, feed_link_url('rss', options), title: t('blacklight.search.rss_feed'))
end
# @param [Hash] options
# @option options :route_set the route scope to use when constructing the link
# @return [String]
def atom_feed_link_tag(options = {})
auto_discovery_link_tag(:atom, feed_link_url('atom', options), title: t('blacklight.search.atom_feed'))
end
# @param [Hash] options
# @option options :route_set the route scope to use when constructing the link
# @return [String]
def json_api_link_tag(options = {})
auto_discovery_link_tag(:json, feed_link_url('json', options), type: 'application/json')
end
##
# Override the Kaminari page_entries_info helper with our own, blacklight-aware
# implementation. Why do we have to do this?
# - We need custom counting information for grouped results
# - We need to provide number_with_delimiter strings to i18n keys
# If we didn't have to do either one of these, we could get away with removing
# this entirely.
#
# @param [RSolr::Resource] collection (or other Kaminari-compatible objects)
# @return [String]
def page_entries_info(collection, entry_name: nil)
entry_name = if entry_name
entry_name.pluralize(collection.size, I18n.locale)
else
collection.entry_name(count: collection.size).to_s
end
# grouped response objects need special handling
end_num = if collection.respond_to?(:groups) && render_grouped_response?(collection)
collection.groups.length
else
collection.limit_value
end
end_num = [collection.offset_value + end_num, collection.total_count].min
case collection.total_count
when 0
t('blacklight.search.pagination_info.no_items_found', entry_name: entry_name).html_safe
when 1
t('blacklight.search.pagination_info.single_item_found', entry_name: entry_name).html_safe
else
t('blacklight.search.pagination_info.pages', entry_name: entry_name,
current_page: collection.current_page,
num_pages: collection.total_pages,
start_num: number_with_delimiter(collection.offset_value + 1),
end_num: number_with_delimiter(end_num),
total_num: number_with_delimiter(collection.total_count),
count: collection.total_pages).html_safe
end
end
##
# Get the offset counter for a document
#
# @param [Integer] idx document index
# @param [Integer] offset additional offset to incremenet the counter by
# @return [Integer]
def document_counter_with_offset idx, offset = nil
offset ||= @response.start if @response
offset ||= 0
unless render_grouped_response?
idx + 1 + offset
end
end
##
# Look up search field user-displayable label
# based on params[:qt] and blacklight_configuration.
# @return [String]
def search_field_label(params)
h(label_for_search_field(params[:search_field]))
end
##
# Look up the current sort field, or provide the default if none is set
#
# @return [Blacklight::Configuration::SortField]
def current_sort_field
(blacklight_config.sort_fields.values.find { |f| f.sort == @response.sort } if @response && @response.sort.present?) || blacklight_config.sort_fields[params[:sort]] || default_sort_field
end
##
# Look up the current per page value, or the default if none if set
#
# @return [Integer]
def current_per_page
(@response.rows if @response && @response.rows > 0) || params.fetch(:per_page, blacklight_config.default_per_page).to_i
end
##
# Should we display the sort and per page widget?
#
# @param [Blacklight::Solr::Response] response
# @return [Boolean]
def show_sort_and_per_page? response = nil
response ||= @response
!response.empty?
end
##
# Should we display the pagination controls?
#
# @param [Blacklight::Solr::Response] response
# @return [Boolean]
def show_pagination? response = nil
response ||= @response
response.limit_value > 0
end
# Render an html <title> appropriate string for a selected facet field and values
#
# @see #render_search_to_page_title
# @param [Symbol] facet the facet field
# @param [Array<String>] values the selected facet values
# @return [String]
def render_search_to_page_title_filter(facet, values)
facet_config = facet_configuration_for_field(facet)
filter_label = facet_field_label(facet_config.key)
filter_value = if values.size < 3
values.map { |value| facet_item_presenter(facet_config, value, facet).label }.to_sentence
else
t('blacklight.search.page_title.many_constraint_values', values: values.size)
end
t('blacklight.search.page_title.constraint', label: filter_label, value: filter_value)
end
# Render an html <title> appropriate string for a set of search parameters
# @param [ActionController::Parameters] params
# @return [String]
def render_search_to_page_title(search_state_or_params)
search_state = if search_state_or_params.is_a? Blacklight::SearchState
search_state_or_params
else
controller.search_state_class.new(params, blacklight_config, self)
end
constraints = []
if search_state.query_param.present?
q_label = label_for_search_field(search_state.search_field.key) unless search_state.search_field&.key.blank? || default_search_field?(search_state.search_field.key)
constraints += if q_label.present?
[t('blacklight.search.page_title.constraint', label: q_label, value: search_state.query_param)]
else
[search_state.query_param]
end
end
if search_state.filters.any?
constraints += search_state.filters.collect { |filter| render_search_to_page_title_filter(filter.key, filter.values) }
end
constraints.join(' / ')
end
##
# Should we render a grouped response (because the response
# contains a grouped response instead of the normal response)
#
# Default to false if there's no response object available (sometimes the case
# for tests, but might happen in other circumstances too..)
# @return [Boolean]
def render_grouped_response? response = @response
response&.grouped?
end
##
# Get the current "view type" (and ensure it is a valid type)
#
# @param [Hash] query_params the query parameters to check
# @return [Symbol]
def document_index_view_type query_params = params || {}
view_param = query_params[:view]
view_param ||= session[:preferred_view] if respond_to?(:session)
if view_param && document_index_views.key?(view_param.to_sym)
view_param.to_sym
else
default_document_index_view_type
end
end
private
# @param [String] format
# @param [Hash] options
# @option options :route_set the route scope to use when constructing the link
def feed_link_url(format, options = {})
scope = options.delete(:route_set) || self
scope.url_for search_state.to_h.merge(format: format)
end
end