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.
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