blacklight 3.1.0 → 3.1.1

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 (28) hide show
  1. data/README.md +1 -1
  2. data/VERSION +1 -1
  3. data/app/controllers/catalog_controller.rb +6 -0
  4. data/app/helpers/blacklight/blacklight_helper_behavior.rb +416 -0
  5. data/app/helpers/blacklight/catalog_helper_behavior.rb +87 -0
  6. data/app/helpers/blacklight/facets_helper_behavior.rb +114 -0
  7. data/app/helpers/{hash_as_hidden_fields.rb → blacklight/hash_as_hidden_fields_helper_behavior.rb} +1 -1
  8. data/app/helpers/blacklight/html_head_helper_behavior.rb +103 -0
  9. data/app/helpers/blacklight/render_constraints_helper_behavior.rb +121 -0
  10. data/app/helpers/blacklight_helper.rb +1 -395
  11. data/app/helpers/catalog_helper.rb +1 -85
  12. data/app/helpers/facets_helper.rb +1 -112
  13. data/app/helpers/hash_as_hidden_fields_helper.rb +3 -0
  14. data/app/helpers/html_head_helper.rb +1 -101
  15. data/app/helpers/render_constraints_helper.rb +1 -119
  16. data/app/views/catalog/{_index_partials/_default.erb → _index_default.html.erb} +0 -0
  17. data/app/views/catalog/{_show_partials/_default.html.erb → _show_default.html.erb} +0 -0
  18. data/lib/blacklight/solr/document/marc_export.rb +2 -6
  19. data/test_support/spec/helpers/hash_as_hidden_fields_spec.rb +2 -2
  20. data/test_support/spec/lib/marc_export_spec.rb +1 -1
  21. data/test_support/spec/views/catalog/{_index_partials/_default.erb_spec.rb → _index_default.erb_spec.rb} +2 -2
  22. data/test_support/spec/views/catalog/{_show_partials/_default.html.erb_spec.rb → _show_default.erb_spec.rb} +2 -2
  23. metadata +31 -31
  24. data/app/helpers/bookmarks_helper.rb +0 -4
  25. data/app/helpers/feedback_helper.rb +0 -3
  26. data/app/helpers/saved_searches_helper.rb +0 -3
  27. data/app/helpers/search_history_helper.rb +0 -3
  28. data/test_support/spec/helpers/search_history_helper_spec.rb +0 -12
data/README.md CHANGED
@@ -7,7 +7,7 @@ Blacklight is open source discovery software. Libraries (or anyone else) can use
7
7
 
8
8
  * [Project Homepage](http://projectblacklight.org)
9
9
  * [Developer Documentation](https://github.com/projectblacklight/blacklight/wiki)
10
- * [Quickstart Guide](https://github.com/projectblacklight/blacklight/wiki/Blacklight-3.x-Quickstart)
10
+ * [Quickstart Guide](https://github.com/projectblacklight/blacklight/wiki/Quickstart)
11
11
  * [JIRA Issue Tracker](http://jira.projectblacklight.org/jira/secure/Dashboard.jspa)
12
12
  * [Support](http://projectblacklight.org/support.html)
13
13
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.1.0
1
+ 3.1.1
@@ -0,0 +1,6 @@
1
+ # -*- encoding : utf-8 -*-
2
+ class CatalogController < ApplicationController
3
+
4
+ include Blacklight::Catalog
5
+
6
+ end
@@ -0,0 +1,416 @@
1
+ # -*- encoding : utf-8 -*-
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ # Methods added to this helper will be available to all templates in the hosting application
5
+ #
6
+ module Blacklight::BlacklightHelperBehavior
7
+ include HashAsHiddenFieldsHelper
8
+ include RenderConstraintsHelper
9
+ include HtmlHeadHelper
10
+ include FacetsHelper
11
+
12
+
13
+ def application_name
14
+ 'Blacklight'
15
+ end
16
+
17
+
18
+ # Create <link rel="alternate"> links from a documents dynamically
19
+ # provided export formats. Currently not used by standard BL layouts,
20
+ # but available for your custom layouts to provide link rel alternates.
21
+ #
22
+ # Returns empty string if no links available.
23
+ #
24
+ # :unique => true, will ensure only one link is output for every
25
+ # content type, as required eg in atom. Which one 'wins' is arbitrary.
26
+ # :exclude => array of format shortnames, formats to not include at all.
27
+ def render_link_rel_alternates(document=@document, options = {})
28
+ options = {:unique => false, :exclude => []}.merge(options)
29
+
30
+ return nil if document.nil?
31
+
32
+ seen = Set.new
33
+
34
+ html = ""
35
+ document.export_formats.each_pair do |format, spec|
36
+ unless( options[:exclude].include?(format) ||
37
+ (options[:unique] && seen.include?(spec[:content_type]))
38
+ )
39
+ html << tag(:link, {:rel=>"alternate", :title=>format, :type => spec[:content_type], :href=> catalog_url(document.id, format)}) << "\n"
40
+
41
+ seen.add(spec[:content_type]) if options[:unique]
42
+ end
43
+ end
44
+ return html.html_safe
45
+ end
46
+
47
+ def render_opensearch_response_metadata
48
+ render :partial => 'catalog/opensearch_response_metadata'
49
+ end
50
+
51
+ def render_body_class
52
+ extra_body_classes.join " "
53
+ end
54
+
55
+ # collection of items to be rendered in the @sidebar
56
+ def sidebar_items
57
+ @sidebar_items ||= []
58
+ end
59
+
60
+ def extra_body_classes
61
+ @extra_body_classes ||= ['blacklight-' + controller.controller_name, 'blacklight-' + [controller.controller_name, controller.action_name].join('-')]
62
+ end
63
+
64
+
65
+ def render_document_list_partial options={}
66
+ render :partial=>'catalog/document_list'
67
+ end
68
+
69
+ # Save function area for search results 'index' view, normally
70
+ # renders next to title. Includes just 'Folder' by default.
71
+ def render_index_doc_actions(document, options={})
72
+ content_tag("div", :class=>"documentFunctions") do
73
+ raw("#{render(:partial => 'bookmark_control', :locals => {:document=> document}.merge(options))}
74
+ #{render(:partial => 'folder_control', :locals => {:document=> document}.merge(options))}")
75
+ end
76
+ end
77
+
78
+ # Save function area for item detail 'show' view, normally
79
+ # renders next to title. By default includes 'Folder' and 'Bookmarks'
80
+ def render_show_doc_actions(document=@document, options={})
81
+ content_tag("div", :class=>"documentFunctions") do
82
+ raw("#{render(:partial => 'bookmark_control', :locals => {:document=> document}.merge(options))}
83
+ #{render(:partial => 'folder_control', :locals => {:document=> document}.merge(options))}")
84
+ end
85
+ end
86
+
87
+ # used in the catalog/_index_partials/_default view
88
+ def index_field_names
89
+ Blacklight.config[:index_fields][:field_names]
90
+ end
91
+
92
+ # used in the _index_partials/_default view
93
+ def index_field_labels
94
+ Blacklight.config[:index_fields][:labels]
95
+ end
96
+
97
+ def spell_check_max
98
+ Blacklight.config[:spell_max] || 0
99
+ end
100
+
101
+ def render_index_field_label args
102
+ field = args[:field]
103
+ html_escape index_field_labels[field]
104
+ end
105
+
106
+ def render_index_field_value args
107
+ value = args[:value]
108
+ value ||= args[:document].get(args[:field], :sep => nil) if args[:document] and args[:field]
109
+ render_field_value value
110
+ end
111
+
112
+ # Used in the show view for displaying the main solr document heading
113
+ def document_heading
114
+ @document[Blacklight.config[:show][:heading]] || @document.id
115
+ end
116
+ def render_document_heading
117
+ content_tag(:h1, document_heading)
118
+ end
119
+
120
+ # Used in the show view for setting the main html document title
121
+ def document_show_html_title
122
+ @document[Blacklight.config[:show][:html_title]]
123
+ end
124
+
125
+ # Used in citation view for displaying the title
126
+ def citation_title(document)
127
+ document[Blacklight.config[:show][:html_title]]
128
+ end
129
+
130
+ # Used in the document_list partial (search view) for building a select element
131
+ def sort_fields
132
+ Blacklight.config[:sort_fields]
133
+ end
134
+
135
+ # Used in the document list partial (search view) for creating a link to the document show action
136
+ def document_show_link_field
137
+ Blacklight.config[:index][:show_link].to_sym
138
+ end
139
+
140
+ # Used in the search form partial for building a select tag
141
+ def search_fields
142
+ Blacklight.search_field_options_for_select
143
+ end
144
+
145
+ # used in the catalog/_show/_default partial
146
+ def document_show_fields
147
+ Blacklight.config[:show_fields][:field_names]
148
+ end
149
+
150
+ # used in the catalog/_show/_default partial
151
+ def document_show_field_labels
152
+ Blacklight.config[:show_fields][:labels]
153
+ end
154
+
155
+ def render_document_show_field_label args
156
+ field = args[:field]
157
+ html_escape document_show_field_labels[field]
158
+ end
159
+
160
+ def render_document_show_field_value args
161
+ value = args[:value]
162
+ value ||= args[:document].get(args[:field], :sep => nil) if args[:document] and args[:field]
163
+ render_field_value value
164
+ end
165
+
166
+ def render_field_value value=nil
167
+ value = [value] unless value.is_a? Array
168
+ value = value.collect { |x| x.respond_to?(:force_encoding) ? x.force_encoding("UTF-8") : x}
169
+ return value.map { |v| html_escape v }.join(field_value_separator).html_safe
170
+ end
171
+
172
+ def field_value_separator
173
+ ', '
174
+ end
175
+
176
+ # Return a normalized partial name that can be used to contruct view partial path
177
+ def document_partial_name(document)
178
+ # .to_s is necessary otherwise the default return value is not always a string
179
+ # using "_" as sep. to more closely follow the views file naming conventions
180
+ # parameterize uses "-" as the default sep. which throws errors
181
+ display_type = document[Blacklight.config[:show][:display_type]]
182
+
183
+ return 'default' unless display_type
184
+ display_type = display_type.join(" ") if display_type.respond_to?(:join)
185
+
186
+ "#{display_type.gsub("-"," ")}".parameterize("_").to_s
187
+ end
188
+
189
+ # given a doc and action_name, this method attempts to render a partial template
190
+ # based on the value of doc[:format]
191
+ # if this value is blank (nil/empty) the "default" is used
192
+ # if the partial is not found, the "default" partial is rendered instead
193
+ def render_document_partial(doc, action_name)
194
+ format = document_partial_name(doc)
195
+
196
+ document_partial_path_templates.each do |str|
197
+ # XXX rather than handling this logic through exceptions, maybe there's a Rails internals method
198
+ # for determining if a partial template exists..
199
+ begin
200
+ return render :partial => (str % [action_name, format]), :locals=>{:document=>doc}
201
+ rescue ActionView::MissingTemplate
202
+ nil
203
+ end
204
+ end
205
+
206
+ return ''
207
+ end
208
+
209
+ # a list of document partial templates to try to render for #render_document_partial
210
+ #
211
+ # (NOTE: I suspect #document_partial_name, #render_document_partial and #document_partial_path_templates
212
+ # should be more succinctly refactored in the future)
213
+ def document_partial_path_templates
214
+ # first, the legacy template names for backwards compatbility
215
+ # followed by the new, inheritable style
216
+ # finally, a controller-specific path for non-catalog subclasses
217
+ @partial_path_templates ||= ["catalog/_%s_partials/%s", "catalog/_%s_partials/default", "%s_%s", "%s_default", "catalog/%s_%s", "catalog/%s_default"]
218
+ end
219
+
220
+
221
+ # Search History and Saved Searches display
222
+ def link_to_previous_search(params)
223
+ link_to(raw(render_search_to_s(params)), catalog_index_path(params)).html_safe
224
+ end
225
+
226
+ #
227
+ # shortcut for built-in Rails helper, "number_with_delimiter"
228
+ #
229
+ def format_num(num); number_with_delimiter(num) end
230
+
231
+ #
232
+ # link based helpers ->
233
+ #
234
+
235
+ # create link to query (e.g. spelling suggestion)
236
+ def link_to_query(query)
237
+ p = params.dup
238
+ p.delete :page
239
+ p.delete :action
240
+ p[:q]=query
241
+ link_url = catalog_index_path(p)
242
+ link_to(query, link_url)
243
+ end
244
+
245
+ def render_document_index_label doc, opts
246
+ label = nil
247
+ label ||= doc.get(opts[:label]) if opts[:label].instance_of? Symbol
248
+ label ||= opts[:label].call(doc, opts) if opts[:label].instance_of? Proc
249
+ label ||= opts[:label] if opts[:label].is_a? String
250
+ label ||= doc.id
251
+ end
252
+
253
+ # link_to_document(doc, :label=>'VIEW', :counter => 3)
254
+ # Use the catalog_path RESTful route to create a link to the show page for a specific item.
255
+ # catalog_path accepts a HashWithIndifferentAccess object. The solr query params are stored in the session,
256
+ # so we only need the +counter+ param here. We also need to know if we are viewing to document as part of search results.
257
+ def link_to_document(doc, opts={:label=>Blacklight.config[:index][:show_link].to_sym, :counter => nil, :results_view => true})
258
+ label = render_document_index_label doc, opts
259
+ link_to_with_data(label, catalog_path(doc.id), {:method => :put, :class => label.parameterize, :data => opts}).html_safe
260
+ end
261
+
262
+ # link_back_to_catalog(:label=>'Back to Search')
263
+ # Create a link back to the index screen, keeping the user's facet, query and paging choices intact by using session.
264
+ def link_back_to_catalog(opts={:label=>'Back to Search'})
265
+ query_params = session[:search] ? session[:search].dup : {}
266
+ query_params.delete :counter
267
+ query_params.delete :total
268
+ link_url = catalog_index_path + "?" + query_params.to_query
269
+ link_to opts[:label], link_url
270
+ end
271
+
272
+ # Create form input type=hidden fields representing the entire search context,
273
+ # for inclusion in a form meant to change some aspect of it, like
274
+ # re-sort or change records per page. Can pass in params hash
275
+ # as :params => hash, otherwise defaults to #params. Can pass
276
+ # in certain top-level params keys to _omit_, defaults to :page
277
+ def search_as_hidden_fields(options={})
278
+
279
+ options = {:params => params, :omit_keys => [:page]}.merge(options)
280
+ my_params = options[:params].dup
281
+ options[:omit_keys].each {|omit_key| my_params.delete(omit_key)}
282
+ # removing action and controller from duplicate params so that we don't get hidden fields for them.
283
+ my_params.delete(:action)
284
+ my_params.delete(:controller)
285
+ # commit is just an artifact of submit button, we don't need it, and
286
+ # don't want it to pile up with another every time we press submit again!
287
+ my_params.delete(:commit)
288
+ # hash_as_hidden_fields in hash_as_hidden_fields.rb
289
+ return hash_as_hidden_fields(my_params)
290
+ end
291
+
292
+
293
+
294
+ def link_to_previous_document(previous_document)
295
+ return if previous_document == nil
296
+ link_to_document previous_document, :label=>'« Previous', :counter => session[:search][:counter].to_i - 1
297
+ end
298
+
299
+ def link_to_next_document(next_document)
300
+ return if next_document == nil
301
+ link_to_document next_document, :label=>'Next »', :counter => session[:search][:counter].to_i + 1
302
+ end
303
+
304
+ # Use case, you want to render an html partial from an XML (say, atom)
305
+ # template. Rails API kind of lets us down, we need to hack Rails internals
306
+ # a bit. code taken from:
307
+ # http://stackoverflow.com/questions/339130/how-do-i-render-a-partial-of-a-different-format-in-rails
308
+ def with_format(format, &block)
309
+ old_format = @template_format
310
+ @template_format = format
311
+ result = block.call
312
+ @template_format = old_format
313
+ return result
314
+ end
315
+
316
+
317
+ # This is an updated +link_to+ that allows you to pass a +data+ hash along with the +html_options+
318
+ # which are then written to the generated form for non-GET requests. The key is the form element name
319
+ # and the value is the value:
320
+ #
321
+ # link_to_with_data('Name', some_path(some_id), :method => :post, :html)
322
+ def link_to_with_data(*args, &block)
323
+ if block_given?
324
+ options = args.first || {}
325
+ html_options = args.second
326
+ concat(link_to(capture(&block), options, html_options))
327
+ else
328
+ name = args.first
329
+ options = args.second || {}
330
+ html_options = args.third
331
+
332
+ url = url_for(options)
333
+
334
+ if html_options
335
+ html_options = html_options.stringify_keys
336
+ href = html_options['href']
337
+ convert_options_to_javascript_with_data!(html_options, url)
338
+ tag_options = tag_options(html_options)
339
+ else
340
+ tag_options = nil
341
+ end
342
+
343
+ href_attr = "href=\"#{url}\"" unless href
344
+ "<a #{href_attr}#{tag_options}>#{h(name) || h(url)}</a>".html_safe
345
+ end
346
+ end
347
+
348
+ # This is derived from +convert_options_to_javascript+ from module Blacklight::+UrlHelperBehavior+ in +url_helper.rb+
349
+ def convert_options_to_javascript_with_data!(html_options, url = '')
350
+ confirm, popup = html_options.delete("confirm"), html_options.delete("popup")
351
+
352
+ method, href = html_options.delete("method"), html_options['href']
353
+ data = html_options.delete("data")
354
+ data = data.stringify_keys if data
355
+
356
+ html_options["onclick"] = case
357
+ when method
358
+ "#{method_javascript_function_with_data(method, url, href, data)}return false;"
359
+ else
360
+ html_options["onclick"]
361
+ end
362
+ end
363
+
364
+ # This is derived from +method_javascript_function+ from module Blacklight::+UrlHelperBehavior+ in +url_helper.rb+
365
+ def method_javascript_function_with_data(method, url = '', href = nil, data=nil)
366
+ action = (href && url.size > 0) ? "'#{url}'" : 'this.href'
367
+ submit_function =
368
+ "var f = document.createElement('form'); f.style.display = 'none'; " +
369
+ "this.parentNode.appendChild(f); f.method = 'POST'; f.action = #{action};"+
370
+ "if(event.metaKey || event.ctrlKey){f.target = '_blank';};" # if the command or control key is being held down while the link is clicked set the form's target to _blank
371
+ if data
372
+ data.each_pair do |key, value|
373
+ submit_function << "var d = document.createElement('input'); d.setAttribute('type', 'hidden'); "
374
+ submit_function << "d.setAttribute('name', '#{key}'); d.setAttribute('value', '#{escape_javascript(value.to_s)}'); f.appendChild(d);"
375
+ end
376
+ end
377
+ unless method == :post
378
+ submit_function << "var m = document.createElement('input'); m.setAttribute('type', 'hidden'); "
379
+ submit_function << "m.setAttribute('name', '_method'); m.setAttribute('value', '#{method}'); f.appendChild(m);"
380
+ end
381
+
382
+ if protect_against_forgery?
383
+ submit_function << "var s = document.createElement('input'); s.setAttribute('type', 'hidden'); "
384
+ submit_function << "s.setAttribute('name', '#{request_forgery_protection_token}'); s.setAttribute('value', '#{escape_javascript form_authenticity_token}'); f.appendChild(s);"
385
+ end
386
+ submit_function << "f.submit();"
387
+ end
388
+
389
+ # determines if the given document id is in the folder
390
+ def item_in_folder?(doc_id)
391
+ session[:folder_document_ids] && session[:folder_document_ids].include?(doc_id) ? true : false
392
+ end
393
+
394
+ # puts together a collection of documents into one refworks export string
395
+ def render_refworks_texts(documents)
396
+ val = ''
397
+ documents.each do |doc|
398
+ if doc.respond_to?(:to_marc)
399
+ val += doc.export_as_refworks_marc_txt + "\n"
400
+ end
401
+ end
402
+ val
403
+ end
404
+
405
+ # puts together a collection of documents into one endnote export string
406
+ def render_endnote_texts(documents)
407
+ val = ''
408
+ documents.each do |doc|
409
+ if doc.respond_to?(:to_marc)
410
+ val += doc.export_as_endnote + "\n"
411
+ end
412
+ end
413
+ val
414
+ end
415
+
416
+ end
@@ -0,0 +1,87 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Blacklight::CatalogHelperBehavior
3
+
4
+ # Pass in an RSolr::Response (or duck-typed similar) object,
5
+ # it translates to a Kaminari-paginatable
6
+ # object, with the keys Kaminari views expect.
7
+ def paginate_params(response)
8
+ per_page = response.rows
9
+ per_page = 1 if per_page < 1
10
+ current_page = (response.start / per_page).ceil + 1
11
+ num_pages = (response.total / per_page.to_f).ceil
12
+ Struct.new(:current_page, :num_pages, :limit_value).new(current_page, num_pages, per_page)
13
+ end
14
+
15
+ # Equivalent to kaminari "paginate", but takes an RSolr::Response as first argument.
16
+ # Will convert it to something kaminari can deal with (using #paginate_params), and
17
+ # then call kaminari paginate with that. Other arguments (options and block) same as
18
+ # kaminari paginate, passed on through.
19
+ # will output HTML pagination controls.
20
+ def paginate_rsolr_response(response, options = {}, &block)
21
+ paginate paginate_params(response), options, &block
22
+ end
23
+
24
+ #
25
+ # shortcut for built-in Rails helper, "number_with_delimiter"
26
+ #
27
+ def format_num(num); number_with_delimiter(num) end
28
+
29
+ #
30
+ # Pass in an RSolr::Response. Displays the "showing X through Y of N" message.
31
+ def render_pagination_info(response, options = {})
32
+ start = response.start + 1
33
+ per_page = response.rows
34
+ current_page = (response.start / per_page).ceil + 1
35
+ num_pages = (response.total / per_page.to_f).ceil
36
+ total_hits = response.total
37
+
38
+ start_num = format_num(start)
39
+ end_num = format_num(start + response.docs.length - 1)
40
+ total_num = format_num(total_hits)
41
+
42
+ entry_name = options[:entry_name] ||
43
+ (response.empty?? 'entry' : response.docs.first.class.name.underscore.sub('_', ' '))
44
+
45
+ if num_pages < 2
46
+ case response.docs.length
47
+ when 0; "No #{h(entry_name.pluralize)} found".html_safe
48
+ when 1; "Displaying <b>1</b> #{h(entry_name)}".html_safe
49
+ else; "Displaying <b>all #{total_num}</b> #{entry_name.pluralize}".html_safe
50
+ end
51
+ else
52
+ "Displaying #{h(entry_name.pluralize)} <b>#{start_num} - #{end_num}</b> of <b>#{total_num}</b>".html_safe
53
+ end
54
+ end
55
+
56
+ # Like #render_pagination_info above, but for an individual
57
+ # item show page. Displays "showing X of Y items" message. Actually takes
58
+ # data from session though (not a great design).
59
+ # Code should call this method rather than interrogating session directly,
60
+ # because implementation of where this data is stored/retrieved may change.
61
+ def item_page_entry_info
62
+ "Showing item <b>#{session[:search][:counter].to_i} of #{format_num(session[:search][:total])}</b> from your search.".html_safe
63
+ end
64
+
65
+ # Look up search field user-displayable label
66
+ # based on params[:qt] and configuration.
67
+ def search_field_label(params)
68
+ h( Blacklight.label_for_search_field(params[:search_field]) )
69
+ end
70
+
71
+ # Export to Refworks URL, called in _show_tools
72
+ def refworks_export_url(document = @document)
73
+ "http://www.refworks.com/express/expressimport.asp?vendor=#{CGI.escape(application_name)}&filter=MARC%20Format&encoding=65001&url=#{CGI.escape(catalog_path(document.id, :format => 'refworks_marc_txt', :only_path => false))}"
74
+ end
75
+
76
+ def render_document_class(document = @document)
77
+ 'blacklight-' + document.get(Blacklight.config[:index][:record_display_type]).parameterize rescue nil
78
+ end
79
+
80
+ def render_document_sidebar_partial(document = @document)
81
+ render :partial => 'show_sidebar'
82
+ end
83
+
84
+ def has_search_parameters?
85
+ !params[:q].blank? or !params[:f].blank? or !params[:search_field].blank?
86
+ end
87
+ end