# -*- encoding : utf-8 -*-
# -*- coding: utf-8 -*-
# Copied from blacklight 3.0. Do not change this file inside hydra. Override in app/helpers/hydra/blacklight_helper_behavior.rb
# When Blacklight 3.2 comes out, remove this file.
#
# Methods added to this helper will be available to all templates in the hosting application
#
module Blacklight::BlacklightHelperBehavior
include HashAsHiddenFields
include RenderConstraintsHelper
def application_name
'Blacklight'
end
##
# This method should be included in any Blacklight layout, including
# custom ones. It will output results of #render_js_includes,
# #render_stylesheet_includes, and all the content of
# current_controller#extra_head_content.
#
# Uses controller methods #extra_head_content, #javascript_includes,
# and #stylesheet_links to find content. Tolerates it if those
# methods don't exist, silently skipping.
#
# By a layout outputting this in html HEAD, it provides an easy way for
# local config or extra plugins to add HEAD content.
#
# Add your own css or remove the defaults by simply editing
# controller.stylesheet_links, controller.javascript_includes,
# or controller.extra_head_content.
#
#
#
# in an initializer or other startup file (plugin init.rb?):
#
# == Apply to all actions in all controllers:
#
# ApplicationController.before_filter do |controller|
# # remove default jquery-ui theme.
# controller.stylesheet_links.each do |args|
# args.delete_if {|a| a =~ /^|\/jquery-ui-[\d.]+\.custom\.css$/ }
# end
#
# # add in a different jquery-ui theme, or any other css or what have you
# controller.stylesheet_links << 'my_css.css'
#
# controller.javascript_includes << "my_local_behaviors.js"
#
# controller.extra_head_content << ''
# end
#
# == Apply to a particular action in a particular controller:
#
# CatalogController.before_filter :only => :show |controller|
# controller.extra_head_content << ''
# end
#
# == Or in a view file that wants to add certain header content? no problem:
#
# <% stylesheet_links << "mystylesheet.css" %>
# <% javascript_includes << "my_js.js" %>
# <% extra_head_content << capture do %>
# <%= tag :link, { :href => some_method_for_something, :rel => "alternate" } %>
# <% end %>
#
# == Full power of javascript_include_tag and stylesheet_link_tag
# Note that the elements added to stylesheet_links and javascript_links
# are arguments to Rails javascript_include_tag and stylesheet_link_tag
# respectively, you can pass complex arguments. eg:
#
# stylesheet_links << ["stylesheet1.css", "stylesheet2.css", {:cache => "mykey"}]
# javascript_includes << ["myjavascript.js", {:plugin => :myplugin} ]
def render_head_content
render_stylesheet_includes +
render_js_includes +
render_extra_head_content
end
##
# Assumes controller has a #stylesheet_link_tag method, array with
# each element being a set of arguments for stylesheet_link_tag
# See #render_head_content for instructions on local code or plugins
# adding stylesheets.
def render_stylesheet_includes
return "".html_safe unless respond_to?(:stylesheet_links)
stylesheet_links.uniq.collect do |args|
stylesheet_link_tag(*args)
end.join("\n").html_safe
end
##
# Assumes controller has a #js_includes method, array with each
# element being a set of arguments for javsascript_include_tag.
# See #render_head_content for instructions on local code or plugins
# adding js files.
def render_js_includes
return "".html_safe unless respond_to?(:javascript_includes)
javascript_includes.uniq.collect do |args|
javascript_include_tag(*args)
end.join("\n").html_safe
end
##
# Assumes controller has a #extra_head_content method
#
def render_extra_head_content
return "".html_safe unless respond_to?(:extra_head_content)
extra_head_content.join("\n").html_safe
end
# Create links from a documents dynamically
# provided export formats. Currently not used by standard BL layouts,
# but available for your custom layouts to provide link rel alternates.
#
# Returns empty string if no links available.
#
# :unique => true, will ensure only one link is output for every
# content type, as required eg in atom. Which one 'wins' is arbitrary.
# :exclude => array of format shortnames, formats to not include at all.
def render_link_rel_alternates(document=@document, options = {})
options = {:unique => false, :exclude => []}.merge(options)
return nil if document.nil?
seen = Set.new
html = ""
document.export_formats.each_pair do |format, spec|
unless( options[:exclude].include?(format) ||
(options[:unique] && seen.include?(spec[:content_type]))
)
html << tag(:link, {:rel=>"alternate", :title=>format, :type => spec[:content_type], :href=> catalog_url(document.id, format)}) << "\n"
seen.add(spec[:content_type]) if options[:unique]
end
end
return html.html_safe
end
def render_opensearch_response_metadata
render :partial => 'catalog/opensearch_response_metadata'
end
def render_body_class
extra_body_classes.join " "
end
# collection of items to be rendered in the @sidebar
def sidebar_items
@sidebar_items ||= []
end
def extra_body_classes
@extra_body_classes ||= ['blacklight-' + controller.controller_name, 'blacklight-' + [controller.controller_name, controller.action_name].join('-')]
end
#
# Blacklight.config based helpers ->
#
# used in the catalog/_facets partial
def facet_field_labels
Blacklight.config[:facet][:labels]
end
# used in the catalog/_facets partial
def facet_field_names
Blacklight.config[:facet][:field_names]
end
# used in the catalog/_facets partial and elsewhere
# Renders a single section for facet limit with a specified
# solr field used for faceting. Can be over-ridden for custom
# display on a per-facet basis.
def render_facet_limit(solr_field)
render( :partial => "catalog/facet_limit", :locals => {:solr_field =>solr_field })
end
def render_document_list_partial options={}
render :partial=>'catalog/document_list'
end
# Save function area for search results 'index' view, normally
# renders next to title. Includes just 'Folder' by default.
def render_index_doc_actions(document, options={})
content_tag("div", :class=>"documentFunctions") do
raw("#{render(:partial => 'bookmark_control', :locals => {:document=> document}.merge(options))}
#{render(:partial => 'folder_control', :locals => {:document=> document}.merge(options))}")
end
end
# Save function area for item detail 'show' view, normally
# renders next to title. By default includes 'Folder' and 'Bookmarks'
def render_show_doc_actions(document=@document, options={})
content_tag("div", :class=>"documentFunctions") do
raw("#{render(:partial => 'bookmark_control', :locals => {:document=> document}.merge(options))}
#{render(:partial => 'folder_control', :locals => {:document=> document}.merge(options))}")
end
end
# used in the catalog/_index_partials/_default view
def index_field_names
Blacklight.config[:index_fields][:field_names]
end
# used in the _index_partials/_default view
def index_field_labels
Blacklight.config[:index_fields][:labels]
end
def spell_check_max
Blacklight.config[:spell_max] || 0
end
def render_index_field_label args
field = args[:field]
html_escape index_field_labels[field]
end
def render_index_field_value args
value = args[:value]
value ||= args[:document].get(args[:field], :sep => nil) if args[:document] and args[:field]
render_field_value value
end
# Used in the show view for displaying the main solr document heading
def document_heading
@document[Blacklight.config[:show][:heading]] || @document.id
end
def render_document_heading
content_tag(:h1, document_heading)
end
# Used in the show view for setting the main html document title
def document_show_html_title
@document[Blacklight.config[:show][:html_title]]
end
# Used in citation view for displaying the title
def citation_title(document)
document[Blacklight.config[:show][:html_title]]
end
# Used in the document_list partial (search view) for building a select element
def sort_fields
Blacklight.config[:sort_fields]
end
# Used in the document list partial (search view) for creating a link to the document show action
def document_show_link_field
Blacklight.config[:index][:show_link].to_sym
end
# Used in the search form partial for building a select tag
def search_fields
Blacklight.search_field_options_for_select
end
# used in the catalog/_show/_default partial
def document_show_fields
Blacklight.config[:show_fields][:field_names]
end
# used in the catalog/_show/_default partial
def document_show_field_labels
Blacklight.config[:show_fields][:labels]
end
def render_document_show_field_label args
field = args[:field]
html_escape document_show_field_labels[field]
end
def render_document_show_field_value args
value = args[:value]
value ||= args[:document].get(args[:field], :sep => nil) if args[:document] and args[:field]
render_field_value value
end
def render_field_value value=nil
value = [value] unless value.is_a? Array
value = value.collect { |x| x.respond_to?(:force_encoding) ? x.force_encoding("UTF-8") : x}
return value.map { |v| html_escape v }.join(field_value_separator).html_safe
end
def field_value_separator
', '
end
# Return a normalized partial name that can be used to contruct view partial path
def document_partial_name(document)
# .to_s is necessary otherwise the default return value is not always a string
# using "_" as sep. to more closely follow the views file naming conventions
# parameterize uses "-" as the default sep. which throws errors
display_type = document[Blacklight.config[:show][:display_type]]
return 'default' unless display_type
display_type = display_type.join(" ") if display_type.respond_to?(:join)
"#{display_type.gsub("-"," ")}".parameterize("_").to_s
end
# given a doc and action_name, this method attempts to render a partial template
# based on the value of doc[:format]
# if this value is blank (nil/empty) the "default" is used
# if the partial is not found, the "default" partial is rendered instead
def render_document_partial(doc, action_name)
format = document_partial_name(doc)
begin
render :partial=>"catalog/_#{action_name}_partials/#{format}", :locals=>{:document=>doc}
rescue ActionView::MissingTemplate
render :partial=>"catalog/_#{action_name}_partials/default", :locals=>{:document=>doc}
end
end
# Search History and Saved Searches display
def link_to_previous_search(params)
link_to(raw(render_search_to_s(params)), catalog_index_path(params)).html_safe
end
#
# facet param helpers ->
#
# Standard display of a facet value in a list. Used in both _facets sidebar
# partial and catalog/facet expanded list. Will output facet value name as
# a link to add that to your restrictions, with count in parens.
# first arg item is a facet value item from rsolr-ext.
# options consist of:
# :suppress_link => true # do not make it a link, used for an already selected value for instance
def render_facet_value(facet_solr_field, item, options ={})
(link_to_unless(options[:suppress_link], item.value, add_facet_params_and_redirect(facet_solr_field, item.value), :class=>"facet_select label") + " " + render_facet_count(item.hits)).html_safe
end
# Standard display of a SELECTED facet value, no link, special span
# with class, and 'remove' button.
def render_selected_facet_value(facet_solr_field, item)
content_tag(:span, render_facet_value(facet_solr_field, item, :suppress_link => true), :class => "selected label") +
link_to("[remove]", remove_facet_params(facet_solr_field, item.value, params), :class=>"remove")
end
# Renders a count value for facet limits. Can be over-ridden locally
# to change style, for instance not use parens. And can be called
# by plugins to get consistent display.
def render_facet_count(num)
content_tag("span", "(" + format_num(num) + ")", :class => "count")
end
# adds the value and/or field to params[:f]
# Does NOT remove request keys and otherwise ensure that the hash
# is suitable for a redirect. See
# add_facet_params_and_redirect
def add_facet_params(field, value)
p = params.dup
p[:f] = (p[:f] || {}).dup # the command above is not deep in rails3, !@#$!@#$
p[:f][field] = (p[:f][field] || []).dup
p[:f][field].push(value)
p
end
# Used in catalog/facet action, facets.rb view, for a click
# on a facet value. Add on the facet params to existing
# search constraints. Remove any paginator-specific request
# params, or other request params that should be removed
# for a 'fresh' display.
# Change the action to 'index' to send them back to
# catalog/index with their new facet choice.
def add_facet_params_and_redirect(field, value)
new_params = add_facet_params(field, value)
# Delete page, if needed.
new_params.delete(:page)
# Delete any request params from facet-specific action, needed
# to redir to index action properly.
Blacklight::Solr::FacetPaginator.request_keys.values.each do |paginator_key|
new_params.delete(paginator_key)
end
new_params.delete(:id)
# Force action to be index.
new_params[:action] = "index"
new_params
end
# copies the current params (or whatever is passed in as the 3rd arg)
# removes the field value from params[:f]
# removes the field if there are no more values in params[:f][field]
# removes additional params (page, id, etc..)
def remove_facet_params(field, value, source_params=params)
p = source_params.dup
# need to dup the facet values too,
# if the values aren't dup'd, then the values
# from the session will get remove in the show view...
p[:f] = p[:f].dup
p[:f][field] = p[:f][field].nil? ? [] : p[:f][field].dup
p.delete :page
p.delete :id
p.delete :counter
p.delete :commit
#return p unless p[field]
p[:f][field] = p[:f][field] - [value]
p[:f].delete(field) if p[:f][field].size == 0
p
end
# true or false, depending on whether the field and value is in params[:f]
def facet_in_params?(field, value)
params[:f] and params[:f][field] and params[:f][field].include?(value)
end
#
# shortcut for built-in Rails helper, "number_with_delimiter"
#
def format_num(num); number_with_delimiter(num) end
#
# link based helpers ->
#
# create link to query (e.g. spelling suggestion)
def link_to_query(query)
p = params.dup
p.delete :page
p.delete :action
p[:q]=query
link_url = catalog_index_path(p)
link_to(query, link_url)
end
def render_document_index_label doc, opts
label = nil
label ||= doc.get(opts[:label]) if opts[:label].instance_of? Symbol
label ||= opts[:label].call(doc, opts) if opts[:label].instance_of? Proc
label ||= opts[:label] if opts[:label].is_a? String
label ||= doc.id
end
# link_to_document(doc, :label=>'VIEW', :counter => 3)
# Use the catalog_path RESTful route to create a link to the show page for a specific item.
# catalog_path accepts a HashWithIndifferentAccess object. The solr query params are stored in the session,
# so we only need the +counter+ param here. We also need to know if we are viewing to document as part of search results.
def link_to_document(doc, opts={:label=>Blacklight.config[:index][:show_link].to_sym, :counter => nil, :results_view => true})
label = render_document_index_label doc, opts
link_to_with_data(label, catalog_path(doc.id), {:method => :put, :class => label.parameterize, :data => opts}).html_safe
end
# link_back_to_catalog(:label=>'Back to Search')
# Create a link back to the index screen, keeping the user's facet, query and paging choices intact by using session.
def link_back_to_catalog(opts={:label=>'Back to Search'})
query_params = session[:search] ? session[:search].dup : {}
query_params.delete :counter
query_params.delete :total
link_url = catalog_index_path + "?" + query_params.to_query
link_to opts[:label], link_url
end
# Create form input type=hidden fields representing the entire search context,
# for inclusion in a form meant to change some aspect of it, like
# re-sort or change records per page. Can pass in params hash
# as :params => hash, otherwise defaults to #params. Can pass
# in certain top-level params keys to _omit_, defaults to :page
def search_as_hidden_fields(options={})
options = {:params => params, :omit_keys => [:page]}.merge(options)
my_params = options[:params].dup
options[:omit_keys].each {|omit_key| my_params.delete(omit_key)}
# removing action and controller from duplicate params so that we don't get hidden fields for them.
my_params.delete(:action)
my_params.delete(:controller)
# commit is just an artifact of submit button, we don't need it, and
# don't want it to pile up with another every time we press submit again!
my_params.delete(:commit)
# hash_as_hidden_fields in hash_as_hidden_fields.rb
return hash_as_hidden_fields(my_params)
end
def link_to_previous_document(previous_document)
return if previous_document == nil
link_to_document previous_document, :label=>'« Previous', :counter => session[:search][:counter].to_i - 1
end
def link_to_next_document(next_document)
return if next_document == nil
link_to_document next_document, :label=>'Next »', :counter => session[:search][:counter].to_i + 1
end
# Use case, you want to render an html partial from an XML (say, atom)
# template. Rails API kind of lets us down, we need to hack Rails internals
# a bit. code taken from:
# http://stackoverflow.com/questions/339130/how-do-i-render-a-partial-of-a-different-format-in-rails
def with_format(format, &block)
old_format = @template_format
@template_format = format
result = block.call
@template_format = old_format
return result
end
# This is an updated +link_to+ that allows you to pass a +data+ hash along with the +html_options+
# which are then written to the generated form for non-GET requests. The key is the form element name
# and the value is the value:
#
# link_to_with_data('Name', some_path(some_id), :method => :post, :html)
def link_to_with_data(*args, &block)
if block_given?
options = args.first || {}
html_options = args.second
concat(link_to(capture(&block), options, html_options))
else
name = args.first
options = args.second || {}
html_options = args.third
url = url_for(options)
if html_options
html_options = html_options.stringify_keys
href = html_options['href']
convert_options_to_javascript_with_data!(html_options, url)
tag_options = tag_options(html_options)
else
tag_options = nil
end
href_attr = "href=\"#{url}\"" unless href
"#{h(name) || h(url)}".html_safe
end
end
# This is derived from +convert_options_to_javascript+ from module +UrlHelper+ in +url_helper.rb+
def convert_options_to_javascript_with_data!(html_options, url = '')
confirm, popup = html_options.delete("confirm"), html_options.delete("popup")
method, href = html_options.delete("method"), html_options['href']
data = html_options.delete("data")
data = data.stringify_keys if data
html_options["onclick"] = case
when method
"#{method_javascript_function_with_data(method, url, href, data)}return false;"
else
html_options["onclick"]
end
end
# This is derived from +method_javascript_function+ from module +UrlHelper+ in +url_helper.rb+
def method_javascript_function_with_data(method, url = '', href = nil, data=nil)
action = (href && url.size > 0) ? "'#{url}'" : 'this.href'
submit_function =
"var f = document.createElement('form'); f.style.display = 'none'; " +
"this.parentNode.appendChild(f); f.method = 'POST'; f.action = #{action};"+
"if(event.metaKey || event.ctrlKey){f.target = '_blank';};" # if the command or control key is being held down while the link is clicked set the form's target to _blank
if data
data.each_pair do |key, value|
submit_function << "var d = document.createElement('input'); d.setAttribute('type', 'hidden'); "
submit_function << "d.setAttribute('name', '#{key}'); d.setAttribute('value', '#{escape_javascript(value.to_s)}'); f.appendChild(d);"
end
end
unless method == :post
submit_function << "var m = document.createElement('input'); m.setAttribute('type', 'hidden'); "
submit_function << "m.setAttribute('name', '_method'); m.setAttribute('value', '#{method}'); f.appendChild(m);"
end
if protect_against_forgery?
submit_function << "var s = document.createElement('input'); s.setAttribute('type', 'hidden'); "
submit_function << "s.setAttribute('name', '#{request_forgery_protection_token}'); s.setAttribute('value', '#{escape_javascript form_authenticity_token}'); f.appendChild(s);"
end
submit_function << "f.submit();"
end
# determines if the given document id is in the folder
def item_in_folder?(doc_id)
session[:folder_document_ids] && session[:folder_document_ids].include?(doc_id) ? true : false
end
# puts together a collection of documents into one refworks export string
def render_refworks_texts(documents)
val = ''
documents.each do |doc|
if doc.respond_to?(:to_marc)
val += doc.export_as_refworks_marc_txt + "\n"
end
end
val
end
# puts together a collection of documents into one endnote export string
def render_endnote_texts(documents)
val = ''
documents.each do |doc|
if doc.respond_to?(:to_marc)
val += doc.export_as_endnote + "\n"
end
end
val
end
def render_document_unapi_microformat(document, options={})
render(:partial=>'catalog/unapi_microformat', :locals => {:document=> document}.merge(options))
end
end