blacklight 3.0pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/.gitmodules +6 -0
- data/.yardopts +4 -0
- data/Gemfile +4 -0
- data/LICENSE +14 -0
- data/README.rdoc +168 -0
- data/Rakefile +9 -0
- data/app/controllers/bookmarks_controller.rb +98 -0
- data/app/controllers/feedback_controller.rb +37 -0
- data/app/controllers/folder_controller.rb +49 -0
- data/app/controllers/saved_searches_controller.rb +45 -0
- data/app/controllers/search_history_controller.rb +25 -0
- data/app/helpers/blacklight_helper.rb +606 -0
- data/app/helpers/bookmarks_helper.rb +3 -0
- data/app/helpers/catalog_helper.rb +65 -0
- data/app/helpers/feedback_helper.rb +2 -0
- data/app/helpers/hash_as_hidden_fields.rb +57 -0
- data/app/helpers/render_constraints_helper.rb +120 -0
- data/app/helpers/saved_searches_helper.rb +2 -0
- data/app/helpers/search_history_helper.rb +2 -0
- data/app/models/bookmark.rb +6 -0
- data/app/models/record_mailer.rb +43 -0
- data/app/models/search.rb +19 -0
- data/app/views/_flash_msg.html.erb +6 -0
- data/app/views/_user_util_links.html.erb +13 -0
- data/app/views/bookmarks/index.html.erb +33 -0
- data/app/views/catalog/_bookmark_control.html.erb +25 -0
- data/app/views/catalog/_bookmark_form.html.erb +8 -0
- data/app/views/catalog/_citation.html.erb +15 -0
- data/app/views/catalog/_constraints.html.erb +7 -0
- data/app/views/catalog/_constraints_element.html.erb +33 -0
- data/app/views/catalog/_did_you_mean.html.erb +10 -0
- data/app/views/catalog/_document_list.html.erb +30 -0
- data/app/views/catalog/_email_form.html.erb +11 -0
- data/app/views/catalog/_facet_limit.html.erb +33 -0
- data/app/views/catalog/_facet_pagination.html.erb +28 -0
- data/app/views/catalog/_facets.html.erb +9 -0
- data/app/views/catalog/_folder_control.html.erb +12 -0
- data/app/views/catalog/_home.html.erb +6 -0
- data/app/views/catalog/_home_text.html.erb +6 -0
- data/app/views/catalog/_index_partials/_default.erb +11 -0
- data/app/views/catalog/_marc_view.html.erb +33 -0
- data/app/views/catalog/_opensearch_response_metadata.html.erb +3 -0
- data/app/views/catalog/_previous_next_doc.html.erb +6 -0
- data/app/views/catalog/_refworks_form.html.erb +7 -0
- data/app/views/catalog/_results_pagination.html.erb +11 -0
- data/app/views/catalog/_search_form.html.erb +14 -0
- data/app/views/catalog/_show_partials/_default.html.erb +9 -0
- data/app/views/catalog/_show_sidebar.html.erb +1 -0
- data/app/views/catalog/_show_tools.html.erb +46 -0
- data/app/views/catalog/_sms_form.html.erb +23 -0
- data/app/views/catalog/_solr_request.html.erb +5 -0
- data/app/views/catalog/_sort_and_per_page.html.erb +20 -0
- data/app/views/catalog/_unapi_microformat.html.erb +1 -0
- data/app/views/catalog/citation.html.erb +1 -0
- data/app/views/catalog/email.erb +1 -0
- data/app/views/catalog/endnote.endnote.erb +1 -0
- data/app/views/catalog/facet.html.erb +28 -0
- data/app/views/catalog/index.atom.builder +108 -0
- data/app/views/catalog/index.html.erb +37 -0
- data/app/views/catalog/index.rss.builder +19 -0
- data/app/views/catalog/librarian_view.html.erb +3 -0
- data/app/views/catalog/opensearch.json.erb +0 -0
- data/app/views/catalog/opensearch.xml.erb +11 -0
- data/app/views/catalog/send_email_record.erb +0 -0
- data/app/views/catalog/show.endnote.erb +1 -0
- data/app/views/catalog/show.html.erb +42 -0
- data/app/views/catalog/show.refworks.erb +1 -0
- data/app/views/catalog/sms.erb +1 -0
- data/app/views/catalog/unapi.xml.builder +6 -0
- data/app/views/feedback/complete.html.erb +3 -0
- data/app/views/feedback/show.html.erb +20 -0
- data/app/views/folder/_tools.html.erb +23 -0
- data/app/views/folder/index.html.erb +44 -0
- data/app/views/layouts/blacklight.html.erb +49 -0
- data/app/views/record_mailer/email_record.erb +6 -0
- data/app/views/record_mailer/sms_record.erb +4 -0
- data/app/views/saved_searches/index.html.erb +27 -0
- data/app/views/search_history/index.html.erb +23 -0
- data/blacklight.gemspec +50 -0
- data/config.ru +4 -0
- data/config/routes.rb +54 -0
- data/db/seeds.rb +7 -0
- data/features/generators.feature +77 -0
- data/features/support/aruba.rb +9 -0
- data/install.rb +0 -0
- data/install/solr.yml +8 -0
- data/lib/blacklight.rb +121 -0
- data/lib/blacklight/catalog.rb +311 -0
- data/lib/blacklight/comma_link_renderer.rb +27 -0
- data/lib/blacklight/configurable.rb +46 -0
- data/lib/blacklight/controller.rb +121 -0
- data/lib/blacklight/engine.rb +32 -0
- data/lib/blacklight/exceptions.rb +13 -0
- data/lib/blacklight/marc.rb +46 -0
- data/lib/blacklight/marc/citation.rb +251 -0
- data/lib/blacklight/search_fields.rb +107 -0
- data/lib/blacklight/solr.rb +7 -0
- data/lib/blacklight/solr/document.rb +239 -0
- data/lib/blacklight/solr/document/dublin_core.rb +40 -0
- data/lib/blacklight/solr/document/email.rb +15 -0
- data/lib/blacklight/solr/document/marc.rb +84 -0
- data/lib/blacklight/solr/document/marc_export.rb +430 -0
- data/lib/blacklight/solr/document/sms.rb +13 -0
- data/lib/blacklight/solr/facet_paginator.rb +93 -0
- data/lib/blacklight/solr_helper.rb +413 -0
- data/lib/blacklight/user.rb +55 -0
- data/lib/blacklight/version.rb +3 -0
- data/lib/colorize.rb +196 -0
- data/lib/generators/blacklight/blacklight_generator.rb +134 -0
- data/lib/generators/blacklight/templates/SolrMarc.jar +0 -0
- data/lib/generators/blacklight/templates/catalog_controller.rb +8 -0
- data/lib/generators/blacklight/templates/config/SolrMarc/config-test.properties +37 -0
- data/lib/generators/blacklight/templates/config/SolrMarc/config.properties +37 -0
- data/lib/generators/blacklight/templates/config/SolrMarc/index.properties +97 -0
- data/lib/generators/blacklight/templates/config/SolrMarc/index_scripts/dewey.bsh +47 -0
- data/lib/generators/blacklight/templates/config/SolrMarc/index_scripts/format.bsh +126 -0
- data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/README_MAPS +1 -0
- data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/callnumber_map.properties +407 -0
- data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/composition_era_map.properties +56 -0
- data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/country_map.properties +379 -0
- data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/format_map.properties +50 -0
- data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/instrument_map.properties +101 -0
- data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/language_map.properties +490 -0
- data/lib/generators/blacklight/templates/config/blacklight_config.rb +245 -0
- data/lib/generators/blacklight/templates/config/solr.yml +6 -0
- data/lib/generators/blacklight/templates/migrations/add_user_types_to_bookmarks_searches.rb +11 -0
- data/lib/generators/blacklight/templates/migrations/create_bookmarks.rb +17 -0
- data/lib/generators/blacklight/templates/migrations/create_searches.rb +15 -0
- data/lib/generators/blacklight/templates/migrations/remove_editable_fields_from_bookmarks.rb +11 -0
- data/lib/generators/blacklight/templates/public/images/blacklight/bg.png +0 -0
- data/lib/generators/blacklight/templates/public/images/blacklight/border.png +0 -0
- data/lib/generators/blacklight/templates/public/images/blacklight/bul_sq_gry.gif +0 -0
- data/lib/generators/blacklight/templates/public/images/blacklight/checkmark.gif +0 -0
- data/lib/generators/blacklight/templates/public/images/blacklight/logo.png +0 -0
- data/lib/generators/blacklight/templates/public/images/blacklight/magnifying_glass.gif +0 -0
- data/lib/generators/blacklight/templates/public/images/blacklight/remove.gif +0 -0
- data/lib/generators/blacklight/templates/public/images/blacklight/separator.gif +0 -0
- data/lib/generators/blacklight/templates/public/images/blacklight/start_over.gif +0 -0
- data/lib/generators/blacklight/templates/public/javascripts/blacklight.js +485 -0
- data/lib/generators/blacklight/templates/public/javascripts/jquery-1.4.2.min.js +154 -0
- data/lib/generators/blacklight/templates/public/javascripts/jquery-ui-1.8.1.custom.min.js +756 -0
- data/lib/generators/blacklight/templates/public/stylesheets/blacklight.css +487 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_gloss-wave_35_558fd0_500x100.png +0 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_2e4f81_256x240.png +0 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
- data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/jquery-ui-1.8.1.custom.css +486 -0
- data/lib/generators/blacklight/templates/public/stylesheets/yui.css +31 -0
- data/lib/generators/blacklight/templates/solr_document.rb +30 -0
- data/lib/railties/blacklight.rake +66 -0
- data/lib/railties/cucumber.rake +53 -0
- data/lib/railties/rspec.rake +188 -0
- data/lib/railties/solr_marc.rake +148 -0
- data/lib/railties/test_solr_server.rb +130 -0
- data/spec/helpers/catalog_helper_spec.rb +111 -0
- data/spec/views/catalog/_sms_form.html.erb_spec.rb +19 -0
- data/tasks/blacklight_tasks.rake +4 -0
- data/uninstall.rb +1 -0
- metadata +431 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
##
|
|
2
|
+
# Module to deal with accessing (and setting some defaults) in an array of
|
|
3
|
+
# hashes that describe Blacklight search fields. Requires the base class this
|
|
4
|
+
# module is added to implements a #config method that returns a hash, where
|
|
5
|
+
# config[:search_fields] will be an array of hashes describing search fields.
|
|
6
|
+
#
|
|
7
|
+
# = Search Field Configuration Hash =
|
|
8
|
+
# [:key]
|
|
9
|
+
# "title", required, unique key used in search URLs to specify search_field
|
|
10
|
+
# [:display_label]
|
|
11
|
+
# "Title", # user-displayable label, optional, if not supplied :key.titlecase will be used
|
|
12
|
+
# [:qt]
|
|
13
|
+
# "search", # Solr qt param, request handler, usually can be left blank; defaults to Blacklight.config[:default_solr_params][:qt] if not specified.
|
|
14
|
+
# [:solr_parameters]
|
|
15
|
+
# {:qf => "something"} # optional hash of additional parameters to pass to solr for searches on this field.
|
|
16
|
+
# [:solr_local_parameters]
|
|
17
|
+
# {:qf => "$something"} # optional hash of additional parameters that will be passed using Solr LocalParams syntax, that can use dollar sign to reference other solr variables.
|
|
18
|
+
# [:include_in_simple_select]
|
|
19
|
+
# false. Defaults to true, but you can set to false to have a search field defined for deep-links or BL extensions, but not actually included in the HTML select for simple search choice.
|
|
20
|
+
#
|
|
21
|
+
# Optionally you can supply a :key, which is what Blacklight will use
|
|
22
|
+
# to identify this search field in HTTP query params. If no :key is
|
|
23
|
+
# supplied, one will be computed from the :display_label. If that will
|
|
24
|
+
# result in a collision of keys, you should supply one explicitly.
|
|
25
|
+
#
|
|
26
|
+
##
|
|
27
|
+
module Blacklight::SearchFields
|
|
28
|
+
extend ActiveSupport::Memoizable
|
|
29
|
+
|
|
30
|
+
# Looks up search field config list from config[:search_fields], and
|
|
31
|
+
# 'normalizes' all field config hashes using normalize_config method.
|
|
32
|
+
# Memoized for efficiency of normalization.
|
|
33
|
+
def search_field_list
|
|
34
|
+
normalized = config[:search_fields].collect {|obj| normalize_config(obj)}
|
|
35
|
+
|
|
36
|
+
if (duplicates = normalized.collect{|h| h[:key]}.uniq!)
|
|
37
|
+
raise "Duplicate keys found in search_field config: #{duplicates.inspect}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
normalized
|
|
41
|
+
end
|
|
42
|
+
memoize :search_field_list
|
|
43
|
+
|
|
44
|
+
# Returns suitable argument to options_for_select method, to create
|
|
45
|
+
# an html select based on #search_field_list. Skips search_fields
|
|
46
|
+
# marked :include_in_simple_select => false
|
|
47
|
+
def search_field_options_for_select
|
|
48
|
+
search_field_list.collect do |field_def|
|
|
49
|
+
[field_def[:display_label], field_def[:key]] unless field_def[:include_in_simple_select] == false
|
|
50
|
+
end.compact
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Looks up a search field config hash from search_field_list having
|
|
54
|
+
# a certain supplied :key.
|
|
55
|
+
def search_field_def_for_key(key)
|
|
56
|
+
return nil if key.blank?
|
|
57
|
+
search_field_list.find {|c| c[:key] == key}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Returns default search field, used for simpler display in history, etc.
|
|
61
|
+
# if not set in config, defaults to first field listed in #search_field_list
|
|
62
|
+
def default_search_field
|
|
63
|
+
config[:default_search_field] || search_field_list[0]
|
|
64
|
+
end
|
|
65
|
+
memoize :default_search_field
|
|
66
|
+
|
|
67
|
+
# Shortcut for commonly needed operation, look up display
|
|
68
|
+
# label for the key specified. Returns "Keyword" if a label
|
|
69
|
+
# can't be found.
|
|
70
|
+
def label_for_search_field(key)
|
|
71
|
+
field_def = search_field_def_for_key(key)
|
|
72
|
+
if field_def && field_def[:display_label]
|
|
73
|
+
field_def[:display_label]
|
|
74
|
+
else
|
|
75
|
+
"Keyword"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
protected
|
|
80
|
+
# Fill in missing default values in a search_field config hash.
|
|
81
|
+
def normalize_config(field_hash)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# Accept legacy two-element array, if it's not a Hash, assume it's legacy.
|
|
85
|
+
# No great way to 'duck type' here.
|
|
86
|
+
unless ( field_hash.kind_of?(Hash))
|
|
87
|
+
# Consistent with legacy behavior where two fields can have the same label,
|
|
88
|
+
# as long as they have different qt's, we base the unique :key on :qt.
|
|
89
|
+
field_hash = {:display_label => field_hash[0], :key => field_hash[1], :qt => field_hash[1]}
|
|
90
|
+
else
|
|
91
|
+
# Make a copy of passed in Hash so we don't alter original.
|
|
92
|
+
field_hash = field_hash.clone
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
raise Exception.new("Search field config is missing ':key' => #{field_hash.inspect}") unless field_hash[:key]
|
|
96
|
+
|
|
97
|
+
# If no display_label was provided, turn the :key into one.
|
|
98
|
+
field_hash[:display_label] ||= field_hash[:key].titlecase
|
|
99
|
+
|
|
100
|
+
# If no :qt was provided, take from config default
|
|
101
|
+
field_hash[:qt] ||= config[:default_solr_params][:qt] if config[:default_solr_params]
|
|
102
|
+
|
|
103
|
+
field_hash
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
end
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
require 'rsolr'
|
|
2
|
+
require 'rsolr-ext'
|
|
3
|
+
##
|
|
4
|
+
##
|
|
5
|
+
# = Introduction
|
|
6
|
+
# Blacklight::Solr::Document is the module with logic for a class representing
|
|
7
|
+
# an individual document returned from Solr results. It can be added in to any
|
|
8
|
+
# local class you want, but in default Blacklight a SolrDocument class is
|
|
9
|
+
# provided for you which is pretty much a blank class "include"ing
|
|
10
|
+
# Blacklight::Solr::Document.
|
|
11
|
+
#
|
|
12
|
+
# Blacklight::Solr::Document mixes in Rsolr::Ext::Model to the calling class.
|
|
13
|
+
# It also provides some DefaultFinders.
|
|
14
|
+
#
|
|
15
|
+
# It also provides support for Document Extensions, which advertise supported
|
|
16
|
+
# transformation formats.
|
|
17
|
+
#
|
|
18
|
+
# = Document Extensions
|
|
19
|
+
# An Blacklight::Solr::Document extension is simply a ruby module which is mixed
|
|
20
|
+
# in to individual Document instances. The intended use case is for documents
|
|
21
|
+
# containing some particular format of source material, such as Marc. An
|
|
22
|
+
# extension can be registered with your document class, along with a block
|
|
23
|
+
# containing custom logic for which documents to apply the extension to.
|
|
24
|
+
#
|
|
25
|
+
# SolrDocument.use_extension(MyExtension) {|document| my_logic_on_document(document}
|
|
26
|
+
#
|
|
27
|
+
# MyExtension will be mixed-in (using ruby 'extend') only to those documents
|
|
28
|
+
# where the block results in true.
|
|
29
|
+
#
|
|
30
|
+
# == Transformation conventions
|
|
31
|
+
# The main use case for extensions is for transforming a Document to another
|
|
32
|
+
# format. Either to another type of Ruby object, or to an exportable string in
|
|
33
|
+
# a certain format.
|
|
34
|
+
#
|
|
35
|
+
# The convention for methods contained in extensions that transform to a ruby
|
|
36
|
+
# object is "to_*". For instance, "to_marc" would return a Ruby Marc object.
|
|
37
|
+
#
|
|
38
|
+
# The convention for methods contained in extensions that transform to an
|
|
39
|
+
# exportable file of some kind is "export_as_*". For instance,
|
|
40
|
+
# "export_as_marc21" would return a String object containing valid marc21, and
|
|
41
|
+
# "export_as_marcxml" would return a String object containing valid marcxml.
|
|
42
|
+
#
|
|
43
|
+
# The tokens used after "export_as" should normally be the format names as
|
|
44
|
+
# registered with Rails Mime::Type.
|
|
45
|
+
#
|
|
46
|
+
# == Advertising export formats
|
|
47
|
+
#
|
|
48
|
+
# If an extension advertises what export formats it can provide, than those
|
|
49
|
+
# formats will automatically be delivered by the Blacklight catalog/show
|
|
50
|
+
# controller, and potentially automatically advertised in various places
|
|
51
|
+
# that advertise available formats. (UnAPI; HTML link rel=alternate; Atom
|
|
52
|
+
# link rel=alterate; etc).
|
|
53
|
+
#
|
|
54
|
+
# Export formats are 'registered' by calling the #will_export_as method
|
|
55
|
+
# on a Document instance. An extension would usually do this in a
|
|
56
|
+
# self.extended method, so it can be called on Documents that have
|
|
57
|
+
# the given extension added to them. For instance:
|
|
58
|
+
#
|
|
59
|
+
# module DemoMarcExtension
|
|
60
|
+
# def self.extended(document)
|
|
61
|
+
# document.will_export_as(:marc21, "application/marc")
|
|
62
|
+
# document.will_export_as(:marcxml, "application/marcxml+xml")
|
|
63
|
+
# end
|
|
64
|
+
#
|
|
65
|
+
# def export_as_marc21 ; something ; end
|
|
66
|
+
# def export_as_marcxml ; something ; end
|
|
67
|
+
# end
|
|
68
|
+
#
|
|
69
|
+
# == Extension Parameters
|
|
70
|
+
# Every class that includes Blacklight::Solr::Document gets a
|
|
71
|
+
# #extension_parameters method for saving arbitrary parameters on class-wide
|
|
72
|
+
# level that can be retrieved by extensions. These are arbitrary, just
|
|
73
|
+
# conventions with a given extension. For instance:
|
|
74
|
+
# SolrDocument.extension_parameters[:marc_source_field] = "solr_stored_field_name"
|
|
75
|
+
#
|
|
76
|
+
module Blacklight::Solr::Document
|
|
77
|
+
autoload :Marc, 'blacklight/solr/document/marc'
|
|
78
|
+
autoload :MarcExport, 'blacklight/solr/document/marc_export'
|
|
79
|
+
autoload :DublinCore, 'blacklight/solr/document/dublin_core'
|
|
80
|
+
autoload :Email, 'blacklight/solr/document/email'
|
|
81
|
+
autoload :Sms, 'blacklight/solr/document/sms'
|
|
82
|
+
|
|
83
|
+
def self.included(base)
|
|
84
|
+
base.send :include, RSolr::Ext::Model
|
|
85
|
+
base.send :extend, ClassMethods
|
|
86
|
+
|
|
87
|
+
# after_initialize hook comes from RSolr::Ext::Model, I think.
|
|
88
|
+
# We need to make sure all extensions get applied.
|
|
89
|
+
base.after_initialize do
|
|
90
|
+
apply_extensions
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# Needs to be called in initializer of class including this module, to
|
|
96
|
+
# apply all registered extensions on a per-document basis
|
|
97
|
+
def apply_extensions
|
|
98
|
+
self.class.registered_extensions.each do | registration|
|
|
99
|
+
self.extend( registration[:module_obj] ) if registration[:condition_proc].nil? || registration[:condition_proc].call( self )
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
##
|
|
104
|
+
# Register exportable formats supported by the individual document.
|
|
105
|
+
# Usually called by an extension in it's self.extended method, to
|
|
106
|
+
# register the formats that extension can export.
|
|
107
|
+
#
|
|
108
|
+
# some_document.will_export_as(:some_format, "application/type") means
|
|
109
|
+
# that the document (usually via an extension) has a method
|
|
110
|
+
# "export_as_some_format" which returns a String of content that
|
|
111
|
+
# is described by the mime content_type given.
|
|
112
|
+
#
|
|
113
|
+
# The format name should ideally _already_ be registered with
|
|
114
|
+
# Rails Mime::Type, in your application initializer, representing
|
|
115
|
+
# the content type given. However, this method will attempt to
|
|
116
|
+
# register it using Mime::Type.register_alias if it's not previously
|
|
117
|
+
# registered. This is a bit sketchy though.
|
|
118
|
+
def will_export_as(short_name, content_type = nil)
|
|
119
|
+
#Lookup in Rails Mime::Type, register if needed, otherwise take
|
|
120
|
+
# content-type from registration if needed. This uses
|
|
121
|
+
# some 'api' to Mime::Type that may or may not be entirely
|
|
122
|
+
# public, the fact that a Mime::CONST is registered for every
|
|
123
|
+
# type. But that's the only way to do the kind of check we need, sorry.
|
|
124
|
+
begin
|
|
125
|
+
mime_type = "Mime::#{short_name.to_s.upcase}".constantize
|
|
126
|
+
content_type = mime_type.to_s unless content_type
|
|
127
|
+
rescue NameError
|
|
128
|
+
# not registered, we need to register. Use register_alias to be least
|
|
129
|
+
# likely to interfere with host app.
|
|
130
|
+
Mime::Type.register_alias(content_type, short_name)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# if content_type is nil, look it up from Rails Mime::Type
|
|
134
|
+
if content_type.nil?
|
|
135
|
+
# Accurate lookup in Rails Mime::Type is kind of pain, it doesn't
|
|
136
|
+
# really provide the right API.
|
|
137
|
+
if defined?(type_const_name)
|
|
138
|
+
content_type = type_const_name.constantize.to_s
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
export_formats[short_name] = {:content_type => content_type}
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Collects formats that this doc can export as.
|
|
145
|
+
# Returns a hash, keys are format short-names that can
|
|
146
|
+
# be exported. Hash includes:
|
|
147
|
+
# :content-type => mime-content-type
|
|
148
|
+
# maybe more later
|
|
149
|
+
# To see if a given export format is supported by this document,
|
|
150
|
+
# simply call document.export_formats.keys.include?(:my_format)
|
|
151
|
+
# Then call #export_as! to do the export.
|
|
152
|
+
def export_formats
|
|
153
|
+
@export_formats ||= {}
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Call with a format shortname, export_as(:marc), simply returns
|
|
157
|
+
# #export_as_marc . Later we may expand the design to allow you
|
|
158
|
+
# to register an arbitrary method name instead of insisting
|
|
159
|
+
# on the convention, so clients should call this method so
|
|
160
|
+
# they'll still keep working if we do that.
|
|
161
|
+
def export_as(short_name)
|
|
162
|
+
send("export_as_#{short_name.to_s}")
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Returns a hash keyed by semantic tokens (see ExtendableClassMethods#semantic_fields), value is an array of
|
|
166
|
+
# strings. (Array to handle multi-value fields). If no value(s)
|
|
167
|
+
# available, empty array is returned.
|
|
168
|
+
#
|
|
169
|
+
# Default implementation here uses ExtendableClassMethods#semantic_fields
|
|
170
|
+
# to just take values from Solr stored fields.
|
|
171
|
+
# Extensions can over-ride this method to provide better/different lookup,
|
|
172
|
+
# but extensions should call super and modify hash returned, to avoid
|
|
173
|
+
# unintentionally erasing values provided by other extensions.
|
|
174
|
+
def to_semantic_values
|
|
175
|
+
unless @semantic_value_hash
|
|
176
|
+
@semantic_value_hash = Hash.new([]) # default to empty array
|
|
177
|
+
self.class.field_semantics.each_pair do |key, solr_field|
|
|
178
|
+
value = self[solr_field]
|
|
179
|
+
# Make single and multi-values all arrays, so clients
|
|
180
|
+
# don't have to know.
|
|
181
|
+
unless value.nil?
|
|
182
|
+
value = [value] unless value.kind_of?(Array)
|
|
183
|
+
@semantic_value_hash[key] = value
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
return @semantic_value_hash
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# Certain class-level methods needed for the document-specific
|
|
192
|
+
# extendability architecture
|
|
193
|
+
module ClassMethods
|
|
194
|
+
attr_writer :registered_extensions
|
|
195
|
+
|
|
196
|
+
# Returns array of hashes of registered extensions. Each hash
|
|
197
|
+
# has a :module_obj key and a :condition_proc key. Usually this
|
|
198
|
+
# method is only used internally in #apply_extensions, but if you
|
|
199
|
+
# want to zero out all previously registered extensions you can call:
|
|
200
|
+
# SolrDocument.registered_extensions = nil
|
|
201
|
+
def registered_extensions
|
|
202
|
+
@registered_extensions ||= []
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def extension_parameters
|
|
206
|
+
@extension_parameters ||= {}
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Register an extension module with the class. A block taking one
|
|
210
|
+
# parameter can be supplied; the block will be passed an instance of
|
|
211
|
+
# a Document, and the extension will be applied only if the block
|
|
212
|
+
# evaluates as true. If no condition is given, the extension will
|
|
213
|
+
# be applied to every instance of the class.
|
|
214
|
+
#
|
|
215
|
+
# SolrDocument.use_extension( SomeExtensionModule ) { | document | should_apply_some_extension?(document) }
|
|
216
|
+
# SolrDocument.use_extension( SomeExtensionModule) # will be applied to all docs
|
|
217
|
+
def use_extension( module_obj, &condition )
|
|
218
|
+
registered_extensions << {:module_obj => module_obj, :condition_proc => condition}
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Class-level method for accessing/setting semantic mappings
|
|
222
|
+
# for solr stored fields. Can be set by local app, key is
|
|
223
|
+
# a symbol for a semantic, value is a solr _stored_ field.
|
|
224
|
+
#
|
|
225
|
+
# Stored field can be single or multi-value. In some cases
|
|
226
|
+
# clients may only use the first value from a multi-value field.
|
|
227
|
+
#
|
|
228
|
+
# Currently documented semantic tokens, not all may be
|
|
229
|
+
# used by core BL, but some may be used by plugins present
|
|
230
|
+
# or future.
|
|
231
|
+
# :title, :author, :year, :language => User-presentable strings.
|
|
232
|
+
def field_semantics
|
|
233
|
+
@field_semantics ||= {}
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'builder'
|
|
2
|
+
|
|
3
|
+
# This module provide Dublin Core export based on the document's semantic values
|
|
4
|
+
module Blacklight::Solr::Document::DublinCore
|
|
5
|
+
def self.extended(document)
|
|
6
|
+
# Register our exportable formats
|
|
7
|
+
Blacklight::Solr::Document::DublinCore.register_export_formats( document )
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.register_export_formats(document)
|
|
11
|
+
document.will_export_as(:xml)
|
|
12
|
+
document.will_export_as(:dc_xml, "text/xml")
|
|
13
|
+
document.will_export_as(:oai_dc_xml, "text/xml")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def dublin_core_field_names
|
|
17
|
+
[:contributor, :coverage, :creator, :date, :description, :format, :identifier, :language, :publisher, :relation, :rights, :source, :subject, :title, :type]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# dublin core elements are mapped against the #dublin_core_field_names whitelist.
|
|
21
|
+
def export_as_oai_dc_xml
|
|
22
|
+
xml = Builder::XmlMarkup.new
|
|
23
|
+
xml.tag!("oai_dc:dc",
|
|
24
|
+
'xmlns:oai_dc' => "http://www.openarchives.org/OAI/2.0/oai_dc/",
|
|
25
|
+
'xmlns:dc' => "http://purl.org/dc/elements/1.1/",
|
|
26
|
+
'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
|
|
27
|
+
'xsi:schemaLocation' => %{http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd}) do
|
|
28
|
+
self.to_semantic_values.select { |field, values| dublin_core_field_names.include? field.to_sym }.each do |field,values|
|
|
29
|
+
values.each do |v|
|
|
30
|
+
xml.tag! 'dc:' + field.to_s, v
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
xml.target!
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
alias_method :export_as_xml, :export_as_oai_dc_xml
|
|
38
|
+
alias_method :export_as_dc_xml, :export_as_oai_dc_xml
|
|
39
|
+
|
|
40
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# This module provides the body of an email export based on the document's semantic values
|
|
2
|
+
module Blacklight::Solr::Document::Email
|
|
3
|
+
|
|
4
|
+
# Return a text string that will be the body of the email
|
|
5
|
+
def to_email_text
|
|
6
|
+
semantics = self.to_semantic_values
|
|
7
|
+
body = ""
|
|
8
|
+
body << "Title: #{semantics[:title].join(" ")}\n" unless semantics[:title].blank?
|
|
9
|
+
body << "Author: #{semantics[:author].join(" ")}\n" unless semantics[:author].blank?
|
|
10
|
+
body << "Format: #{semantics[:format].join(" ")}\n" unless semantics[:format].blank?
|
|
11
|
+
body << "Language: #{semantics[:language].join(" ")}" unless semantics[:language].blank?
|
|
12
|
+
return body unless body.blank?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
|
|
2
|
+
# This is a document extension meant to be mixed into a
|
|
3
|
+
# Blacklight::Solr::Document class, such as SolrDocument. It provides support
|
|
4
|
+
# for restoration of MARC data (xml or binary) from a Solr stored field, and
|
|
5
|
+
# then provides various transformations/exports of that Marc via the included
|
|
6
|
+
# Blacklight::Solr::Document::MarcExport module.
|
|
7
|
+
#
|
|
8
|
+
# This extension would normally be registered using
|
|
9
|
+
# Blacklight::Solr::Document#use_extension. eg:
|
|
10
|
+
#
|
|
11
|
+
# SolrDocument.use_extension( Blacklight::Solr::Document::Marc ) { |document| my_logic_for_document_has_marc?( document ) }
|
|
12
|
+
#
|
|
13
|
+
# This extension also expects a :marc_source_field and :marc_format_type to
|
|
14
|
+
# be registered with the hosting classes extension_parameters. In an initializer
|
|
15
|
+
# or other startup code:
|
|
16
|
+
# SolrDocument.extension_paramters[:marc_source_field] = "name_of_solr_stored_field"
|
|
17
|
+
# SolrDocument.extension_parameters[:marc_format_type] = :marc21 # or :marcxml
|
|
18
|
+
module Blacklight::Solr::Document::Marc
|
|
19
|
+
|
|
20
|
+
include Blacklight::Solr::Document::MarcExport # All our export_as stuff based on to_marc.
|
|
21
|
+
|
|
22
|
+
class UnsupportedMarcFormatType < RuntimeError; end
|
|
23
|
+
|
|
24
|
+
def self.extended(document)
|
|
25
|
+
# Register our exportable formats, we inherit these from MarcExport
|
|
26
|
+
Blacklight::Solr::Document::MarcExport.register_export_formats( document )
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# DEPRECATED. Here for legacy purposes, but use to_marc instead. Or
|
|
30
|
+
# internally, use the protected _marc_helper method to get the
|
|
31
|
+
# (somewhat confusingly named) Blacklight::Marc::Document helper object.
|
|
32
|
+
#
|
|
33
|
+
# This method gets attached to a SolrDocument.
|
|
34
|
+
# it uses the marc_source_field and marc_format_type
|
|
35
|
+
# class attributes to create the Blacklight::Marc::Document instance.
|
|
36
|
+
# Only returns a Blacklight::Marc::Document instance if
|
|
37
|
+
# the self.class.marc_source_field key exists.
|
|
38
|
+
def marc
|
|
39
|
+
warn "[DEPRECATION] aDocument.marc is deprecated. Please use aDocument.respond_to?(:to_marc) / aDocument.respond_to?(:marc), or aDocument.exports_as.keys.include?(:some_format) / aDocument.export_as(:some_format) instead."
|
|
40
|
+
|
|
41
|
+
_marc_helper
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# ruby-marc object
|
|
45
|
+
def to_marc
|
|
46
|
+
@_ruby_marc_obj ||= load_marc
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
protected
|
|
51
|
+
def marc_source
|
|
52
|
+
@_marc_source ||= fetch(_marc_source_field)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def load_marc
|
|
56
|
+
case _marc_format_type.to_s
|
|
57
|
+
when 'marcxml'
|
|
58
|
+
records = MARC::XMLReader.new(StringIO.new( fetch(_marc_source_field) )).to_a
|
|
59
|
+
return records[0]
|
|
60
|
+
when 'marc21'
|
|
61
|
+
return MARC::Record.new_from_marc( fetch(_marc_source_field) )
|
|
62
|
+
else
|
|
63
|
+
|
|
64
|
+
raise UnsupportedMarcFormatType.new("Only marcxml and marc21 are supported, this documents format is #{_marc_format_type} and the current extension parameters are #{self.class.extension_parameters.inspect}")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _marc_helper
|
|
71
|
+
@_marc_helper ||= (
|
|
72
|
+
Blacklight::Marc::Document.new fetch(_marc_source_field), _marc_format_type )
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def _marc_source_field
|
|
76
|
+
self.class.extension_parameters[:marc_source_field]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def _marc_format_type
|
|
80
|
+
#TODO: Raise if not present
|
|
81
|
+
self.class.extension_parameters[:marc_format_type]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|