app/helpers/dams_objects_helper.rb
require "base64"
require "openssl"
module DamsObjectsHelper
include Dams::ControllerHelper
#---
# Openurl, embedded metadata support,
# Get metadata from solr document, and parsing each field and produce openURL key-encoded value query strings.
# create mapping from DAMS fields to dublin core fields
# hweng@ucsd.edu
#---
def field_mapping(fieldName)
data_arr=[]
fieldData = getFieldData(fieldName)
if fieldData != nil
fieldData.each do |value|
data_arr.push(value)
end
end
data_arr
end
def field_mapping_note(note_key, note_type)
data_arr=[]
index = getComponentIndex
fieldData = @document["#{index}note_json_tesim"]
if fieldData != nil
fieldData.each do |datum|
note = JSON.parse(datum)
if note[note_key] == note_type
data_arr.push(note['value'])
end
end
end
data_arr
end
def getFieldData(fieldName)
index = getComponentIndex
fieldData = @document["#{index}#{fieldName}"]
end
def getComponentIndex
index = (defined?(componentIndex)) ? "component_#{componentIndex}_" : ''
end
# Mapping from title_json_tesim, and concatenate as title_json_tesim.value:title_json_tesim.subTitle
def getTitle
data_arr=[]
fieldData=getFieldData('title_json_tesim')
title_value=''
if fieldData != nil
fieldData.each do |datum|
title = JSON.parse(datum)
if title['value'] != ''
title_value = getFullTitle title
if title['subtitle'] != ''
title_value=title_value+ title['subtitle']
end
data_arr.push(title_value)
end
end
end
data_arr
end
#Need to update
def getCreator
data_arr=[]
end
def getFormat
data_arr=[]
end
def getDescription
data_arr = field_mapping_note("type","abstract")
end
def getRelation
data_arr=[]
fieldData=getFieldData('collection_json_tesim')
# implementation for relation mapping
end
#Need refactor code when MADS implementation is done.
def getCoverage
data_arr=[]
end
#Need refactor code when MADS implementation is done.
def getSubject
fieldValue=field_mapping('subject_tesim')
end
def getDate
data_arr=[]
index = getComponentIndex
fieldData = @document["#{index}date_json_tesim"]
if fieldData != nil
fieldData.each do |datum|
date = JSON.parse(datum)
if date['value'] != ''
data_arr.push(date['value'])
end
end
end
data_arr
end
def getLanguage
fieldValue=field_mapping('language_tesim')
end
def getIdentifier
data_arr = field_mapping_note("displayLabel","ARK")
end
#dc:publisher
def getPublisher
data_arr = field_mapping_note("type","publication")
end
def getCopyright
data_arr=[]
index = getComponentIndex
fieldData = @document["#{index}copyright_tesim"]
if fieldData != nil
fieldJSON = JSON.parse(fieldData.first)
temp =fieldJSON['purposeNote']
temp ='purposeNote: '+temp + ' '+'note: '+fieldJSON['note']
data_arr.push(temp)
end
data_arr
end
def getFilesType
ret = ""
index = getComponentIndex
fieldData = @document["#{index}files_tesim"]
if fieldData != nil
fieldData.each do |datum|
files = JSON.parse(datum)
if files['mimeType'].end_with?("pdf")
ret = files['formatName']
end
end
end
ret
end
def export_as_openurl
query_string = []
query_string << "url_ver=Z39.88-2004&ctx_ver=Z39.88-2004&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Adc&rfr_id=info%3Asid%2Fblacklight.rubyforge.org%3Agenerator"
field_map = {
'title' => getTitle,
'creator'=>getCreator,
'subject'=>getSubject,
'description'=>getDescription,
'date'=>getDate,
'format'=>getFormat,
'language'=> getLanguage,
'identifier'=>getIdentifier,
#'coverage'=>getCoverage,
'publisher'=>getPublisher,
'rights'=> getCopyright
}
field_map.each do |kev, values|
next if values.empty? or values.first.nil?
values.each do |value|
query_string << "rft.#{kev}=#{CGI::escape(value)}"
end
end
query_string.join('&') unless query_string.blank?
end
#--
# End of openURL implementation
#
#---
# select_file: Select files to display
#---
def select_file( params )
document = params[:document]
component = params[:component]
max_size = params[:quality]
type = params[:type]
prefix = (component != nil) ? "component_#{component}_" : ""
files = document["#{prefix}files_tesim"]
# identify acceptable files
service_use = nil
service_dim = 0
service_json = nil
display_dim = 0
display_json = nil
if files != nil
files.each{ |f|
fjson = JSON.parse(f)
fid = fjson["id"]
use = fjson["use"]
if use != nil
use = use.first
end
qual = fjson["quality"]
if qual != nil
qualArr = qual.split("x")
file_dim = qualArr.max { |a,b| a.to_i <=> b.to_i }.to_i
end
if type == nil || use.start_with?(type)
if use != nil && use.end_with?("-service")
if (service_json == nil || service_use.start_with?("image-") )
service_json = fjson
service_use = use
service_dim = file_dim.to_i
end
elsif max_size == nil || file_dim == nil || file_dim.to_i < max_size
if (display_json == nil || file_dim.to_i > display_dim) && use != nil && (not use.end_with?("-source") )
display_json = fjson
display_dim = file_dim.to_i
end
end
end
}
end
# build file metadata hash
info = Hash.new
if ( service_json != nil )
info[:service] = service_json
end
if ( display_json != nil )
info[:display] = display_json
end
info
end
def render_file (params)
component = params[:component]
quality = params[:quality]
if component=="0"
files = select_file( :document=>@document, :quality=>quality )
else
files = select_file( :document=>@document, :component=>component, :quality=>quality )
end
end
#---
# render_file_use
#---
def render_file_use( params )
files = render_file (params)
file_info = files[:service]
if file_info != nil
use = file_info['use'].first
end
end
#---
# render_service_file
#---
def render_service_file( params )
files = render_file (params)
service_file = files[:service]
if service_file != nil
services=service_file["id"]
end
end
#---
# render_display_file
#---
def render_display_file( params )
files = render_file (params)
if files.has_key?(:display)
display_file = files[:display]
display=display_file["id"]
else
service_file = files[:service]
if service_file != nil
services=service_file["id"]
#---
# todo: replace no_display with appreciate icons"
#--
if services.include?('mp3')
display = "no_display"
elsif services.include?('tar.gz')||services.include?('tar')||services.include?('zip')||services.include?('xml')
display = "no_display"
end
end
end
display
end
#---
# POST SOLR UPDATE REWRITES
#---
#---
# Get the display file id value from the component's 'files_tesim' value. Replaces 'render_display_file'.
#
# @param quality (Optional) The quality of the display file. Possible values: 'icon', 'thumbnail', 'preview' (Default), 'large' and 'huge'
# @param componentIndex (Optional) The component's index.
# @return If found, returns a string that is the component's display file id value, otherwise returns 'no_display'
# @author David T.
#---
def grabDisplayFile(parameters={})
p = {:componentIndex=>nil,:quality=>'preview'}.merge(parameters)
componentIndex = p[:componentIndex]
validQualities = Set.new ['icon', 'thumbnail', 'preview', 'large', 'huge']
quality = (validQualities.include? p[:quality].downcase) ? p[:quality].downcase : 'preview'
prefix = (componentIndex != nil) ? "component_#{componentIndex}_" : ''
fieldData = @document["#{prefix}files_tesim"]
result = 'no_display'
if fieldData != nil
fieldData.each do |datum|
files = JSON.parse(datum)
if files["use"].end_with?("-#{quality}")
result = files["id"]
break
end
end
end
return result
end
def grabAnyDisplayFile
file = grabDisplayFile
component_count = @document[:component_count_isi] || 0
i = 0
while file == 'no_display' && i < component_count do
i = i + 1
file = grabDisplayFile(componentIndex: i)
file = "#{i}_#{file}" if file != 'no_display'
end
return file
end
#---
# Get the service file id value from the component's 'files_tesim' value. Replaces 'render_service_file'.
#
# @param componentIndex (Optional) The component's index.
# @return A string that is the component's file id value
# @author David T., hweng
#---
def required_file_use?(param)
(param.end_with?("document-service")||param.end_with?("video-service")||param.end_with?("audio-service"))
end
def file_use(parameters, types)
fieldData = file_data(parameters)
result = nil
if fieldData != nil
fieldData.each do |datum|
files = JSON.parse(datum)
if files["use"].end_with?("-service")
result = files[types]
if required_file_use?(files["use"])
break
end
end
end
end
return result
end
def grabServiceFile(parameters={})
file_use(parameters,"id")
end
def grabFileUse(parameters={})
file_use(parameters,"use")
end
def file_data(parameters)
p = {:componentIndex=>nil}.merge(parameters)
componentIndex = p[:componentIndex]
prefix = (componentIndex != nil) ? "component_#{componentIndex}_" : ''
fieldData = @document["#{prefix}files_tesim"]
end
#---
# Get the source file id value from the component's 'files_tesim' value.
#
# @param componentIndex (Optional) The component's index.
# @return A string that is the component's file id value
# @author escowles, hweng
#---
def grabSourceFile(parameters={})
fieldData = file_data(parameters)
result = nil
if fieldData != nil
fieldData.each do |datum|
files = JSON.parse(datum)
if files["use"].end_with?("-source")
result = files["id"]
break
end
end
end
return result
end
def grabPDFFile(parameters={})
fieldData = file_data(parameters)
result = nil
if fieldData != nil
fieldData.each do |datum|
files = JSON.parse(datum)
if files["use"].end_with?("document-service") || files["use"].end_with?("document-source")
result = files["id"]
break
end
end
end
return result
end
#------------------------
# COMPONENT TREE METHODS
#------------------------
#---
# Get the object's title
#
# @param componentIndex (Optional) The component's index.
# @return A string that is the title value for our object.
# @author David T.
# Updated by hweng@ucsd.edu to fix the label/title display issue.
#---
def grabTitle(parameters={})
p = {:componentIndex=>nil}.merge(parameters)
componentIndex = p[:componentIndex]
prefix = (componentIndex != nil) ? "component_#{componentIndex}_" : ''
fieldData = @document["#{prefix}title_json_tesim"]
result = nil
if fieldData != nil
fieldData.each do |datum|
title = JSON.parse(datum)
if !title['value'].blank?
result = getFullTitle title
break
else
result = title['name']
end
end
else
result = "Generic Component Title #{componentIndex}"
end
result
end
#---
# Get the file type value from the component's file use value
#
# @param fileUse The component's file use (type/role) value. E.g., "image-service", "audio-service", etc.
# @return A string that is the file type value for our component.
# @author David T.
#---
def grabFileType(fileUse)
fileType = (fileUse) ? fileUse.split("-").first : 'no-files'
end
#---
# Determines which Bootstrap icon glyph to use based on a component's file type.
#
# @param fileUse The component's file use (type/role) value. E.g., "image-service", "audio-service", etc.
# @return A string that is the CSS class name of the icon we want to display.
# @author David T.
#---
def grabIcon(fileUse)
icon = grabFileType(fileUse)
case icon
when 'image'
icon = 'glyphicon glyphicon-picture'
when 'audio'
icon = 'glyphicon glyphicon-volume-up'
when 'video'
icon = 'glyphicon glyphicon-film'
when 'no-files'
icon = 'glyphicon glyphicon-stop'
else
icon ='glyphicon glyphicon-file'
end
return icon
end
#-------------------------------------------------------------------------------
# The below is the new version of component tree display,
# which fixed the following probelms of old version tree display:
# 1. the order issue: always display the nested parent-child nodes at bottom of the list.
# 2. Display twice for the child node which is both at top level and nested level.
# And separated the logic code from display code.
#
# by hweng@ucsd.edu
#-------------------------------------------------------------------------------
# @author listu@ucsd.edu
# @date 04/14/2015
# Updated note:
# - Fixed the flataten/repeated components display problem when component tree depth > 2
# - Fixed the missing </li> end tag for some of the components that may distort the tree displayed.
#-------------------------------------------------------------------------------
def init_Tree(components)
end
def display_tree(components)
@checked = []
if !components.nil? && !components.empty?
concat '<ul class="unstyled">'.html_safe
components.each do |i|
display_node i if @checked[i].nil? || !@checked[i]
end
concat '</ul>'.html_safe
end
end
def display_node(index)
children_list = @document["component_#{index}_children_isim"]
concat "<li>".html_safe
if children_list.nil? || children_list.empty?
render_node_HTML(index, false )
else
render_node_HTML(index, true)
concat "<ul class='unstyled node-container'>".html_safe
children_list.each do |value|
display_node(value.to_i)
end
concat "</ul>".html_safe
end
concat "</li>".html_safe
@firstButton = nil
end
def render_node_HTML(index, is_parent_node)
@checked[index]= true
fileUse = grabFileUse(:componentIndex=>index)
btnAttrForFiles = "onClick='dp.COV.showComponent(#{index});'"
btnID = "node-btn-#{index}"
btnCSS = (fileUse) ? "node-file #{@firstButton}" : ''
btnCSS += is_parent_node ? ' node-parent' : ''
iconCSS = is_parent_node ? 'icon-chevron-down node-toggle' : grabIcon(fileUse)
btnTitle = grabTitle(:componentIndex=> index)
concat "<i class='#{iconCSS} node-icon'></i> <button type='button' id='#{btnID}' data-index='#{index}' class='btn btn-small btn-link #{btnCSS}' #{btnAttrForFiles}>#{btnTitle}</button>".html_safe
end
def listComponents (component_map)
components = component_map.nil? ? [] : component_map.first.dup.gsub!(':', ',').gsub!(/[\[\]{}"]/, '').split(',')
components.reject { |c| c.blank? }.map! { |i| i.to_i }
end
#-------------------------------
# End of Component Tree Display
#-------------------------------
#-----------
# STREAMING
#-----------
#---
# Builds Wowza URL
#
# @param fieldData "files_tesim" data (JSON)
# @param objid Object ID (string)
# @param cmpid Component ID (string)
# @return string or nil
# @author David T.
#---
def grabWowzaURL(fieldData,objid,cmpid)
if fieldData != nil
fieldData.each do |datum|
files = JSON.parse(datum)
if files['use'] == 'audio-service' || files['use'] == 'video-service'
fileid = cmpid + '-' + files['id']
encrypted = encrypt_stream_name( objid, fileid, request.ip )
return Rails.configuration.wowza_baseurl + encrypted
end
end
else
nil
end
end
## video stream name encryption
def encrypt_stream_name( pid, fid, ip )
# random nonce
nonce=rand(36**16).to_s(36)
while nonce.length < 16 do
nonce += "x"
end
# load key from environment variable
key = ENV.fetch('APPS_DHH_STREAMING_KEY') {'xxxxxxxxxxxxxxxx'}
# encrypt
str="#{pid} #{fid} #{ip}"
cipher = OpenSSL::Cipher::AES.new(128,:CBC)
cipher.encrypt
cipher.key = key
cipher.iv = nonce
enc = cipher.update(str) + cipher.final
# base64-encode
b64 = Base64.encode64 enc
b64 = b64.gsub("+","-").gsub("/","_").gsub("\n","")
"#{nonce},#{b64}"
end
#---
# Builds Wowza Secure Token
#
# @param field_data "files_tesim" data (JSON)
# @param obj_id Object ID (String)
# @param cmp_id Component ID (String)
# @param base_url (String)
# @return string or nil
# @author Vivian
#---
def secure_token(field_data, obj_id, cmp_id, base_url)
file_name = audio_video_file_name(field_data, cmp_id)
return nil unless file_name
end_time = secure_token_end_time
token_hash = secure_token_hash(end_time, file_name, obj_id, base_url)
"#{Rails.configuration.secure_token_name}endtime=#{end_time}&#{Rails.configuration.secure_token_name}hash=#{token_hash}".html_safe
end
#---
# Builds Wowza Token Hash
#
# @param file_name (String)
# @param end_time (Integer)
# @param obj_id (String)
# @param base_url (String)
# @return string
#---
def secure_token_hash(end_time, file_name, obj_id, base_url)
token_params = []
token_params << Rails.configuration.secure_token_secret
token_params << "#{Rails.configuration.secure_token_name}endtime=#{end_time}"
token_params = token_params.sort
stream = base_url.sub(%r{.*?\/}, '')
obj_path = obj_id.scan(/.{1,2}/).join('/')
hash_in = "#{stream}#{obj_path}/#{ark_naan}-#{obj_id}-#{file_name}?#{token_params.join('&')}"
hash_out = Digest::SHA2.new(256).digest(hash_in.to_s)
hash_out = Base64.encode64(hash_out).to_s.strip
matchers = { '+' => '-', '/' => '_' }
hash_out.gsub(/\+|\//) { |match| matchers[match] }
end
#---
# Builds Wowza Secure Token Base URL
#
# @param field_data "files_tesim" data (JSON)
# @param obj_id Object ID (String)
# @param cmp_id Component ID (String)
# @param base_url (String)
# @return string or nil
#---
def secure_token_base_url(field_data, obj_id, cmp_id, base_url)
file_name = audio_video_file_name(field_data, cmp_id)
return nil unless file_name
obj_path = obj_id.scan(/.{1,2}/).join('/')
"#{base_url}#{obj_path}/#{ark_naan}-#{obj_id}-#{file_name}".html_safe
end
#---
# Get file name
#
# @param field_data "files_tesim" data (JSON)
# @param cmp_id Component ID (String)
# @return string or nil
#---
def audio_video_file_name(field_data, cmp_id)
return nil unless field_data
field_data.each do |datum|
files = JSON.parse(datum)
if files['use'] == 'audio-service' || files['use'] == 'video-service'
file_name = cmp_id + '-' + files['id']
return file_name
end
end
end
def ark_naan() Rails.configuration.id_namespace.sub(%r{.*ark:\/}, '')[0..4] end
#---
# Creates Wowza Token End Time
#
# @return Integer
#---
def secure_token_end_time
Time.zone = 'America/Los_Angeles'
Time.now.to_i + 48.hours
end
#------------
# /STREAMING
#------------
#---
# Check to see if an object has a "Restricted Notice"
#
# @return An HTML string if a restricted notice is present, nil otherwise
#---
def grabRestrictedText(data)
result = nil
if data != nil
data.each do |datum|
note = JSON.parse(datum)
if note['value'].start_with?('Culturally sensitive content: ', 'Copyrighted content: ','Embargoed content: ')
note_array = note['value'].split(': ')
result = "<h3>#{note_array[0].titleize}</h3><p>#{note_array[1]}</p>".html_safe
end
# Add 'View Content' button to certain cases
if note['value'].start_with?('Culturally sensitive content: ')
result += '<p>Would you like to view this content?</p><button type="button" id="view-masked-object" class="btn btn-primary btn-mini pull-right">Yes, I would like to view this content.</button>'.html_safe
end
end
end
result
end
#---
# Check to see if an object has a "metadataDisplay or localDisplay otherRights"
#
# @return An HTML string if an object has a "metadataDisplay or localDisplay otherRights", nil otherwise
#---
def grab_access_text(document)
out = []
data = rights_data document
return nil unless metadata_display?(data) && cannot?(:read, document)
out << content_tag(:h3, 'Restricted View')
out << content_tag(:p, get_attribution_note(document['otherNote_json_tesim']))
safe_join(out)
end
def get_attribution_note(data)
result = 'Content not available. Access may granted for research purposes at the discretion of the UC San Diego Library. For more information please contact the '
program_email = { 'Digital Library Development Program' => 'dlp@ucsd.edu', 'Special Collections & Archives' => 'spcoll@ucsd.edu', 'Research Data Curation Program' => 'research-data-curation@ucsd.edu'}
return result unless data
data.each do |datum|
note = JSON.parse(datum)
if note['type'].start_with?('local attribution')
program = note['value'].split(', ').first
result += "#{program} at #{program_email[program]}"
end
end
result
end
#---
# Normalized rdf view from DAMS4 REST API
#---
def normalized_rdf_path( pid )
# get REST API url from AF config
baseurl = ActiveFedora.fedora_config.credentials[:url].gsub(/\/fedora$/,'')
"#{baseurl}/api/objects/#{pid}/transform?recursive=true&xsl=normalize.xsl"
end
def rdf_edit_url (pid)
proxy_url = ActiveFedora.fedora_config.credentials[:proxy]
"#{proxy_url.nil? ? '' : proxy_url}/damsmanager/rdfImport.do?ark=#{pid}"
end
end