blacklight 6.2.0 → 6.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/.solr_wrapper +5 -0
  4. data/.travis.yml +3 -5
  5. data/Gemfile +3 -3
  6. data/VERSION +1 -1
  7. data/app/assets/javascripts/blacklight/core.js +19 -5
  8. data/app/helpers/blacklight/blacklight_helper_behavior.rb +48 -20
  9. data/app/helpers/blacklight/configuration_helper_behavior.rb +1 -1
  10. data/app/helpers/blacklight/url_helper_behavior.rb +2 -1
  11. data/app/presenters/blacklight/document_presenter.rb +58 -150
  12. data/app/presenters/blacklight/field_presenter.rb +31 -0
  13. data/app/presenters/blacklight/index_presenter.rb +69 -0
  14. data/app/presenters/blacklight/link_alternate_presenter.rb +29 -0
  15. data/app/presenters/blacklight/rendering/abstract_step.rb +24 -0
  16. data/app/presenters/blacklight/rendering/helper_method.rb +23 -0
  17. data/app/presenters/blacklight/rendering/join.rb +16 -0
  18. data/app/presenters/blacklight/rendering/link_to_facet.rb +35 -0
  19. data/app/presenters/blacklight/rendering/microdata.rb +17 -0
  20. data/app/presenters/blacklight/rendering/pipeline.rb +32 -0
  21. data/app/presenters/blacklight/rendering/terminator.rb +9 -0
  22. data/app/presenters/blacklight/show_presenter.rb +93 -0
  23. data/app/services/blacklight/field_retriever.rb +58 -0
  24. data/app/views/catalog/_document_default.atom.builder +2 -3
  25. data/app/views/catalog/_document_default.rss.builder +2 -2
  26. data/app/views/kaminari/blacklight/_next_page.html.erb +9 -3
  27. data/app/views/kaminari/blacklight/_prev_page.html.erb +10 -3
  28. data/lib/blacklight/configuration.rb +15 -3
  29. data/lib/blacklight/configuration/null_field.rb +13 -0
  30. data/lib/blacklight/configuration/view_config.rb +6 -0
  31. data/lib/generators/blacklight/templates/catalog_controller.rb +1 -1
  32. data/lib/generators/blacklight/templates/config/blacklight.yml +1 -1
  33. data/lib/generators/blacklight/templates/config/jetty.yml +1 -1
  34. data/spec/controllers/alternate_controller_spec.rb +2 -2
  35. data/spec/controllers/application_controller_spec.rb +1 -1
  36. data/spec/controllers/blacklight/facet_spec.rb +3 -3
  37. data/spec/controllers/blacklight/search_fields_spec.rb +7 -7
  38. data/spec/controllers/blacklight/search_helper_spec.rb +44 -44
  39. data/spec/controllers/blacklight/suggest_search_spec.rb +1 -1
  40. data/spec/controllers/bookmarks_controller_spec.rb +6 -6
  41. data/spec/controllers/catalog_controller_spec.rb +125 -125
  42. data/spec/controllers/saved_searches_controller_spec.rb +4 -9
  43. data/spec/controllers/search_history_controller_spec.rb +3 -6
  44. data/spec/controllers/suggest_controller_spec.rb +2 -2
  45. data/spec/features/alternate_controller_spec.rb +3 -3
  46. data/spec/features/bookmarks_spec.rb +6 -6
  47. data/spec/features/did_you_mean_spec.rb +10 -10
  48. data/spec/features/facets_spec.rb +4 -4
  49. data/spec/features/record_view_spec.rb +4 -4
  50. data/spec/features/saved_searches_spec.rb +4 -4
  51. data/spec/features/search_context_spec.rb +4 -4
  52. data/spec/features/search_filters_spec.rb +10 -10
  53. data/spec/features/search_formats_spec.rb +2 -2
  54. data/spec/features/search_history_spec.rb +5 -5
  55. data/spec/features/search_pagination_spec.rb +4 -4
  56. data/spec/features/search_results_spec.rb +7 -7
  57. data/spec/features/search_sort_spec.rb +2 -2
  58. data/spec/features/search_spec.rb +6 -6
  59. data/spec/helpers/blacklight_helper_spec.rb +105 -65
  60. data/spec/helpers/catalog_helper_spec.rb +36 -36
  61. data/spec/helpers/configuration_helper_spec.rb +28 -28
  62. data/spec/helpers/facets_helper_spec.rb +39 -39
  63. data/spec/helpers/hash_as_hidden_fields_spec.rb +1 -1
  64. data/spec/helpers/render_constraints_helper_spec.rb +1 -1
  65. data/spec/helpers/search_history_constraints_helper_spec.rb +7 -7
  66. data/spec/helpers/url_helper_spec.rb +20 -17
  67. data/spec/lib/blacklight/search_state_spec.rb +2 -2
  68. data/spec/lib/blacklight/utils_spec.rb +15 -15
  69. data/spec/lib/blacklight_spec.rb +1 -1
  70. data/spec/lib/tasks/blacklight_task_spec.rb +1 -1
  71. data/spec/models/blacklight/configurable_spec.rb +3 -3
  72. data/spec/models/blacklight/configuration_spec.rb +52 -52
  73. data/spec/models/blacklight/document_spec.rb +10 -10
  74. data/spec/models/blacklight/facet_paginator_spec.rb +5 -5
  75. data/spec/models/blacklight/search_builder_spec.rb +34 -34
  76. data/spec/models/blacklight/user_spec.rb +4 -4
  77. data/spec/models/bookmark_spec.rb +5 -5
  78. data/spec/models/record_mailer_spec.rb +11 -11
  79. data/spec/models/search_spec.rb +1 -1
  80. data/spec/models/solr_document_spec.rb +4 -4
  81. data/spec/presenters/document_presenter_spec.rb +94 -50
  82. data/spec/presenters/index_presenter_spec.rb +147 -0
  83. data/spec/presenters/pipeline_spec.rb +28 -0
  84. data/spec/presenters/show_presenter_spec.rb +287 -0
  85. data/spec/routing/catalog_routing_spec.rb +11 -11
  86. data/spec/spec_helper.rb +10 -1
  87. data/spec/support/backport_test.rb +38 -0
  88. data/spec/test_app_templates/Gemfile.extra +2 -10
  89. data/spec/views/_user_util_links.html.erb_spec.rb +1 -1
  90. data/spec/views/catalog/_constraints.html.erb_spec.rb +3 -3
  91. data/spec/views/catalog/_constraints_element.html.erb_spec.rb +5 -5
  92. data/spec/views/catalog/_document.html.erb_spec.rb +2 -2
  93. data/spec/views/catalog/_document_list.html.erb_spec.rb +1 -1
  94. data/spec/views/catalog/_facet_layout.html.erb_spec.rb +4 -4
  95. data/spec/views/catalog/_facets.html.erb_spec.rb +4 -4
  96. data/spec/views/catalog/_index_default.erb_spec.rb +5 -4
  97. data/spec/views/catalog/_index_header_default.html.erb_spec.rb +4 -3
  98. data/spec/views/catalog/_paginate_compact.html.erb_spec.rb +3 -3
  99. data/spec/views/catalog/_search_header.erb_spec.rb +1 -1
  100. data/spec/views/catalog/_show_default.erb_spec.rb +6 -5
  101. data/spec/views/catalog/_show_sidebar.erb_spec.rb +2 -1
  102. data/spec/views/catalog/_show_tools.html.erb_spec.rb +8 -8
  103. data/spec/views/catalog/_sort_and_per_page.html.erb_spec.rb +2 -2
  104. data/spec/views/catalog/_thumbnail_default.erb_spec.rb +3 -2
  105. data/spec/views/catalog/_view_type_group.html.erb_spec.rb +3 -3
  106. data/spec/views/catalog/facet.html.erb_spec.rb +3 -3
  107. data/spec/views/catalog/index.atom.builder_spec.rb +15 -14
  108. data/spec/views/catalog/index.html.erb_spec.rb +3 -3
  109. data/spec/views/catalog/opensearch.xml.builder_spec.rb +1 -1
  110. data/spec/views/catalog/show.html.erb_spec.rb +5 -4
  111. data/tasks/blacklight.rake +1 -1
  112. metadata +25 -4
  113. data/config/jetty.yml +0 -13
@@ -0,0 +1,31 @@
1
+ module Blacklight
2
+ # Renders a field and handles link_to_search or helper_method if supplied
3
+ class FieldPresenter
4
+ def initialize(controller, document, field_config, options)
5
+ @controller = controller
6
+ @document = document
7
+ @field_config = field_config
8
+ @options = options
9
+ end
10
+
11
+ attr_reader :controller, :document, :field_config, :options
12
+
13
+ def render
14
+ if options[:value]
15
+ # This prevents helper methods from drawing.
16
+ config = Configuration::NullField.new(field_config.to_h.except(:helper_method))
17
+ values = Array.wrap(options[:value])
18
+ else
19
+ config = field_config
20
+ values = retrieve_values
21
+ end
22
+ Rendering::Pipeline.render(values, config, document, controller, options)
23
+ end
24
+
25
+ private
26
+
27
+ def retrieve_values
28
+ FieldRetriever.new(document, field_config).fetch
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+ module Blacklight
3
+ class IndexPresenter
4
+ # @param [SolrDocument] document
5
+ # @param [ActionController::Base] controller scope for linking and generating urls
6
+ # @param [Blacklight::Configuration] configuration
7
+ def initialize(document, controller, configuration = controller.blacklight_config)
8
+ @document = document
9
+ @configuration = configuration
10
+ @controller = controller
11
+ end
12
+
13
+ ##
14
+ # Render the document index heading
15
+ #
16
+ # @param [Symbol, Proc, String] field Render the given field or evaluate the proc or render the given string
17
+ # @param [Hash] opts
18
+ # TODO: the default field should be `document_show_link_field(doc)'
19
+ def label(field_or_string_or_proc, opts = {})
20
+ config = Configuration::NullField.new
21
+ value = case field_or_string_or_proc
22
+ when Symbol
23
+ config = field_config(field_or_string_or_proc)
24
+ @document[field_or_string_or_proc]
25
+ when Proc
26
+ field_or_string_or_proc.call(@document, opts)
27
+ when String
28
+ field_or_string_or_proc
29
+ end
30
+
31
+ value ||= @document.id
32
+ field_values(config, value: value)
33
+ end
34
+
35
+ ##
36
+ # Render the index field label for a document
37
+ #
38
+ # Allow an extention point where information in the document
39
+ # may drive the value of the field
40
+ # @param [String] field
41
+ # @param [Hash] opts
42
+ # @options opts [String] :value
43
+ def field_value field, options = {}
44
+ field_config = field_config(field)
45
+ field_values(field_config, options)
46
+ end
47
+
48
+ private
49
+
50
+ ##
51
+ # Get the value for a document's field, and prepare to render it.
52
+ # - highlight_field
53
+ # - accessor
54
+ # - solr field
55
+ #
56
+ # Rendering:
57
+ # - helper_method
58
+ # - link_to_search
59
+ # @param [Blacklight::Configuration::Field] solr field configuration
60
+ # @param [Hash] options additional options to pass to the rendering helpers
61
+ def field_values(field_config, options={})
62
+ FieldPresenter.new(@controller, @document, field_config, options).render
63
+ end
64
+
65
+ def field_config(field)
66
+ @configuration.index_fields.fetch(field) { Configuration::NullField.new(field) }
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,29 @@
1
+ module Blacklight
2
+ # Create <link rel="alternate"> links from a documents dynamically
3
+ class LinkAlternatePresenter
4
+ include ActionView::Helpers::OutputSafetyHelper
5
+ include ActionView::Helpers::TagHelper
6
+
7
+ def initialize(controller, document, options)
8
+ @controller = controller
9
+ @document = document
10
+ @options = { unique: false, exclude: [] }.merge(options)
11
+ end
12
+
13
+ attr_reader :controller, :document, :options
14
+
15
+ # Renders links to alternate representations
16
+ # provided by export formats. Returns empty string if no links available.
17
+ def render
18
+ seen = Set.new
19
+
20
+ safe_join(document.export_formats.map do |format, spec|
21
+ next if options[:exclude].include?(format) || (options[:unique] && seen.include?(spec[:content_type]))
22
+
23
+ seen.add(spec[:content_type])
24
+
25
+ tag(:link, rel: "alternate", title: format, type: spec[:content_type], href: controller.polymorphic_url(document, format: format))
26
+ end.compact, "\n")
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ module Blacklight
2
+ module Rendering
3
+ class AbstractStep
4
+ def initialize(values, config, document, context, options, stack)
5
+ @values = values
6
+ @config = config
7
+ @document = document
8
+ @context = context
9
+ @options = options
10
+ @stack = stack
11
+ end
12
+
13
+ attr_reader :values, :config, :document, :context, :options, :stack
14
+
15
+ protected
16
+
17
+ def next_step(output_values)
18
+ first, *rest = *stack
19
+ first.new(output_values, config, document, context, options, rest).render
20
+ end
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,23 @@
1
+ module Blacklight
2
+ module Rendering
3
+ class HelperMethod < AbstractStep
4
+ def render
5
+ return next_step(values) unless config.helper_method
6
+ return render_helper # short circut the rest of the steps
7
+ end
8
+
9
+ private
10
+
11
+ def render_helper
12
+ context.send(config.helper_method,
13
+ options.merge(document: document,
14
+ field: config.field,
15
+ config: config,
16
+ value: values))
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+
23
+
@@ -0,0 +1,16 @@
1
+ module Blacklight
2
+ module Rendering
3
+ class Join < AbstractStep
4
+ def render
5
+ options = config.separator_options || {}
6
+ next_step(values.map { |x| html_escape(x) }.to_sentence(options).html_safe)
7
+ end
8
+
9
+ private
10
+
11
+ def html_escape(*args)
12
+ ERB::Util.html_escape(*args)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ module Blacklight
2
+ module Rendering
3
+ class LinkToFacet < AbstractStep
4
+ def render
5
+ # TODO: We should rename the config variable, because it creates a link to a facet.
6
+ return next_step(values) unless config.link_to_search
7
+ next_step(render_link)
8
+ end
9
+
10
+ private
11
+
12
+ # This allows the link to wrap an itemprop
13
+ def render_link
14
+ values.map { |v| link(link_field, v) }
15
+ end
16
+
17
+ def link_field
18
+ return config.key if config.link_to_search === true
19
+ config.link_to_search
20
+ end
21
+
22
+ def link(field, v)
23
+ context.link_to v, search_path(field, v)
24
+ end
25
+
26
+ def search_path(field, v)
27
+ context.search_action_path(facet_params(field, v))
28
+ end
29
+
30
+ def facet_params(field, v)
31
+ context.search_state.reset.add_facet_params(field, v)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,17 @@
1
+ module Blacklight
2
+ module Rendering
3
+ class Microdata < AbstractStep
4
+ include ActionView::Helpers::TagHelper
5
+ def render
6
+ return next_step(values) unless config.itemprop
7
+ next_step(values.map { |x| itemprop(x, config.itemprop) })
8
+ end
9
+
10
+ private
11
+
12
+ def itemprop(val, itemprop)
13
+ content_tag :span, val, itemprop: itemprop
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ module Blacklight
2
+ module Rendering
3
+ # The field rendering pipeline
4
+ class Pipeline
5
+ def initialize(values, config, document, context, options)
6
+ @values = values
7
+ @config = config
8
+ @document = document
9
+ @context = context
10
+ @options = options
11
+ end
12
+
13
+ attr_reader :values, :config, :document, :context, :options
14
+
15
+ def self.render(values, config, document, context, options)
16
+ new(values, config, document, context, options).render
17
+ end
18
+
19
+ def render
20
+ first, *rest = *stack
21
+ first.new(values, config, document, context, options, rest).render
22
+ end
23
+
24
+ protected
25
+
26
+ # Ordered list of operations, Terminator must be at the end.
27
+ def stack
28
+ [HelperMethod, LinkToFacet, Microdata, Join, Terminator]
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,9 @@
1
+ module Blacklight
2
+ module Rendering
3
+ class Terminator < AbstractStep
4
+ def render
5
+ values
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+ module Blacklight
3
+ class ShowPresenter
4
+ # @param [SolrDocument] document
5
+ # @param [ActionController::Base] controller scope for linking and generating urls
6
+ # @param [Blacklight::Configuration] configuration
7
+ def initialize(document, controller, configuration = controller.blacklight_config)
8
+ @document = document
9
+ @configuration = configuration
10
+ @controller = controller
11
+ end
12
+
13
+ ##
14
+ # Create <link rel="alternate"> links from a documents dynamically
15
+ # provided export formats. Returns empty string if no links available.
16
+ #
17
+ # @params [Hash] options
18
+ # @option options [Boolean] :unique ensures only one link is output for every
19
+ # content type, e.g. as required by atom
20
+ # @option options [Array<String>] :exclude array of format shortnames to not include in the output
21
+ # @deprecated moved to ShowPresenter#link_rel_alternates
22
+ def link_rel_alternates(options = {})
23
+ LinkAlternatePresenter.new(@controller, @document, options).render
24
+ end
25
+
26
+ ##
27
+ # Get the document's "title" to display in the <title> element.
28
+ # (by default, use the #document_heading)
29
+ #
30
+ # @see #document_heading
31
+ # @return [String]
32
+ def html_title
33
+ if view_config.html_title_field
34
+ fields = Array.wrap(view_config.html_title_field)
35
+ f = fields.detect { |field| @document.has? field }
36
+ f ||= 'id'
37
+ field_values(field_config(f))
38
+ else
39
+ heading
40
+ end
41
+ end
42
+
43
+ ##
44
+ # Get the value of the document's "title" field, or a placeholder
45
+ # value (if empty)
46
+ #
47
+ # @param [SolrDocument] document
48
+ # @return [String]
49
+ def heading
50
+ fields = Array.wrap(view_config.title_field)
51
+ f = fields.detect { |field| @document.has? field }
52
+ f ||= @configuration.document_model.unique_key
53
+ field_values(field_config(f), value: @document[f])
54
+ end
55
+
56
+ ##
57
+ # Render the show field value for a document
58
+ #
59
+ # Allow an extention point where information in the document
60
+ # may drive the value of the field
61
+ # @param [String] field
62
+ # @param [Hash] options
63
+ # @options opts [String] :value
64
+ def field_value field, options={}
65
+ field_values(field_config(field), options)
66
+ end
67
+
68
+ private
69
+
70
+ ##
71
+ # Get the value for a document's field, and prepare to render it.
72
+ # - highlight_field
73
+ # - accessor
74
+ # - solr field
75
+ #
76
+ # Rendering:
77
+ # - helper_method
78
+ # - link_to_search
79
+ # @param [Blacklight::Configuration::Field] solr field configuration
80
+ # @param [Hash] options additional options to pass to the rendering helpers
81
+ def field_values(field_config, options={})
82
+ FieldPresenter.new(@controller, @document, field_config, options).render
83
+ end
84
+
85
+ def view_config
86
+ @configuration.view_config(:show)
87
+ end
88
+
89
+ def field_config(field)
90
+ @configuration.show_fields.fetch(field) { Configuration::NullField.new(field) }
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,58 @@
1
+ module Blacklight
2
+ class FieldRetriever
3
+ # @param [SolrDocument] document
4
+ # @param [Blacklight::Configuration::Field] field_config solr field configuration
5
+ def initialize(document, field_config)
6
+ @document = document
7
+ @field_config = field_config
8
+ end
9
+
10
+ attr_reader :document, :field_config
11
+ delegate :field, to: :field_config
12
+
13
+ # @return [Array]
14
+ def fetch
15
+ Array.wrap(
16
+ case
17
+ when field_config.highlight
18
+ retrieve_highlight
19
+ when field_config.accessor
20
+ retieve_using_accessor
21
+ when field_config
22
+ retrieve_simple
23
+ end
24
+ )
25
+ end
26
+
27
+ private
28
+
29
+ def retrieve_simple
30
+ # regular document field
31
+ if field_config.default and field_config.default.is_a? Proc
32
+ document.fetch(field_config.field, &field_config.default)
33
+ else
34
+ document.fetch(field_config.field, field_config.default)
35
+ end
36
+ end
37
+
38
+ def retieve_using_accessor
39
+ # implicit method call
40
+ if field_config.accessor === true
41
+ document.send(field)
42
+ # arity-1 method call (include the field name in the call)
43
+ elsif !field_config.accessor.is_a?(Array) && document.method(field_config.accessor).arity != 0
44
+ document.send(field_config.accessor, field)
45
+ # chained method calls
46
+ else
47
+ Array(field_config.accessor).inject(document) do |result, method|
48
+ result.send(method)
49
+ end
50
+ end
51
+ end
52
+
53
+ def retrieve_highlight
54
+ # retrieve the document value from the highlighting response
55
+ document.highlight_field(field_config.field).map(&:html_safe) if document.has_highlight_field? field_config.field
56
+ end
57
+ end
58
+ end