app/presenters/blacklight/document_presenter.rb
# frozen_string_literal: true
module Blacklight
# An abstract class that the view presenters for SolrDocuments descend from
class DocumentPresenter
attr_reader :document, :configuration, :view_context
class_attribute :thumbnail_presenter
self.thumbnail_presenter = ThumbnailPresenter
# @param [SolrDocument] document
# @param [ActionView::Base] view_context scope for linking and generating urls
# @param [Blacklight::Configuration] configuration
def initialize(document, view_context, configuration = view_context.blacklight_config, view_config: nil, field_presenter_options: {})
@document = document
@view_context = view_context
@configuration = configuration
@view_config = view_config
@field_presenter_options = field_presenter_options
end
# @return [Hash<String,Configuration::Field>] all the fields for this index view that should be rendered
def fields_to_render(document_fields = fields, **kwargs)
return to_enum(:fields_to_render, document_fields, **kwargs) unless block_given?
document_fields.each do |name, field_config|
field_presenter = field_presenter(field_config, kwargs)
next unless field_presenter.render_field? && field_presenter.any?
yield name, field_config, field_presenter
end
end
def field_presenters(document_fields = fields, **kwargs)
return to_enum(:field_presenters, document_fields, **kwargs) unless block_given?
fields_to_render(document_fields, **kwargs).each { |_, _, config| yield config }
end
##
# Get the value of the document's "title" field, or a placeholder
# value (if empty)
#
# @return [String]
def heading
return field_value(view_config.title_field) if view_config.title_field.is_a? Blacklight::Configuration::Field
fields = Array.wrap(view_config.title_field) + [configuration.document_model.unique_key]
f = fields.lazy.map { |field| field_config(field) }.detect { |field_config| field_presenter(field_config).any? }
f ? field_value(f, except_operations: [Rendering::HelperMethod]) : ""
end
##
# Get the document's "title" to display in the <title> element.
# (by default, use the #document_heading)
#
# @see #document_heading
# @return [String]
def html_title
return field_value(view_config.html_title_field) if view_config.html_title_field.is_a? Blacklight::Configuration::Field
if view_config.html_title_field
fields = Array.wrap(view_config.html_title_field) + [configuration.document_model.unique_key]
f = fields.lazy.map { |field| field_config(field) }.detect { |field_config| field_presenter(field_config).any? }
field_value(f)
else
heading
end
end
def display_type(base_name = nil, default: nil)
fields = []
fields += Array.wrap(view_config[:"#{base_name}_display_type_field"]) if base_name && view_config.key?(:"#{base_name}_display_type_field")
fields += Array.wrap(view_config.display_type_field)
if fields.empty? && show_view_config != view_config
fields += Array.wrap(show_view_config[:"#{base_name}_display_type_field"]) if base_name && show_view_config.key?(:"#{base_name}_display_type_field")
fields += Array.wrap(show_view_config.display_type_field)
end
fields += ['format'] if fields.empty? # backwards compatibility with the old default value for display_type_field
display_type = fields.lazy.map { |field| field_presenter(display_fields[field] || Configuration::NullDisplayField.new(field)) }.detect(&:any?)&.values
display_type || Array(default)
end
##
# Render the field label for a document
#
# Allow an extention point where information in the document
# may drive the value of the field
# @param [Configuration::Field] field_config
# @param [Hash] options
# @option options [String] :value
def field_value field_config, options = {}
field_presenter(field_config, options).render
end
def thumbnail_presenter_class
view_config.thumbnail_presenter || thumbnail_presenter
end
def thumbnail
@thumbnail ||= thumbnail_presenter_class.new(document, view_context, view_config)
end
##
# Create <link rel="alternate"> links from a documents dynamically
# provided export formats. Returns empty string if no links available.
#
# @param [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<String>] :exclude array of format shortnames to not include in the output
def link_rel_alternates(options = {})
LinkAlternatePresenter.new(view_context, document, options).render
end
def view_config
@view_config ||= show_view_config
end
def show_view_config
configuration.view_config(:show, action_name: view_context.action_name)
end
def inspect
fields = "document:#{document.inspect}"
"#<#{self.class.name}:#{object_id} #{fields}>"
end
private
# @return [Hash<String,Configuration::Field>]
def fields
@fields ||= Array(display_type).inject(display_fields) do |fields, display_type|
fields.merge(display_fields(configuration.for_display_type(display_type)))
end
end
def display_fields(config = configuration)
config[view_config.document_fields_key || :index_fields]
end
def field_config(field)
fields.fetch(field) { Configuration::NullDisplayField.new(field) }
end
def field_presenter(field_config, options = {})
field_config.presenter.new(view_context, document, field_config, field_presenter_options.merge(options))
end
def field_presenter_options
@field_presenter_options ||= {}
end
end
end