blacklight 3.1.0 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
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