blacklight 7.20.1 → 7.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.babelrc +11 -0
  3. data/.docker/app/Dockerfile +3 -1
  4. data/.dockerignore +3 -0
  5. data/.env +2 -2
  6. data/.github/workflows/ruby.yml +3 -2
  7. data/Gemfile +1 -1
  8. data/README.md +1 -1
  9. data/VERSION +1 -1
  10. data/app/assets/javascripts/blacklight/blacklight.js +62 -32
  11. data/app/assets/stylesheets/blacklight/_constraints.scss +3 -1
  12. data/app/assets/stylesheets/blacklight/_controls.scss +2 -1
  13. data/app/components/blacklight/constraints_component.rb +3 -7
  14. data/app/components/blacklight/facet_field_checkboxes_component.rb +1 -1
  15. data/app/components/blacklight/facet_item_pivot_component.rb +1 -1
  16. data/app/components/blacklight/system/modal_component.html.erb +2 -2
  17. data/app/controllers/concerns/blacklight/catalog.rb +2 -2
  18. data/app/helpers/blacklight/facets_helper_behavior.rb +1 -1
  19. data/app/helpers/blacklight/render_partials_helper_behavior.rb +4 -1
  20. data/app/javascript/blacklight/core.js +8 -2
  21. data/app/javascript/blacklight/modal.js +20 -4
  22. data/app/models/concerns/blacklight/document/email.rb +18 -6
  23. data/app/models/concerns/blacklight/document/sms.rb +16 -4
  24. data/app/models/record_mailer.rb +9 -1
  25. data/app/presenters/blacklight/facet_item_presenter.rb +2 -0
  26. data/app/views/bookmarks/index.html.erb +1 -1
  27. data/app/views/catalog/_search_results.html.erb +1 -1
  28. data/app/views/record_mailer/sms_record.text.erb +1 -1
  29. data/config/locales/blacklight.de.yml +1 -1
  30. data/docker-compose.yml +5 -2
  31. data/lib/blacklight/configuration/facet_field.rb +2 -0
  32. data/lib/blacklight/configuration.rb +9 -0
  33. data/lib/blacklight/engine.rb +2 -0
  34. data/lib/blacklight/search_state/filter_field.rb +34 -19
  35. data/lib/blacklight/solr/response/facets.rb +14 -5
  36. data/lib/blacklight/solr/search_builder_behavior.rb +2 -14
  37. data/package.json +4 -2
  38. data/spec/controllers/catalog_controller_spec.rb +14 -2
  39. data/spec/features/axe_spec.rb +6 -7
  40. data/spec/features/facet_missing_spec.rb +12 -4
  41. data/spec/helpers/blacklight/facets_helper_behavior_spec.rb +7 -7
  42. data/spec/lib/blacklight/search_state/filter_field_spec.rb +3 -17
  43. data/spec/models/blacklight/configuration_spec.rb +92 -0
  44. data/spec/models/blacklight/document/email_spec.rb +32 -0
  45. data/spec/models/blacklight/document/sms_spec.rb +33 -0
  46. data/spec/models/blacklight/solr/response/facets_spec.rb +7 -1
  47. data/spec/models/blacklight/solr/search_builder_spec.rb +0 -13
  48. data/spec/models/record_mailer_spec.rb +30 -2
  49. data/spec/spec_helper.rb +3 -3
  50. data/tasks/blacklight.rake +5 -1
  51. metadata +8 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee4c46b8533bb359a6619ef6dddd96b4de1449b4bfc96db65a357469bebc7cc1
4
- data.tar.gz: 9eecbab00d1484a3e417bd60245479fc58d6bd9c41a7b16be91d3cef056846a2
3
+ metadata.gz: 100a7b0ab6394c96ba5ad5606bffe36132721d9f09c5946911845f117d8bddee
4
+ data.tar.gz: a57654c6cdb05add30658903b407d86661a8b975b0af389f84c56736bff2d56c
5
5
  SHA512:
6
- metadata.gz: 97d968403618ee1d25fac527cea64467e3be78040e0d58e8d672463b94c22f272ff7b30fec66af576084bf2a9e8d169a7d3bec9b58b0d1187bb8ffc5bdab2f21
7
- data.tar.gz: 31855c12d2d58eb7863aa90a4b60c106d208b302460bd0f263fdd32baf8d6025f94025cdf953c82a42220a403c2d0eeae367e7729c7f714f8bc19d23bb561974
6
+ metadata.gz: dec089984409283e902abf6d3a79c549a1785742396dd6d1e6dbd8ec5b49b1ec5ae4be46cad25eb5bdff663880fce761e023c313f71a020f989f17932492adb4
7
+ data.tar.gz: d1bb132cc0ef21f5f9c3a715bf407c47eba801f274d7d78f5e872b6f495298d5bad2720ee89e84944c733babcda22ec62f6d8eed119df9e7ded38b70fd5de1b7
data/.babelrc ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "presets": [
3
+ [
4
+ "@babel/preset-env",
5
+ {
6
+ "useBuiltIns": "entry",
7
+ "corejs": "2.0"
8
+ }
9
+ ]
10
+ ]
11
+ }
@@ -9,8 +9,10 @@ RUN apk add --update --no-cache \
9
9
  libxml2-dev \
10
10
  libxslt-dev \
11
11
  nodejs \
12
+ shared-mime-info \
12
13
  sqlite-dev \
13
- tzdata
14
+ tzdata \
15
+ yarn
14
16
 
15
17
  RUN mkdir /app
16
18
  WORKDIR /app
data/.dockerignore ADDED
@@ -0,0 +1,3 @@
1
+ Gemfile.lock
2
+ .internal_test_app
3
+ .solr_wrapper.yml
data/.env CHANGED
@@ -1,5 +1,5 @@
1
- ALPINE_RUBY_VERSION=2.6.5
2
- RAILS_VERSION=5.2.5
1
+ ALPINE_RUBY_VERSION=2.7.5
2
+ RAILS_VERSION=5.2.6
3
3
  SOLR_PORT=8983
4
4
  SOLR_URL=http://solr:8983/solr/blacklight-core
5
5
  SOLR_VERSION=latest
@@ -9,9 +9,10 @@ name: CI
9
9
 
10
10
  on:
11
11
  push:
12
- branches: [ master ]
12
+ branches:
13
+ - main
14
+ - 'release-*'
13
15
  pull_request:
14
- branches: [ master ]
15
16
 
16
17
  jobs:
17
18
  lint:
data/Gemfile CHANGED
@@ -27,7 +27,7 @@ else
27
27
  if ENV['RAILS_VERSION']
28
28
  if ENV['RAILS_VERSION'] == 'edge'
29
29
  gem 'rails', github: 'rails/rails'
30
- ENV['ENGINE_CART_RAILS_OPTIONS'] = '--edge --skip-turbolinks'
30
+ ENV['ENGINE_CART_RAILS_OPTIONS'] = '--edge'
31
31
  else
32
32
  gem 'rails', ENV['RAILS_VERSION']
33
33
  end
data/README.md CHANGED
@@ -39,7 +39,7 @@ rails generate blacklight:install
39
39
 
40
40
  ## Contributing Code
41
41
 
42
- Code contributions are always welcome, instructions for contributing can be found at [CONTRIBUTING.md](https://github.com/projectblacklight/blacklight/blob/master/CONTRIBUTING.md).
42
+ Code contributions are always welcome, instructions for contributing can be found at [CONTRIBUTING.md](https://github.com/projectblacklight/blacklight/blob/main/CONTRIBUTING.md).
43
43
 
44
44
  ## Configuring Apache Solr
45
45
  You'll also want some information about how Blacklight expects [Apache Solr](http://lucene.apache.org/solr ) to run, which you can find in [README_SOLR](https://github.com/projectblacklight/blacklight/wiki/README_SOLR)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 7.20.1
1
+ 7.22.0
@@ -1,18 +1,22 @@
1
- Blacklight = function () {
1
+ "use strict";
2
+
3
+ var Blacklight = function () {
2
4
  var buffer = new Array();
3
5
  return {
4
- onLoad: function (func) {
6
+ onLoad: function onLoad(func) {
5
7
  buffer.push(func);
6
8
  },
7
- activate: function () {
9
+ activate: function activate() {
8
10
  for (var i = 0; i < buffer.length; i++) {
9
11
  buffer[i].call();
10
12
  }
11
13
  },
12
- listeners: function () {
14
+ listeners: function listeners() {
13
15
  var listeners = [];
14
16
 
15
- if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
17
+ if (typeof Turbo !== 'undefined') {
18
+ listeners.push('turbo:load');
19
+ } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
16
20
  // Turbolinks 5
17
21
  if (Turbolinks.BrowserAdapter) {
18
22
  listeners.push('turbolinks:load');
@@ -37,11 +41,14 @@ Blacklight.listeners().forEach(function (listener) {
37
41
  });
38
42
  });
39
43
  Blacklight.onLoad(function () {
40
- const elem = document.querySelector('.no-js');
44
+ var elem = document.querySelector('.no-js'); // The "no-js" class may already have been removed because this function is
45
+ // run on every turbo:load event, in that case, it won't find an element.
46
+
41
47
  if (!elem) return;
42
48
  elem.classList.remove('no-js');
43
49
  elem.classList.add('js');
44
50
  });
51
+ window.Blacklight = Blacklight;
45
52
  /*global Bloodhound */
46
53
 
47
54
  Blacklight.onLoad(function () {
@@ -87,7 +94,7 @@ Blacklight.onLoad(function () {
87
94
  $(Blacklight.doBookmarkToggleBehavior.selector).blCheckboxSubmit({
88
95
  // cssClass is added to elements added, plus used for id base
89
96
  cssClass: 'toggle-bookmark',
90
- success: function (checked, response) {
97
+ success: function success(checked, response) {
91
98
  if (response.bookmarks) {
92
99
  $('[data-role=bookmark-counter]').text(response.bookmarks.count);
93
100
  }
@@ -105,8 +112,8 @@ Blacklight.onLoad(function () {
105
112
  // Button clicks should change focus. As of 10/3/19, Firefox for Mac and
106
113
  // Safari both do not set focus to a button on button click.
107
114
  // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information
108
- document.querySelectorAll('button.collapse-toggle').forEach(button => {
109
- button.addEventListener('click', () => {
115
+ document.querySelectorAll('button.collapse-toggle').forEach(function (button) {
116
+ button.addEventListener('click', function () {
110
117
  event.target.focus();
111
118
  });
112
119
  });
@@ -193,12 +200,12 @@ Blacklight.onLoad(function () {
193
200
  dataType: 'json',
194
201
  type: form.attr('method').toUpperCase(),
195
202
  data: form.serialize(),
196
- error: function () {
203
+ error: function error() {
197
204
  label.removeAttr('disabled');
198
205
  checkbox.removeAttr('disabled');
199
206
  options.error.call();
200
207
  },
201
- success: function (data, status, xhr) {
208
+ success: function success(data, status, xhr) {
202
209
  //if app isn't running at all, xhr annoyingly
203
210
  //reports success with status 0.
204
211
  if (xhr.status != 0) {
@@ -224,10 +231,10 @@ Blacklight.onLoad(function () {
224
231
  $.fn.blCheckboxSubmit.defaults = {
225
232
  //cssClass is added to elements added, plus used for id base
226
233
  cssClass: 'blCheckboxSubmit',
227
- error: function () {
234
+ error: function error() {
228
235
  alert("Error");
229
236
  },
230
- success: function () {} //callback
237
+ success: function success() {} //callback
231
238
 
232
239
  };
233
240
  })(jQuery);
@@ -243,12 +250,12 @@ Blacklight.doResizeFacetLabelsAndCounts = function () {
243
250
  }
244
251
 
245
252
  document.querySelectorAll('.facet-values, .pivot-facet').forEach(function (elem) {
246
- const nodes = elem.querySelectorAll('.facet-count'); // TODO: when we drop ie11 support, this can become the spread operator:
253
+ var nodes = elem.querySelectorAll('.facet-count'); // TODO: when we drop ie11 support, this can become the spread operator:
247
254
 
248
- const longest = Array.from(nodes).sort(longer)[0];
255
+ var longest = Array.from(nodes).sort(longer)[0];
249
256
 
250
257
  if (longest && longest.textContent) {
251
- const width = longest.textContent.length + 1 + 'ch';
258
+ var width = longest.textContent.length + 1 + 'ch';
252
259
  elem.querySelector('.facet-count').style.width = width;
253
260
  }
254
261
  });
@@ -359,13 +366,13 @@ Blacklight.modal.onFailure = function (jqXHR, textStatus, errorThrown) {
359
366
  console.error('Server error:', this.url, jqXHR.status, errorThrown);
360
367
  var contents = '<div class="modal-header">' + '<div class="modal-title">There was a problem with your request.</div>' + '<button type="button" class="blacklight-modal-close btn-close close" data-dismiss="modal" aria-label="Close">' + ' <span aria-hidden="true">&times;</span>' + '</button></div>' + ' <div class="modal-body"><p>Expected a successful response from the server, but got an error</p>' + '<pre>' + this.type + ' ' + this.url + "\n" + jqXHR.status + ': ' + errorThrown + '</pre></div>';
361
368
  $(Blacklight.modal.modalSelector).find('.modal-content').html(contents);
362
- $(Blacklight.modal.modalSelector).modal('show');
369
+ Blacklight.modal.show();
363
370
  };
364
371
 
365
372
  Blacklight.modal.receiveAjax = function (contents) {
366
373
  // does it have a data- selector for container?
367
374
  // important we don't execute script tags, we shouldn't.
368
- // code modelled off of JQuery ajax.load. https://github.com/jquery/jquery/blob/master/src/ajax/load.js?source=c#L62
375
+ // code modelled off of JQuery ajax.load. https://github.com/jquery/jquery/blob/main/src/ajax/load.js?source=c#L62
369
376
  var container = $('<div>').append(jQuery.parseHTML(contents)).find(Blacklight.modal.containerSelector).first();
370
377
 
371
378
  if (container.length !== 0) {
@@ -378,7 +385,7 @@ Blacklight.modal.receiveAjax = function (contents) {
378
385
  $(Blacklight.modal.modalSelector).trigger(e); // if they did preventDefault, don't show the dialog
379
386
 
380
387
  if (e.isDefaultPrevented()) return;
381
- $(Blacklight.modal.modalSelector).modal('show');
388
+ Blacklight.modal.show();
382
389
  };
383
390
 
384
391
  Blacklight.modal.modalAjaxLinkClick = function (e) {
@@ -425,7 +432,7 @@ Blacklight.modal.setupModal = function () {
425
432
  Blacklight.modal.checkCloseModal = function (event) {
426
433
  if ($(event.target).find(Blacklight.modal.modalCloseSelector).length) {
427
434
  var modalFlashes = $(this).find('.flash_messages');
428
- $(event.target).modal('hide');
435
+ Blacklight.modal.hide(event.target);
429
436
  event.preventDefault();
430
437
  var mainFlashes = $('#main-flashes');
431
438
  mainFlashes.append(modalFlashes);
@@ -433,6 +440,22 @@ Blacklight.modal.checkCloseModal = function (event) {
433
440
  }
434
441
  };
435
442
 
443
+ Blacklight.modal.hide = function (el) {
444
+ if (bootstrap.Modal.VERSION >= "5") {
445
+ bootstrap.Modal.getOrCreateInstance(el).hide();
446
+ } else {
447
+ $(el || Blacklight.modal.modalSelector).modal('hide');
448
+ }
449
+ };
450
+
451
+ Blacklight.modal.show = function (el) {
452
+ if (bootstrap.Modal.VERSION >= "5") {
453
+ bootstrap.Modal.getOrCreateInstance(el).show();
454
+ } else {
455
+ $(el || Blacklight.modal.modalSelector).modal('show');
456
+ }
457
+ };
458
+
436
459
  Blacklight.onLoad(function () {
437
460
  Blacklight.modal.setupModal();
438
461
  });
@@ -443,9 +466,9 @@ Blacklight.doSearchContextBehavior = function () {
443
466
  return Blacklight.do_search_context_behavior();
444
467
  }
445
468
 
446
- const elements = document.querySelectorAll('a[data-context-href]'); // Equivalent to Array.from(), but supports ie11
469
+ var elements = document.querySelectorAll('a[data-context-href]'); // Equivalent to Array.from(), but supports ie11
447
470
 
448
- const nodes = Array.prototype.slice.call(elements);
471
+ var nodes = Array.prototype.slice.call(elements);
449
472
  nodes.forEach(function (element) {
450
473
  element.addEventListener('click', function (e) {
451
474
  Blacklight.handleSearchContextMethod.call(e.currentTarget, e);
@@ -453,9 +476,17 @@ Blacklight.doSearchContextBehavior = function () {
453
476
  });
454
477
  };
455
478
 
456
- Blacklight.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content;
479
+ Blacklight.csrfToken = function () {
480
+ var _document$querySelect;
481
+
482
+ return (_document$querySelect = document.querySelector('meta[name=csrf-token]')) === null || _document$querySelect === void 0 ? void 0 : _document$querySelect.content;
483
+ };
484
+
485
+ Blacklight.csrfParam = function () {
486
+ var _document$querySelect2;
457
487
 
458
- Blacklight.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content; // this is the Rails.handleMethod with a couple adjustments, described inline:
488
+ return (_document$querySelect2 = document.querySelector('meta[name=csrf-param]')) === null || _document$querySelect2 === void 0 ? void 0 : _document$querySelect2.content;
489
+ }; // this is the Rails.handleMethod with a couple adjustments, described inline:
459
490
  // first, we're attaching this directly to the event handler, so we can check for meta-keys
460
491
 
461
492
 
@@ -467,22 +498,21 @@ Blacklight.handleSearchContextMethod = function (event) {
467
498
 
468
499
  var link = this; // instead of using the normal href, we need to use the context href instead
469
500
 
470
- let href = link.getAttribute('data-context-href');
471
- let target = link.getAttribute('target');
472
- let csrfToken = Blacklight.csrfToken();
473
- let csrfParam = Blacklight.csrfParam();
474
- let form = document.createElement('form');
501
+ var href = link.getAttribute('data-context-href');
502
+ var target = link.getAttribute('target');
503
+ var csrfToken = Blacklight.csrfToken();
504
+ var csrfParam = Blacklight.csrfParam();
505
+ var form = document.createElement('form');
475
506
  form.method = 'post';
476
507
  form.action = href;
477
- let formContent = `<input name="_method" value="post" type="hidden" />
478
- <input name="redirect" value="${link.getAttribute('href')}" type="hidden" />`; // check for meta keys.. if set, we should open in a new tab
508
+ var formContent = "<input name=\"_method\" value=\"post\" type=\"hidden\" />\n <input name=\"redirect\" value=\"".concat(link.getAttribute('href'), "\" type=\"hidden\" />"); // check for meta keys.. if set, we should open in a new tab
479
509
 
480
510
  if (event.metaKey || event.ctrlKey) {
481
511
  target = '_blank';
482
512
  }
483
513
 
484
514
  if (csrfParam !== undefined && csrfToken !== undefined) {
485
- formContent += `<input name="${csrfParam}" value="${csrfToken}" type="hidden" />`;
515
+ formContent += "<input name=\"".concat(csrfParam, "\" value=\"").concat(csrfToken, "\" type=\"hidden\" />");
486
516
  } // Must trigger submit by click on a button, else "submit" event handler won't work!
487
517
  // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
488
518
 
@@ -1,6 +1,8 @@
1
1
  .constraints-container {
2
2
  @extend .mb-2;
3
- @extend .d-flex;
3
+ display: flex;
4
+ flex-wrap: wrap;
5
+ gap: 0.5rem 0.25rem;
4
6
  }
5
7
 
6
8
  .applied-filter {
@@ -1,5 +1,6 @@
1
1
  .search-widgets {
2
- @extend .d-flex;
2
+ display: flex;
3
+
3
4
  > * {
4
5
  @extend .mx-1;
5
6
  }
@@ -63,14 +63,10 @@ module Blacklight
63
63
 
64
64
  Deprecation.silence(Blacklight::SearchState) do
65
65
  @search_state.filters.map do |facet|
66
- missing_facet = @search_state.params.dig("f", "-#{facet.key}:").present?
67
66
  facet.values.map do |val|
68
- next if val.blank? && !missing_facet
67
+ next if val.blank?
69
68
 
70
- if missing_facet && val.blank?
71
- missing = I18n.t("blacklight.search.facets.missing")
72
- yield facet_item_presenter(facet.config, missing, facet.key)
73
- elsif val.is_a?(Array)
69
+ if val.is_a?(Array)
74
70
  yield inclusive_facet_item_presenter(facet.config, val, facet.key) if val.any?(&:present?)
75
71
  else
76
72
  yield facet_item_presenter(facet.config, val, facet.key)
@@ -90,7 +86,7 @@ module Blacklight
90
86
  end
91
87
 
92
88
  def facet_item_presenter(facet_config, facet_item, facet_field)
93
- Blacklight::FacetItemPresenter.new(facet_item, facet_config, @view_context, facet_field)
89
+ (facet_config.item_presenter || Blacklight::FacetItemPresenter).new(facet_item, facet_config, @view_context, facet_field)
94
90
  end
95
91
 
96
92
  def inclusive_facet_item_presenter(facet_config, facet_item, facet_field)
@@ -17,7 +17,7 @@ module Blacklight
17
17
  return to_enum(:presenters) unless block_given?
18
18
 
19
19
  @facet_field.paginator.items.each do |item|
20
- yield Blacklight::FacetItemPresenter.new(item, @facet_field.facet_field, @view_context, @facet_field.key, @facet_field.search_state)
20
+ yield (@facet_field.facet_field.item_presenter || Blacklight::FacetItemPresenter).new(item, @facet_field.facet_field, @view_context, @facet_field.key, @facet_field.search_state)
21
21
  end
22
22
  end
23
23
  end
@@ -78,7 +78,7 @@ module Blacklight
78
78
  end
79
79
 
80
80
  def facet_item_presenter(facet_item)
81
- Blacklight::FacetItemPresenter.new(facet_item, @facet_item.facet_config, @view_context, @facet_item.facet_field, @facet_item.search_state)
81
+ (@facet_item.facet_config.item_presenter || Blacklight::FacetItemPresenter).new(facet_item, @facet_item.facet_config, @view_context, @facet_item.facet_field, @facet_item.search_state)
82
82
  end
83
83
  end
84
84
  end
@@ -6,8 +6,8 @@
6
6
  <h1 class="modal-title"><%= title %></h1>
7
7
  <% end) %>
8
8
 
9
- <button type="button" class="blacklight-modal-close btn-close close" data-dismiss="modal" aria-label="<%= t('blacklight.modal.close') %>">
10
- <span aria-hidden="true">&times;</span>
9
+ <button type="button" class="blacklight-modal-close btn-close close" data-bs-dismiss="modal" data-dismiss="modal" aria-label="<%= t('blacklight.modal.close') %>">
10
+ <span aria-hidden="true" class="visually-hidden">&times;</span>
11
11
  </button>
12
12
  </div>
13
13
 
@@ -260,7 +260,7 @@ module Blacklight::Catalog
260
260
 
261
261
  # Email Action (this will render the appropriate view on GET requests and process the form and send the email on POST requests)
262
262
  def email_action documents
263
- mail = RecordMailer.email_record(documents, { to: params[:to], message: params[:message] }, url_options)
263
+ mail = RecordMailer.email_record(documents, { to: params[:to], message: params[:message], config: blacklight_config }, url_options)
264
264
  if mail.respond_to? :deliver_now
265
265
  mail.deliver_now
266
266
  else
@@ -271,7 +271,7 @@ module Blacklight::Catalog
271
271
  # SMS action (this will render the appropriate view on GET requests and process the form and send the email on POST requests)
272
272
  def sms_action documents
273
273
  to = "#{params[:to].gsub(/[^\d]/, '')}@#{params[:carrier]}"
274
- mail = RecordMailer.sms_record(documents, { to: to }, url_options)
274
+ mail = RecordMailer.sms_record(documents, { to: to, config: blacklight_config }, url_options)
275
275
  if mail.respond_to? :deliver_now
276
276
  mail.deliver_now
277
277
  else
@@ -319,7 +319,7 @@ module Blacklight::FacetsHelperBehavior
319
319
  end
320
320
 
321
321
  def facet_item_presenter(facet_config, facet_item, facet_field)
322
- Blacklight::FacetItemPresenter.new(facet_item, facet_config, self, facet_field)
322
+ (facet_config.item_presenter || Blacklight::FacetItemPresenter).new(facet_item, facet_config, self, facet_field)
323
323
  end
324
324
 
325
325
  def facet_item_component(facet_config, facet_item, facet_field, **args)
@@ -9,7 +9,10 @@ module Blacklight::RenderPartialsHelperBehavior
9
9
  # @param [Hash] locals to pass to the render call
10
10
  # @return [String]
11
11
  def render_document_index documents = nil, locals = {}
12
- documents ||= @response.documents
12
+ unless documents
13
+ Deprecation.warn(self, "Calling render_document_index without documents is deprecated and will be removed in version 8")
14
+ documents = @response.documents
15
+ end
13
16
  render_document_index_with_view(document_index_view_type, documents, locals)
14
17
  end
15
18
 
@@ -1,4 +1,4 @@
1
- Blacklight = function() {
1
+ const Blacklight = function() {
2
2
  var buffer = new Array;
3
3
  return {
4
4
  onLoad: function(func) {
@@ -13,7 +13,9 @@ Blacklight = function() {
13
13
 
14
14
  listeners: function () {
15
15
  var listeners = [];
16
- if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
16
+ if (typeof Turbo !== 'undefined') {
17
+ listeners.push('turbo:load');
18
+ } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
17
19
  // Turbolinks 5
18
20
  if (Turbolinks.BrowserAdapter) {
19
21
  listeners.push('turbolinks:load');
@@ -41,8 +43,12 @@ Blacklight.listeners().forEach(function(listener) {
41
43
  Blacklight.onLoad(function () {
42
44
  const elem = document.querySelector('.no-js');
43
45
 
46
+ // The "no-js" class may already have been removed because this function is
47
+ // run on every turbo:load event, in that case, it won't find an element.
44
48
  if (!elem) return;
45
49
 
46
50
  elem.classList.remove('no-js')
47
51
  elem.classList.add('js')
48
52
  })
53
+
54
+ window.Blacklight = Blacklight
@@ -114,13 +114,13 @@ Blacklight.modal.onFailure = function(jqXHR, textStatus, errorThrown) {
114
114
  this.type + ' ' + this.url + "\n" + jqXHR.status + ': ' + errorThrown +
115
115
  '</pre></div>';
116
116
  $(Blacklight.modal.modalSelector).find('.modal-content').html(contents);
117
- $(Blacklight.modal.modalSelector).modal('show');
117
+ Blacklight.modal.show();
118
118
  }
119
119
 
120
120
  Blacklight.modal.receiveAjax = function (contents) {
121
121
  // does it have a data- selector for container?
122
122
  // important we don't execute script tags, we shouldn't.
123
- // code modelled off of JQuery ajax.load. https://github.com/jquery/jquery/blob/master/src/ajax/load.js?source=c#L62
123
+ // code modelled off of JQuery ajax.load. https://github.com/jquery/jquery/blob/main/src/ajax/load.js?source=c#L62
124
124
  var container = $('<div>').
125
125
  append( jQuery.parseHTML(contents) ).find( Blacklight.modal.containerSelector ).first();
126
126
  if (container.length !== 0) {
@@ -135,7 +135,7 @@ Blacklight.modal.receiveAjax = function (contents) {
135
135
  // if they did preventDefault, don't show the dialog
136
136
  if (e.isDefaultPrevented()) return;
137
137
 
138
- $(Blacklight.modal.modalSelector).modal('show');
138
+ Blacklight.modal.show();
139
139
  };
140
140
 
141
141
 
@@ -196,7 +196,7 @@ Blacklight.modal.checkCloseModal = function(event) {
196
196
  if ($(event.target).find(Blacklight.modal.modalCloseSelector).length) {
197
197
  var modalFlashes = $(this).find('.flash_messages');
198
198
 
199
- $(event.target).modal('hide');
199
+ Blacklight.modal.hide(event.target);
200
200
  event.preventDefault();
201
201
 
202
202
  var mainFlashes = $('#main-flashes');
@@ -205,6 +205,22 @@ Blacklight.modal.checkCloseModal = function(event) {
205
205
  }
206
206
  }
207
207
 
208
+ Blacklight.modal.hide = function(el) {
209
+ if (bootstrap.Modal.VERSION >= "5") {
210
+ bootstrap.Modal.getOrCreateInstance(el).hide();
211
+ } else {
212
+ $(el || Blacklight.modal.modalSelector).modal('hide');
213
+ }
214
+ }
215
+
216
+ Blacklight.modal.show = function(el) {
217
+ if (bootstrap.Modal.VERSION >= "5") {
218
+ bootstrap.Modal.getOrCreateInstance(el).show();
219
+ } else {
220
+ $(el || Blacklight.modal.modalSelector).modal('show');
221
+ }
222
+ }
223
+
208
224
  Blacklight.onLoad(function() {
209
225
  Blacklight.modal.setupModal();
210
226
  });
@@ -2,13 +2,25 @@
2
2
  # This module provides the body of an email export based on the document's semantic values
3
3
  module Blacklight::Document::Email
4
4
  # Return a text string that will be the body of the email
5
- def to_email_text
6
- semantics = to_semantic_values
5
+ def to_email_text(config = nil)
7
6
  body = []
8
- body << I18n.t('blacklight.email.text.title', value: semantics[:title].join(" ")) if semantics[:title].present?
9
- body << I18n.t('blacklight.email.text.author', value: semantics[:author].join(" ")) if semantics[:author].present?
10
- body << I18n.t('blacklight.email.text.format', value: semantics[:format].join(" ")) if semantics[:format].present?
11
- body << I18n.t('blacklight.email.text.language', value: semantics[:language].join(" ")) if semantics[:language].present?
7
+
8
+ if config
9
+ body = config.email_fields.map do |name, field|
10
+ values = [self[name]].flatten
11
+ "#{field.label} #{values.join(' ')}" if self[name].present?
12
+ end
13
+ end
14
+
15
+ # Use to_semantic_values for backwards compatibility
16
+ if body.empty?
17
+ semantics = to_semantic_values
18
+ body << I18n.t('blacklight.email.text.title', value: semantics[:title].join(" ")) if semantics[:title].present?
19
+ body << I18n.t('blacklight.email.text.author', value: semantics[:author].join(" ")) if semantics[:author].present?
20
+ body << I18n.t('blacklight.email.text.format', value: semantics[:format].join(" ")) if semantics[:format].present?
21
+ body << I18n.t('blacklight.email.text.language', value: semantics[:language].join(" ")) if semantics[:language].present?
22
+ end
23
+
12
24
  return body.join("\n") unless body.empty?
13
25
  end
14
26
  end
@@ -2,11 +2,23 @@
2
2
  # This module provides the body of an email export based on the document's semantic values
3
3
  module Blacklight::Document::Sms
4
4
  # Return a text string that will be the body of the email
5
- def to_sms_text
6
- semantics = to_semantic_values
5
+ def to_sms_text(config = nil)
7
6
  body = []
8
- body << I18n.t('blacklight.sms.text.title', value: semantics[:title].first) if semantics[:title].present?
9
- body << I18n.t('blacklight.sms.text.author', value: semantics[:author].first) if semantics[:author].present?
7
+
8
+ if config
9
+ body = config.sms_fields.map do |name, field|
10
+ values = [self[name]].flatten
11
+ "#{field.label} #{values.first}" if self[name].present?
12
+ end
13
+ end
14
+
15
+ # Use to_semantic_values for backwards compatibility
16
+ if body.empty?
17
+ semantics = to_semantic_values
18
+ body << I18n.t('blacklight.sms.text.title', value: semantics[:title].first) if semantics[:title].present?
19
+ body << I18n.t('blacklight.sms.text.author', value: semantics[:author].first) if semantics[:author].present?
20
+ end
21
+
10
22
  return body.join unless body.empty?
11
23
  end
12
24
  end
@@ -3,7 +3,12 @@
3
3
  class RecordMailer < ActionMailer::Base
4
4
  def email_record(documents, details, url_gen_params)
5
5
  title = begin
6
- documents.first.to_semantic_values[:title]
6
+ title_field = details[:config].email.title_field
7
+ if title_field
8
+ [documents.first[title_field]].flatten.first
9
+ else
10
+ documents.first.to_semantic_values[:title]
11
+ end
7
12
  rescue
8
13
  I18n.t('blacklight.email.text.default_title')
9
14
  end
@@ -14,6 +19,7 @@ class RecordMailer < ActionMailer::Base
14
19
 
15
20
  @documents = documents
16
21
  @message = details[:message]
22
+ @config = details[:config]
17
23
  @url_gen_params = url_gen_params
18
24
 
19
25
  mail(to: details[:to], subject: subject)
@@ -21,7 +27,9 @@ class RecordMailer < ActionMailer::Base
21
27
 
22
28
  def sms_record(documents, details, url_gen_params)
23
29
  @documents = documents
30
+ @config = details[:config]
24
31
  @url_gen_params = url_gen_params
32
+
25
33
  mail(to: details[:to], subject: "")
26
34
  end
27
35
  end
@@ -56,6 +56,8 @@ module Blacklight
56
56
  view_context.public_send(facet_config.helper_method, label_value)
57
57
  elsif facet_config.query && facet_config.query[label_value]
58
58
  facet_config.query[label_value][:label]
59
+ elsif value == Blacklight::SearchState::FilterField::MISSING
60
+ I18n.t("blacklight.search.facets.missing")
59
61
  elsif facet_config.date
60
62
  localization_options = facet_config.date == true ? {} : facet_config.date
61
63
  I18n.l(Time.zone.parse(label_value), **localization_options)
@@ -14,7 +14,7 @@
14
14
  <%= render 'sort_and_per_page' %>
15
15
  <%= render partial: 'tools', locals: { document_list: @response.documents } %>
16
16
  <h2 class='section-heading sr-only visually-hidden'><%= t('blacklight.bookmarks.list_title') %></h2>
17
- <%= render_document_index %>
17
+ <%= render_document_index @response.documents %>
18
18
  <%= render 'results_pagination' %>
19
19
  <% end %>
20
20
  </div>
@@ -26,7 +26,7 @@
26
26
  <%- elsif render_grouped_response? %>
27
27
  <%= Deprecation.silence(Blacklight::RenderPartialsHelperBehavior) { render_grouped_document_index } %>
28
28
  <%- else %>
29
- <%= render_document_index %>
29
+ <%= render_document_index @response.documents %>
30
30
  <%- end %>
31
31
 
32
32
  <%= render 'results_pagination' %>