blacklight 3.0pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. data/.gitignore +17 -0
  2. data/.gitmodules +6 -0
  3. data/.yardopts +4 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +14 -0
  6. data/README.rdoc +168 -0
  7. data/Rakefile +9 -0
  8. data/app/controllers/bookmarks_controller.rb +98 -0
  9. data/app/controllers/feedback_controller.rb +37 -0
  10. data/app/controllers/folder_controller.rb +49 -0
  11. data/app/controllers/saved_searches_controller.rb +45 -0
  12. data/app/controllers/search_history_controller.rb +25 -0
  13. data/app/helpers/blacklight_helper.rb +606 -0
  14. data/app/helpers/bookmarks_helper.rb +3 -0
  15. data/app/helpers/catalog_helper.rb +65 -0
  16. data/app/helpers/feedback_helper.rb +2 -0
  17. data/app/helpers/hash_as_hidden_fields.rb +57 -0
  18. data/app/helpers/render_constraints_helper.rb +120 -0
  19. data/app/helpers/saved_searches_helper.rb +2 -0
  20. data/app/helpers/search_history_helper.rb +2 -0
  21. data/app/models/bookmark.rb +6 -0
  22. data/app/models/record_mailer.rb +43 -0
  23. data/app/models/search.rb +19 -0
  24. data/app/views/_flash_msg.html.erb +6 -0
  25. data/app/views/_user_util_links.html.erb +13 -0
  26. data/app/views/bookmarks/index.html.erb +33 -0
  27. data/app/views/catalog/_bookmark_control.html.erb +25 -0
  28. data/app/views/catalog/_bookmark_form.html.erb +8 -0
  29. data/app/views/catalog/_citation.html.erb +15 -0
  30. data/app/views/catalog/_constraints.html.erb +7 -0
  31. data/app/views/catalog/_constraints_element.html.erb +33 -0
  32. data/app/views/catalog/_did_you_mean.html.erb +10 -0
  33. data/app/views/catalog/_document_list.html.erb +30 -0
  34. data/app/views/catalog/_email_form.html.erb +11 -0
  35. data/app/views/catalog/_facet_limit.html.erb +33 -0
  36. data/app/views/catalog/_facet_pagination.html.erb +28 -0
  37. data/app/views/catalog/_facets.html.erb +9 -0
  38. data/app/views/catalog/_folder_control.html.erb +12 -0
  39. data/app/views/catalog/_home.html.erb +6 -0
  40. data/app/views/catalog/_home_text.html.erb +6 -0
  41. data/app/views/catalog/_index_partials/_default.erb +11 -0
  42. data/app/views/catalog/_marc_view.html.erb +33 -0
  43. data/app/views/catalog/_opensearch_response_metadata.html.erb +3 -0
  44. data/app/views/catalog/_previous_next_doc.html.erb +6 -0
  45. data/app/views/catalog/_refworks_form.html.erb +7 -0
  46. data/app/views/catalog/_results_pagination.html.erb +11 -0
  47. data/app/views/catalog/_search_form.html.erb +14 -0
  48. data/app/views/catalog/_show_partials/_default.html.erb +9 -0
  49. data/app/views/catalog/_show_sidebar.html.erb +1 -0
  50. data/app/views/catalog/_show_tools.html.erb +46 -0
  51. data/app/views/catalog/_sms_form.html.erb +23 -0
  52. data/app/views/catalog/_solr_request.html.erb +5 -0
  53. data/app/views/catalog/_sort_and_per_page.html.erb +20 -0
  54. data/app/views/catalog/_unapi_microformat.html.erb +1 -0
  55. data/app/views/catalog/citation.html.erb +1 -0
  56. data/app/views/catalog/email.erb +1 -0
  57. data/app/views/catalog/endnote.endnote.erb +1 -0
  58. data/app/views/catalog/facet.html.erb +28 -0
  59. data/app/views/catalog/index.atom.builder +108 -0
  60. data/app/views/catalog/index.html.erb +37 -0
  61. data/app/views/catalog/index.rss.builder +19 -0
  62. data/app/views/catalog/librarian_view.html.erb +3 -0
  63. data/app/views/catalog/opensearch.json.erb +0 -0
  64. data/app/views/catalog/opensearch.xml.erb +11 -0
  65. data/app/views/catalog/send_email_record.erb +0 -0
  66. data/app/views/catalog/show.endnote.erb +1 -0
  67. data/app/views/catalog/show.html.erb +42 -0
  68. data/app/views/catalog/show.refworks.erb +1 -0
  69. data/app/views/catalog/sms.erb +1 -0
  70. data/app/views/catalog/unapi.xml.builder +6 -0
  71. data/app/views/feedback/complete.html.erb +3 -0
  72. data/app/views/feedback/show.html.erb +20 -0
  73. data/app/views/folder/_tools.html.erb +23 -0
  74. data/app/views/folder/index.html.erb +44 -0
  75. data/app/views/layouts/blacklight.html.erb +49 -0
  76. data/app/views/record_mailer/email_record.erb +6 -0
  77. data/app/views/record_mailer/sms_record.erb +4 -0
  78. data/app/views/saved_searches/index.html.erb +27 -0
  79. data/app/views/search_history/index.html.erb +23 -0
  80. data/blacklight.gemspec +50 -0
  81. data/config.ru +4 -0
  82. data/config/routes.rb +54 -0
  83. data/db/seeds.rb +7 -0
  84. data/features/generators.feature +77 -0
  85. data/features/support/aruba.rb +9 -0
  86. data/install.rb +0 -0
  87. data/install/solr.yml +8 -0
  88. data/lib/blacklight.rb +121 -0
  89. data/lib/blacklight/catalog.rb +311 -0
  90. data/lib/blacklight/comma_link_renderer.rb +27 -0
  91. data/lib/blacklight/configurable.rb +46 -0
  92. data/lib/blacklight/controller.rb +121 -0
  93. data/lib/blacklight/engine.rb +32 -0
  94. data/lib/blacklight/exceptions.rb +13 -0
  95. data/lib/blacklight/marc.rb +46 -0
  96. data/lib/blacklight/marc/citation.rb +251 -0
  97. data/lib/blacklight/search_fields.rb +107 -0
  98. data/lib/blacklight/solr.rb +7 -0
  99. data/lib/blacklight/solr/document.rb +239 -0
  100. data/lib/blacklight/solr/document/dublin_core.rb +40 -0
  101. data/lib/blacklight/solr/document/email.rb +15 -0
  102. data/lib/blacklight/solr/document/marc.rb +84 -0
  103. data/lib/blacklight/solr/document/marc_export.rb +430 -0
  104. data/lib/blacklight/solr/document/sms.rb +13 -0
  105. data/lib/blacklight/solr/facet_paginator.rb +93 -0
  106. data/lib/blacklight/solr_helper.rb +413 -0
  107. data/lib/blacklight/user.rb +55 -0
  108. data/lib/blacklight/version.rb +3 -0
  109. data/lib/colorize.rb +196 -0
  110. data/lib/generators/blacklight/blacklight_generator.rb +134 -0
  111. data/lib/generators/blacklight/templates/SolrMarc.jar +0 -0
  112. data/lib/generators/blacklight/templates/catalog_controller.rb +8 -0
  113. data/lib/generators/blacklight/templates/config/SolrMarc/config-test.properties +37 -0
  114. data/lib/generators/blacklight/templates/config/SolrMarc/config.properties +37 -0
  115. data/lib/generators/blacklight/templates/config/SolrMarc/index.properties +97 -0
  116. data/lib/generators/blacklight/templates/config/SolrMarc/index_scripts/dewey.bsh +47 -0
  117. data/lib/generators/blacklight/templates/config/SolrMarc/index_scripts/format.bsh +126 -0
  118. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/README_MAPS +1 -0
  119. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/callnumber_map.properties +407 -0
  120. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/composition_era_map.properties +56 -0
  121. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/country_map.properties +379 -0
  122. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/format_map.properties +50 -0
  123. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/instrument_map.properties +101 -0
  124. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/language_map.properties +490 -0
  125. data/lib/generators/blacklight/templates/config/blacklight_config.rb +245 -0
  126. data/lib/generators/blacklight/templates/config/solr.yml +6 -0
  127. data/lib/generators/blacklight/templates/migrations/add_user_types_to_bookmarks_searches.rb +11 -0
  128. data/lib/generators/blacklight/templates/migrations/create_bookmarks.rb +17 -0
  129. data/lib/generators/blacklight/templates/migrations/create_searches.rb +15 -0
  130. data/lib/generators/blacklight/templates/migrations/remove_editable_fields_from_bookmarks.rb +11 -0
  131. data/lib/generators/blacklight/templates/public/images/blacklight/bg.png +0 -0
  132. data/lib/generators/blacklight/templates/public/images/blacklight/border.png +0 -0
  133. data/lib/generators/blacklight/templates/public/images/blacklight/bul_sq_gry.gif +0 -0
  134. data/lib/generators/blacklight/templates/public/images/blacklight/checkmark.gif +0 -0
  135. data/lib/generators/blacklight/templates/public/images/blacklight/logo.png +0 -0
  136. data/lib/generators/blacklight/templates/public/images/blacklight/magnifying_glass.gif +0 -0
  137. data/lib/generators/blacklight/templates/public/images/blacklight/remove.gif +0 -0
  138. data/lib/generators/blacklight/templates/public/images/blacklight/separator.gif +0 -0
  139. data/lib/generators/blacklight/templates/public/images/blacklight/start_over.gif +0 -0
  140. data/lib/generators/blacklight/templates/public/javascripts/blacklight.js +485 -0
  141. data/lib/generators/blacklight/templates/public/javascripts/jquery-1.4.2.min.js +154 -0
  142. data/lib/generators/blacklight/templates/public/javascripts/jquery-ui-1.8.1.custom.min.js +756 -0
  143. data/lib/generators/blacklight/templates/public/stylesheets/blacklight.css +487 -0
  144. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  145. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  146. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
  147. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  148. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  149. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  150. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_gloss-wave_35_558fd0_500x100.png +0 -0
  151. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  152. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  153. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
  154. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
  155. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_2e4f81_256x240.png +0 -0
  156. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
  157. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
  158. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/jquery-ui-1.8.1.custom.css +486 -0
  159. data/lib/generators/blacklight/templates/public/stylesheets/yui.css +31 -0
  160. data/lib/generators/blacklight/templates/solr_document.rb +30 -0
  161. data/lib/railties/blacklight.rake +66 -0
  162. data/lib/railties/cucumber.rake +53 -0
  163. data/lib/railties/rspec.rake +188 -0
  164. data/lib/railties/solr_marc.rake +148 -0
  165. data/lib/railties/test_solr_server.rb +130 -0
  166. data/spec/helpers/catalog_helper_spec.rb +111 -0
  167. data/spec/views/catalog/_sms_form.html.erb_spec.rb +19 -0
  168. data/tasks/blacklight_tasks.rake +4 -0
  169. data/uninstall.rb +1 -0
  170. 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,7 @@
1
+ module Blacklight::Solr
2
+
3
+ autoload :Facets, 'blacklight/solr/facets'
4
+ autoload :FacetPaginator, 'blacklight/solr/facet_paginator'
5
+ autoload :Document, 'blacklight/solr/document'
6
+
7
+ 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