module Blacklight
class DocumentPresenter
include ActionView::Helpers::OutputSafetyHelper
include ActionView::Helpers::TagHelper
extend Deprecation
# @param [SolrDocument] document
# @param [ActionController::Base] controller scope for linking and generating urls
# @param [Blacklight::Configuration] configuration
def initialize(document, controller, configuration = controller.blacklight_config)
@document = document
@configuration = configuration
@controller = controller
end
##
# Get the value of the document's "title" field, or a placeholder
# value (if empty)
#
# @param [SolrDocument] document
# @return [String]
def document_heading
fields = Array(@configuration.view_config(:show).title_field)
f = fields.find { |field| @document.has? field }
if f.nil?
render_field_value(@document.id)
else
render_field_value(@document[f])
end
end
##
# Create links from a documents dynamically
# provided export formats. Returns empty string if no links available.
#
# @params [SolrDocument] document
# @params [Hash] options
# @option options [Boolean] :unique ensures only one link is output for every
# content type, e.g. as required by atom
# @option options [Array] :exclude array of format shortnames to not include in the output
def link_rel_alternates(options = {})
options = { unique: false, exclude: [] }.merge(options)
seen = Set.new
safe_join(@document.export_formats.map do |format, spec|
next if options[:exclude].include?(format) || (options[:unique] && seen.include?(spec[:content_type]))
seen.add(spec[:content_type])
tag(:link, rel: "alternate", title: format, type: spec[:content_type], href: @controller.polymorphic_url(@document, format: format))
end.compact, "\n")
end
##
# Get the document's "title" to display in the element.
# (by default, use the #document_heading)
#
# @see #document_heading
# @return [String]
def document_show_html_title
if @configuration.view_config(:show).html_title_field
fields = Array(@configuration.view_config(:show).html_title_field)
f = fields.find { |field| @document.has? field }
if f.nil?
render_field_value(@document.id)
else
render_field_value(@document[f])
end
else
document_heading
end
end
##
# Render a value (or array of values) from a field
#
# @param [String] value or list of values to display
# @param [Blacklight::Solr::Configuration::Field] solr field configuration
# @return [String]
def render_field_value value=nil, field_config=nil
safe_values = Array(value).collect { |x| x.respond_to?(:force_encoding) ? x.force_encoding("UTF-8") : x }
if field_config and field_config.itemprop
safe_values = safe_values.map { |x| content_tag :span, x, :itemprop => field_config.itemprop }
end
render_values(safe_values, field_config)
end
##
# Render a fields values as a string
# @param [Array] values to display
# @param [Blacklight::Solr::Configuration::Field] solr field configuration
# @return [String]
def render_values(values, field_config = nil)
options = {}
options = field_config.separator_options if field_config && field_config.separator_options
if field_config && field_config.separator
Deprecation.warn(self.class, 'The field configuration #separator is deprecated. Use #separator_options instead')
options[:words_connector] ||= field_config.separator
options[:two_words_connector] ||= field_config.separator
options[:last_word_connector] ||= field_config.separator
end
values.to_sentence(options)
end
##
# Render the document index heading
#
# @param [Hash] opts (Deprecated)
# @option opts [Symbol] :label Render the given field from the document
# @option opts [Proc] :label Evaluate the given proc
# @option opts [String] :label Render the given string
# @param [Symbol, Proc, String] field Render the given field or evaluate the proc or render the given string
def render_document_index_label field, opts ={}
if field.is_a? Hash
Deprecation.warn DocumentPresenter, "Calling render_document_index_label with a hash is deprecated"
field = field[:label]
end
label = case field
when Symbol
@document[field]
when Proc
field.call(@document, opts)
when String
field
end
render_field_value label || @document.id
end
##
# Render the index field label for a document
#
# Allow an extention point where information in the document
# may drive the value of the field
# @param [String] field
# @param [Hash] opts
# @options opts [String] :value
def render_index_field_value field, options = {}
field_config = @configuration.index_fields[field]
value = options[:value] || get_field_values(field, field_config, options)
render_field_value value, field_config
end
##
# Render the show field value for a document
#
# Allow an extention point where information in the document
# may drive the value of the field
# @param [String] field
# @param [Hash] options
# @options opts [String] :value
def render_document_show_field_value field, options={}
field_config = @configuration.show_fields[field]
value = options[:value] || get_field_values(field, field_config, options)
render_field_value value, field_config
end
##
# Get the value for a document's field, and prepare to render it.
# - highlight_field
# - accessor
# - solr field
#
# Rendering:
# - helper_method
# - link_to_search
# TODO : maybe this should be merged with render_field_value, and the ugly signature
# simplified by pushing some of this logic into the "model"
# @param [SolrDocument] document
# @param [String] field name
# @param [Blacklight::Solr::Configuration::Field] solr field configuration
# @param [Hash] options additional options to pass to the rendering helpers
def get_field_values field, field_config, options = {}
# retrieving values
value = case
when (field_config and field_config.highlight)
# retrieve the document value from the highlighting response
@document.highlight_field(field_config.field).map(&:html_safe) if @document.has_highlight_field? field_config.field
when (field_config and field_config.accessor)
# implicit method call
if field_config.accessor === true
@document.send(field)
# arity-1 method call (include the field name in the call)
elsif !field_config.accessor.is_a?(Array) && @document.method(field_config.accessor).arity != 0
@document.send(field_config.accessor, field)
# chained method calls
else
Array(field_config.accessor).inject(@document) do |result, method|
result.send(method)
end
end
when field_config
# regular document field
if field_config.default and field_config.default.is_a? Proc
@document.fetch(field_config.field, &field_config.default)
else
@document.fetch(field_config.field, field_config.default)
end
when field
@document[field]
end
# rendering values
case
when (field_config and field_config.helper_method)
@controller.send(field_config.helper_method, options.merge(:document => @document, :field => field, :value => value))
when (field_config and field_config.link_to_search)
link_field = if field_config.link_to_search === true
field_config.key
else
field_config.link_to_search
end
Array(value).map do |v|
@controller.link_to render_field_value(v, field_config), @controller.search_action_path(@controller.add_facet_params(link_field, v, {}))
end if field
else
value
end
end
end
end