arclight 1.0.0 → 1.6.2

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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +22 -15
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +185 -26
  5. data/.solr_wrapper +2 -1
  6. data/README.md +4 -8
  7. data/app/assets/stylesheets/arclight/application.scss +1 -0
  8. data/app/assets/stylesheets/arclight/build.scss +4 -0
  9. data/app/assets/stylesheets/arclight/modules/hierarchy_and_online_contents.scss +22 -18
  10. data/app/assets/stylesheets/arclight/modules/icons.scss +9 -4
  11. data/app/assets/stylesheets/arclight/modules/layout.scss +23 -14
  12. data/app/assets/stylesheets/arclight/modules/mastheads.scss +14 -45
  13. data/app/assets/stylesheets/arclight/modules/search_form.scss +9 -0
  14. data/app/assets/stylesheets/arclight/modules/search_results.scss +15 -16
  15. data/app/assets/stylesheets/arclight/modules/truncator.scss +2 -0
  16. data/app/components/arclight/access_component.rb +1 -1
  17. data/app/components/arclight/bookmark_component.html.erb +1 -1
  18. data/app/components/arclight/breadcrumb_component.rb +17 -7
  19. data/app/components/arclight/breadcrumbs_hierarchy_component.html.erb +2 -2
  20. data/app/components/arclight/breadcrumbs_hierarchy_component.rb +1 -1
  21. data/app/components/arclight/collection_context_component.rb +1 -1
  22. data/app/components/arclight/collection_info_component.rb +1 -1
  23. data/app/components/arclight/collection_sidebar_component.rb +2 -4
  24. data/app/components/arclight/document_collection_hierarchy_component.html.erb +8 -4
  25. data/app/components/arclight/document_component.html.erb +1 -1
  26. data/app/components/arclight/document_components_hierarchy_component.html.erb +5 -17
  27. data/app/components/arclight/document_components_hierarchy_component.rb +7 -1
  28. data/app/components/arclight/document_download_component.rb +1 -1
  29. data/app/components/arclight/embed_component.rb +2 -2
  30. data/app/components/arclight/expand_hierarchy_button_component.html.erb +5 -0
  31. data/app/components/arclight/expand_hierarchy_button_component.rb +16 -0
  32. data/app/components/arclight/group_component.html.erb +6 -2
  33. data/app/components/arclight/group_component.rb +1 -1
  34. data/app/components/arclight/index_metadata_field_component.html.erb +1 -2
  35. data/app/components/arclight/masthead_component.html.erb +8 -6
  36. data/app/components/arclight/metadata_section_component.rb +1 -1
  37. data/app/components/arclight/oembed_viewer_component.rb +1 -1
  38. data/app/components/arclight/online_content_filter_component.html.erb +1 -1
  39. data/app/components/arclight/online_content_filter_component.rb +2 -4
  40. data/app/components/arclight/online_status_indicator_component.rb +1 -1
  41. data/app/components/arclight/repository_breadcrumb_component.html.erb +1 -1
  42. data/app/components/arclight/repository_breadcrumb_component.rb +1 -1
  43. data/app/components/arclight/repository_location_component.html.erb +2 -2
  44. data/app/components/arclight/search_bar_component.html.erb +3 -1
  45. data/app/components/arclight/search_bar_component.rb +1 -1
  46. data/app/components/arclight/search_result_breadcrumbs_component.html.erb +6 -3
  47. data/app/components/arclight/search_result_title_component.html.erb +5 -3
  48. data/app/helpers/arclight/ead_format_helpers.rb +87 -0
  49. data/app/helpers/arclight_helper.rb +11 -0
  50. data/app/models/arclight/document_downloads.rb +1 -1
  51. data/app/models/arclight/parent.rb +3 -10
  52. data/app/models/arclight/parents.rb +6 -4
  53. data/app/models/concerns/arclight/solr_document.rb +14 -4
  54. data/app/views/catalog/_document_list.html.erb +8 -0
  55. data/app/views/catalog/_group.html.erb +1 -1
  56. data/app/views/shared/_main_menu_links.html.erb +1 -1
  57. data/arclight.gemspec +1 -2
  58. data/lib/arclight/normalized_date.rb +5 -10
  59. data/lib/arclight/normalized_id.rb +4 -2
  60. data/lib/arclight/repository.rb +1 -1
  61. data/lib/arclight/traject/ead2_component_config.rb +57 -14
  62. data/lib/arclight/traject/ead2_config.rb +57 -15
  63. data/lib/arclight/version.rb +1 -1
  64. data/lib/generators/arclight/install_generator.rb +41 -4
  65. data/lib/generators/arclight/templates/arclight.scss +6 -5
  66. data/lib/generators/arclight/templates/catalog_controller.rb +48 -5
  67. data/lib/generators/arclight/templates/config/locales/arclight.en.yml +8 -0
  68. data/lib/generators/arclight/templates/config/repositories.yml +2 -2
  69. data/package.json +6 -5
  70. data/solr/conf/schema.xml +13 -7
  71. data/solr/conf/solrconfig.xml +61 -44
  72. data/tasks/arclight.rake +9 -2
  73. data/template.rb +5 -5
  74. metadata +18 -21
@@ -23,13 +23,34 @@ module Arclight
23
23
  .scrub!(:strip).to_html
24
24
  end
25
25
 
26
+ # rubocop:disable Metrics/AbcSize
26
27
  def ead_to_html_scrubber
27
28
  Loofah::Scrubber.new do |node|
28
29
  format_render_attributes(node) if node.attr('render').present?
30
+ convert_to_span(node) if CONVERT_TO_SPAN_TAGS.include? node.name
31
+ convert_to_br(node) if CONVERT_TO_BR_TAG.include? node.name
32
+ format_links(node) if %w[extptr extref extrefloc ptr ref].include? node.name
29
33
  format_lists(node) if %w[list chronlist].include? node.name
34
+ format_indexes(node) if node.name == 'index'
35
+ format_tables(node) if node.name == 'table'
30
36
  node
31
37
  end
32
38
  end
39
+ # rubocop:enable Metrics/AbcSize
40
+
41
+ # Tags that should be converted to <span> tags because of formatting conflicts between XML and HTML
42
+ CONVERT_TO_SPAN_TAGS = ['title'].freeze
43
+
44
+ # Tags that should be converted to HTML <br/> tags
45
+ CONVERT_TO_BR_TAG = ['lb'].freeze
46
+
47
+ def convert_to_span(node)
48
+ node.name = 'span'
49
+ end
50
+
51
+ def convert_to_br(node)
52
+ node.name = 'br'
53
+ end
33
54
 
34
55
  def condense_whitespace(str)
35
56
  str.squish.strip.gsub(/>[\n\s]+</, '><')
@@ -91,6 +112,17 @@ module Arclight
91
112
  end
92
113
  end
93
114
 
115
+ def format_links(node)
116
+ node.remove_attribute('target')
117
+ node.remove_attribute('type')
118
+ if %w[extptr extref extrefloc].include? node.name
119
+ node['target'] = '_blank'
120
+ node['class'] = 'external-link'
121
+ end
122
+ node.content = node['title'] if (%w[extptr ptr].include? node.name) && node['title'].present?
123
+ node.name = 'a' if node['href'].present?
124
+ end
125
+
94
126
  def format_lists(node)
95
127
  format_simple_lists(node) if node.name == 'list' && node['type'] != 'deflist'
96
128
  format_deflists(node) if node.name == 'list' && node['type'] == 'deflist'
@@ -218,5 +250,60 @@ module Arclight
218
250
  end
219
251
  multi_events.each { |event_node| event_node.name = 'div' }
220
252
  end
253
+
254
+ # Format EAD <index> elements
255
+ def format_indexes(node)
256
+ index_head = node.at_css('head')
257
+ index_head&.name = 'h3'
258
+ index_head&.add_class('index-head')
259
+ index_head['id'] = ['index-', index_head.text].join.parameterize if index_head.present?
260
+ format_indexentries(node)
261
+ node.name = 'div'
262
+ end
263
+
264
+ # Grab all of the <indexentry> children as a nodeset, move them into
265
+ # a <table>, wrap each entry in a <tr> & each value in a <td>.
266
+ def format_indexentries(node)
267
+ indexentries = node.css('indexentry')
268
+ return if indexentries.blank?
269
+
270
+ indexentries.first.previous = '<table class="table indexentries" />'
271
+ indexentries.map do |i|
272
+ i.parent = node.at_css('table.table.indexentries')
273
+ i.wrap('<tr/>')
274
+ i.element_children.map { |c| c.wrap('<td/>') }
275
+ # Assuming two columns in an index, create a blank cell for entries
276
+ # with a missing value.
277
+ i.add_child('<td/>') if i.element_children.count == 1
278
+ end
279
+ end
280
+
281
+ # Format EAD <table> elements, converting <tgroup> to HTML <table>.
282
+ # Ignoring <colspec>, @colname, @colwidth complex rendering
283
+ # logic for now.
284
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
285
+ def format_tables(node)
286
+ node.remove_attribute('frame')
287
+ node.name = 'div' if node.css('tgroup').present?
288
+ format_table_head(node)
289
+ tgroups = node.css('tgroup')
290
+ tgroups&.map do |t|
291
+ t.name = 'table'
292
+ t.remove_attribute('cols')
293
+ t.add_class('table')
294
+ t.css('row').map { |r| r.name = 'tr' }
295
+ t.css('thead entry').map { |e| e.name = 'th' }
296
+ t.css('tbody entry').map { |e| e.name = 'td' }
297
+ end
298
+ end
299
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
300
+
301
+ def format_table_head(node)
302
+ table_head = node.at_css('head')
303
+ return if table_head.blank?
304
+
305
+ table_head.name = 'h3'
306
+ table_head.add_class('table-head')
307
+ end
221
308
  end
222
309
  end
@@ -5,6 +5,7 @@
5
5
  module ArclightHelper
6
6
  include Arclight::EadFormatHelpers
7
7
  include Arclight::FieldConfigHelpers
8
+ include Blacklight::DocumentHelperBehavior
8
9
  include Blacklight::LayoutHelperBehavior
9
10
 
10
11
  def repository_collections_path(repository)
@@ -91,6 +92,16 @@ module ArclightHelper
91
92
  end
92
93
  end
93
94
 
95
+ ##
96
+ # Override of BL core https://github.com/projectblacklight/blacklight/blob/v8.1.0/app/helpers/blacklight/document_helper_behavior.rb#L55
97
+ # Remove document_type check. It isn't a method on arclight documents
98
+ # Check if the document is in the user's bookmarks
99
+ # @param [Blacklight::Document] document
100
+ # @return [Boolean]
101
+ def bookmarked?(document)
102
+ current_bookmarks.any? { |x| x.document_id == document.id }
103
+ end
104
+
94
105
  def current_context_document
95
106
  @document
96
107
  end
@@ -33,7 +33,7 @@ module Arclight
33
33
  class << self
34
34
  def config
35
35
  @config ||= begin
36
- YAML.safe_load(::File.read(config_filename))
36
+ YAML.safe_load_file(config_filename)
37
37
  rescue Errno::ENOENT
38
38
  {}
39
39
  end
@@ -7,6 +7,9 @@ module Arclight
7
7
  class Parent
8
8
  attr_reader :id, :label, :eadid, :level
9
9
 
10
+ alias global_id id
11
+ Arclight.deprecation.deprecate_methods(self, global_id: 'Call `id` instead')
12
+
10
13
  def initialize(id:, label:, eadid:, level:)
11
14
  @id = id
12
15
  @label = label
@@ -17,15 +20,5 @@ module Arclight
17
20
  def collection?
18
21
  level == 'collection'
19
22
  end
20
-
21
- ##
22
- # Concatenates the eadid and the id, to return an "id" in the context of
23
- # Blacklight and Solr
24
- # @return [String]
25
- def global_id
26
- return id if eadid == id
27
-
28
- "#{eadid}#{id}"
29
- end
30
23
  end
31
24
  end
@@ -5,10 +5,11 @@ module Arclight
5
5
  # Object for parsing and formalizing Solr_Ead "Parents"
6
6
  # https://github.com/awead/solr_ead/blob/8cf7ffaa66e0e4c9c0b12f5646d6c2e20984cd99/lib/solr_ead/behaviors.rb#L54-L57
7
7
  class Parents
8
- attr_reader :ids, :labels, :levels
8
+ attr_reader :ids, :legacy_ids, :labels, :levels
9
9
 
10
- def initialize(ids:, labels:, eadid:, levels:)
10
+ def initialize(ids:, legacy_ids:, labels:, eadid:, levels:)
11
11
  @ids = ids
12
+ @legacy_ids = legacy_ids
12
13
  @labels = labels
13
14
  @eadid = eadid
14
15
  @levels = levels
@@ -21,17 +22,18 @@ module Arclight
21
22
  ##
22
23
  # @return [Array[Arclight::Parent]]
23
24
  def as_parents
24
- ids.map.with_index { |id, idx| Arclight::Parent.new(id: id, label: labels[idx], eadid: eadid, level: levels[idx]) }
25
+ (ids.presence || legacy_ids).map.with_index { |id, idx| Arclight::Parent.new(id: id, label: labels[idx], eadid: eadid, level: levels[idx]) }
25
26
  end
26
27
 
27
28
  ##
28
29
  # @param [SolrDocument] document
29
30
  def self.from_solr_document(document)
30
31
  ids = document.parent_ids
32
+ legacy_ids = document.legacy_parent_ids.map { |legacy_id| document.root == legacy_id ? legacy_id : "#{document.root}#{legacy_id}" }
31
33
  labels = document.parent_labels
32
34
  eadid = document.eadid
33
35
  levels = document.parent_levels
34
- new(ids: ids, labels: labels, eadid: eadid, levels: levels)
36
+ new(ids: ids, legacy_ids: legacy_ids, labels: labels, eadid: eadid, levels: levels)
35
37
  end
36
38
  end
37
39
  end
@@ -7,11 +7,15 @@ module Arclight
7
7
  extend ActiveSupport::Concern
8
8
 
9
9
  included do
10
- attribute :parent_ids, :array, 'parent_ssim'
10
+ attribute :collection_id, :string, '_root_'
11
+ Arclight.deprecation.deprecate_methods(self, collection_id: 'Use `root` instead')
12
+ attribute :parent_ids, :array, 'parent_ids_ssim'
13
+ attribute :legacy_parent_ids, :array, 'parent_ssim'
14
+ Arclight.deprecation.deprecate_methods(self, legacy_parent_ids: 'Use `parent_ids` instead')
11
15
  attribute :parent_labels, :array, 'parent_unittitles_ssm'
12
16
  attribute :parent_levels, :array, 'parent_levels_ssm'
13
17
  attribute :unitid, :string, 'unitid_ssm'
14
- attribute :extent, :string, 'extent_ssm'
18
+ attribute :extent, :array, 'extent_ssm'
15
19
  attribute :abstract, :string, 'abstract_html_tesm'
16
20
  attribute :scope, :string, 'scopecontent_html_tesm'
17
21
  attribute :creator, :string, 'creator_ssm'
@@ -48,6 +52,7 @@ module Arclight
48
52
  def normalized_eadid
49
53
  Arclight::NormalizedId.new(eadid).to_s
50
54
  end
55
+ Arclight.deprecation.deprecate_methods(self, normalized_eadid: 'Use `root` instead')
51
56
 
52
57
  def repository
53
58
  first('repository_ssm') || collection&.first('repository_ssm')
@@ -111,8 +116,13 @@ module Arclight
111
116
  end
112
117
 
113
118
  def containers
114
- # NOTE: that .titlecase strips punctuation, like hyphens, we want to keep
115
- fetch('containers_ssim', []).map(&:capitalize)
119
+ # NOTE: Keep uppercase characters if present, but upcase the first if not already
120
+ containers_field = fetch('containers_ssim', []).reject(&:empty?)
121
+ return [] if containers_field.blank?
122
+
123
+ containers_field.map do |container|
124
+ container.dup.sub!(/\A./, &:upcase)
125
+ end
116
126
  end
117
127
 
118
128
  # @return [Array<String>] with embedded highlights using <em>...</em>
@@ -0,0 +1,8 @@
1
+ <% # Override to conditionally add id="documents" on div. When documents are grouped it causes duplicate id issues %>
2
+ <% # https://github.com/projectblacklight/blacklight/blob/v8.1.0/app/views/catalog/_document_list.html.erb %>
3
+ <% # container for all documents in index list view -%>
4
+ <% view_config = local_assigns[:view_config] || blacklight_config&.view_config(document_index_view_type) %>
5
+ <div <%= 'id="documents"'.html_safe unless grouped? %> class="al-document-listings documents-<%= view_config&.key || document_index_view_type %>">
6
+ <% document_presenters = documents.map { |doc| document_presenter(doc) } -%>
7
+ <%= render view_config.document_component.with_collection(document_presenters, partials: view_config.partials, counter_offset: @response&.start || 0) %>
8
+ </div>
@@ -1,4 +1,4 @@
1
1
  <% # container for all groups in index view -%>
2
- <div class='al-grouped-results'>
2
+ <div id="documents" class='al-grouped-results'>
3
3
  <%= render (blacklight_config.view_config(document_index_view_type).group_component || Arclight::GroupComponent).with_collection(@response.groups) %>
4
4
  </div>
@@ -1,6 +1,6 @@
1
1
  <li class="nav-item <%= repositories_active_class %>">
2
2
  <%= link_to t('arclight.routes.repositories'), arclight_engine.repositories_path, class: 'nav-link' %>
3
3
  </li>
4
- <li class="nav-item ml-3 ms-3 <%= collection_active_class %>">
4
+ <li class="nav-item ms-3 <%= collection_active_class %>">
5
5
  <%= link_to t('arclight.routes.collections'), arclight_engine.collections_path, class: 'nav-link' %>
6
6
  </li>
data/arclight.gemspec CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
26
26
 
27
27
  spec.add_dependency 'blacklight', '>= 8.0.0', '< 9'
28
28
  spec.add_dependency 'gretel'
29
- spec.add_dependency 'rails', '~> 7.0'
29
+ spec.add_dependency 'rails', '>= 7.1', '< 9'
30
30
  spec.add_dependency 'traject', '~> 3.0'
31
31
  spec.add_dependency 'traject_plus', '~> 2.0'
32
32
 
@@ -43,5 +43,4 @@ Gem::Specification.new do |spec|
43
43
  spec.add_development_dependency 'selenium-webdriver'
44
44
  spec.add_development_dependency 'simplecov'
45
45
  spec.add_development_dependency 'solr_wrapper'
46
- spec.add_development_dependency 'webdrivers'
47
46
  end
@@ -39,16 +39,11 @@ module Arclight
39
39
 
40
40
  # @see http://www2.archivists.org/standards/DACS/part_I/chapter_2/4_date for rules
41
41
  def normalize
42
- if inclusive.present?
43
- result = inclusive.to_s
44
- result << ", bulk #{bulk}" if bulk.present?
45
- elsif other.present?
46
- result = other.to_s
47
- else
48
- result = nil
49
- end
50
-
51
- result&.strip
42
+ result = []
43
+ result << inclusive if inclusive.present?
44
+ result << other if other.present?
45
+ result << "bulk #{bulk}" if bulk.present?
46
+ result.compact.map(&:strip).join(', ').presence
52
47
  end
53
48
  end
54
49
  end
@@ -5,9 +5,11 @@ require 'arclight/exceptions'
5
5
  module Arclight
6
6
  ##
7
7
  # A simple utility class to normalize identifiers
8
- # to be used around the application for linking
9
8
  class NormalizedId
10
- def initialize(id)
9
+ # Accepts unused kwargs from the ead2_config.rb id to_field directive
10
+ # (:title and :repository) so that applications can provide a custom
11
+ # id_normalizer class to traject to form the collection id from these attributes.
12
+ def initialize(id, **_kwargs)
11
13
  @id = id
12
14
  end
13
15
 
@@ -72,7 +72,7 @@ module Arclight
72
72
  # @return [Hash<Slug,Repository>]
73
73
  def self.from_yaml(file)
74
74
  repos = {}
75
- data = YAML.safe_load(File.read(file))
75
+ data = YAML.safe_load_file(file)
76
76
  data.each_key do |slug|
77
77
  repos[slug] = new(data[slug].merge(slug: slug))
78
78
  end
@@ -29,6 +29,7 @@ settings do
29
29
  provide 'title_normalizer', 'Arclight::NormalizedTitle'
30
30
  provide 'reader_class_name', 'Arclight::Traject::NokogiriNamespacelessReader'
31
31
  provide 'logger', Logger.new($stderr)
32
+ provide 'component_identifier_format', '%<root_id>s_%<ref_id>s'
32
33
  end
33
34
 
34
35
  NAME_ELEMENTS = %w[corpname famname name persname].freeze
@@ -60,6 +61,7 @@ DID_SEARCHABLE_NOTES_FIELDS = %w[
60
61
  abstract
61
62
  materialspec
62
63
  physloc
64
+ note
63
65
  ].freeze
64
66
 
65
67
  # ==================
@@ -67,7 +69,9 @@ DID_SEARCHABLE_NOTES_FIELDS = %w[
67
69
  #
68
70
  # NOTE: All fields should be stored in Solr
69
71
  # ==================
70
- to_field 'ref_ssi' do |record, accumulator, _context|
72
+ to_field 'ref_ssi' do |record, accumulator, context|
73
+ next if context.output_hash['ref_ssi']
74
+
71
75
  accumulator << if record.attribute('id').blank?
72
76
  strategy = Arclight::MissingIdStrategy.selected
73
77
  hexdigest = strategy.new(record).to_hexdigest
@@ -82,7 +86,7 @@ to_field 'ref_ssi' do |record, accumulator, _context|
82
86
  record['id'] = hexdigest
83
87
  hexdigest
84
88
  else
85
- record.attribute('id')&.value&.strip&.gsub('.', '-')
89
+ record.attribute('id')&.value&.strip&.gsub('.', '-') # rubocop:disable Style/SafeNavigationChainLength
86
90
  end
87
91
  end
88
92
  to_field 'ref_ssm' do |_record, accumulator, context|
@@ -90,10 +94,14 @@ to_field 'ref_ssm' do |_record, accumulator, context|
90
94
  end
91
95
 
92
96
  to_field 'id' do |_record, accumulator, context|
93
- accumulator << [
94
- settings[:root].output_hash['id'],
95
- context.output_hash['ref_ssi']
96
- ].join
97
+ next if context.output_hash['id']
98
+
99
+ data = {
100
+ root_id: settings[:root].output_hash['id']&.first,
101
+ ref_id: context.output_hash['ref_ssi']&.first
102
+ }
103
+
104
+ accumulator << (settings[:component_identifier_format] % data)
97
105
  end
98
106
 
99
107
  to_field 'title_filing_ssi', extract_xpath('./did/unittitle'), first_only
@@ -131,6 +139,11 @@ to_field 'parent_ssi' do |_record, accumulator, _context|
131
139
  accumulator.concat settings[:parent].output_hash['ref_ssi'] || settings[:parent].output_hash['id']
132
140
  end
133
141
 
142
+ to_field 'parent_ids_ssim' do |_record, accumulator, _context|
143
+ accumulator.concat(settings[:parent].output_hash['parent_ids_ssim'] || [])
144
+ accumulator.concat settings[:parent].output_hash['id']
145
+ end
146
+
134
147
  to_field 'parent_unittitles_ssm' do |_rec, accumulator, _context|
135
148
  accumulator.concat(settings[:parent].output_hash['parent_unittitles_ssm'] || [])
136
149
  accumulator.concat settings[:parent].output_hash['normalized_title_ssm'] || []
@@ -155,12 +168,42 @@ to_field 'collection_ssim' do |_record, accumulator, _context|
155
168
  accumulator.concat settings[:root].output_hash['normalized_title_ssm']
156
169
  end
157
170
 
158
- to_field 'extent_ssm', extract_xpath('./did/physdesc/extent')
159
- to_field 'extent_tesim', extract_xpath('./did/physdesc/extent')
171
+ # This accumulates direct text from a physdesc, ignoring child elements handled elsewhere
172
+ to_field 'physdesc_tesim', extract_xpath('./did/physdesc', to_text: false) do |_record, accumulator|
173
+ accumulator.map! do |element|
174
+ physdesc = []
175
+ element.children.map do |child|
176
+ next if child.instance_of?(Nokogiri::XML::Element)
177
+
178
+ physdesc << child.text&.strip unless child.text&.strip&.empty?
179
+ end.flatten
180
+ physdesc.join(' ') unless physdesc.empty?
181
+ end
182
+ end
183
+
184
+ to_field 'extent_ssm' do |record, accumulator|
185
+ physdescs = record.xpath('./did/physdesc')
186
+ extents_per_physdesc = physdescs.map do |physdesc|
187
+ extents = physdesc.xpath('./extent').map { |e| e.text.strip }
188
+ # Join extents within the same physdesc with an empty string
189
+ extents.join(' ') unless extents.empty?
190
+ end
191
+
192
+ # Add each physdesc separately to the accumulator
193
+ accumulator.concat(extents_per_physdesc)
194
+ end
195
+
196
+ to_field 'extent_tesim' do |_record, accumulator, context|
197
+ accumulator.concat context.output_hash['extent_ssm'] || []
198
+ end
199
+
200
+ to_field 'physfacet_tesim', extract_xpath('./did/physdesc/physfacet')
201
+ to_field 'dimensions_tesim', extract_xpath('./did/physdesc/dimensions')
202
+
203
+ to_field 'indexes_html_tesm', extract_xpath('./index', to_text: false)
204
+ to_field 'indexes_tesim', extract_xpath('./index')
160
205
 
161
- to_field 'creator_ssm', extract_xpath('./did/origination')
162
206
  to_field 'creator_ssim', extract_xpath('./did/origination')
163
- to_field 'creators_ssim', extract_xpath('./did/origination')
164
207
  to_field 'creator_sort' do |record, accumulator|
165
208
  accumulator << record.xpath('./did/origination').map(&:text).join(', ')
166
209
  end
@@ -214,7 +257,7 @@ to_field 'digital_objects_ssm', extract_xpath('./dao|./did/dao', to_text: false)
214
257
  end
215
258
  end
216
259
 
217
- to_field 'date_range_ssim', extract_xpath('./did/unitdate/@normal', to_text: false) do |_record, accumulator|
260
+ to_field 'date_range_isim', extract_xpath('./did/unitdate/@normal', to_text: false) do |_record, accumulator|
218
261
  range = Arclight::YearRange.new
219
262
  next range.years if accumulator.blank?
220
263
 
@@ -225,6 +268,7 @@ end
225
268
 
226
269
  NAME_ELEMENTS.map do |selector|
227
270
  to_field 'names_ssim', extract_xpath("./controlaccess/#{selector}"), unique
271
+ to_field 'names_ssim', extract_xpath(".//#{selector}"), unique
228
272
  to_field "#{selector}_ssim", extract_xpath(".//#{selector}")
229
273
  end
230
274
 
@@ -244,8 +288,6 @@ to_field 'access_subjects_ssm' do |_record, accumulator, context|
244
288
  accumulator.concat(context.output_hash.fetch('access_subjects_ssim', []))
245
289
  end
246
290
 
247
- to_field 'acqinfo_ssim', extract_xpath('/ead/archdesc/acqinfo/*[local-name()!="head"]')
248
- to_field 'acqinfo_ssim', extract_xpath('/ead/archdesc/descgrp/acqinfo/*[local-name()!="head"]')
249
291
  to_field 'acqinfo_ssim', extract_xpath('./acqinfo/*[local-name()!="head"]')
250
292
  to_field 'acqinfo_ssim', extract_xpath('./descgrp/acqinfo/*[local-name()!="head"]')
251
293
 
@@ -263,8 +305,8 @@ SEARCHABLE_NOTES_FIELDS.map do |selector|
263
305
  end
264
306
  DID_SEARCHABLE_NOTES_FIELDS.map do |selector|
265
307
  to_field "#{selector}_html_tesm", extract_xpath("./did/#{selector}", to_text: false)
308
+ to_field "#{selector}_tesim", extract_xpath("./did/#{selector}")
266
309
  end
267
- to_field 'did_note_ssm', extract_xpath('./did/note')
268
310
 
269
311
  # =============================
270
312
  # Each component child document
@@ -280,6 +322,7 @@ to_field 'components' do |record, accumulator, context|
280
322
  provide :counter, context.settings[:counter]
281
323
  provide :depth, context.settings[:depth].to_i + 1
282
324
  provide :component_traject_config, context.settings[:component_traject_config]
325
+ provide :component_identifier_format, context.settings[:component_identifier_format]
283
326
  end
284
327
 
285
328
  i.load_config_file(context.settings[:component_traject_config])
@@ -6,6 +6,7 @@ require 'traject/nokogiri_reader'
6
6
  require 'traject_plus'
7
7
  require 'traject_plus/macros'
8
8
  require 'arclight/level_label'
9
+ require 'arclight/normalized_id'
9
10
  require 'arclight/normalized_date'
10
11
  require 'arclight/normalized_title'
11
12
  require 'active_model/conversion' ## Needed for Arclight::Repository
@@ -48,14 +49,18 @@ DID_SEARCHABLE_NOTES_FIELDS = %w[
48
49
  abstract
49
50
  materialspec
50
51
  physloc
52
+ note
51
53
  ].freeze
52
54
 
53
55
  settings do
56
+ provide 'component_traject_config', File.join(__dir__, 'ead2_component_config.rb')
57
+ provide 'id_normalizer', 'Arclight::NormalizedId'
58
+ provide 'date_normalizer', 'Arclight::NormalizedDate'
59
+ provide 'title_normalizer', 'Arclight::NormalizedTitle'
54
60
  provide 'reader_class_name', 'Arclight::Traject::NokogiriNamespacelessReader'
55
61
  provide 'solr_writer.commit_on_close', 'true'
56
62
  provide 'repository', ENV.fetch('REPOSITORY_ID', nil)
57
63
  provide 'logger', Logger.new($stderr)
58
- provide 'component_traject_config', File.join(__dir__, 'ead2_component_config.rb')
59
64
  end
60
65
 
61
66
  each_record do |_record, context|
@@ -72,7 +77,14 @@ end
72
77
  # NOTE: All fields should be stored in Solr
73
78
  # ==================
74
79
 
75
- to_field 'id', extract_xpath('/ead/eadheader/eadid'), strip, gsub('.', '-')
80
+ to_field 'id' do |record, accumulator|
81
+ id = record.at_xpath('/ead/eadheader/eadid')&.text
82
+ unitid = record.at_xpath('/ead/archdesc/did/unitid')&.text
83
+ title = record.at_xpath('/ead/archdesc/did/unittitle')&.text
84
+ repository = settings['repository']
85
+ accumulator << settings['id_normalizer'].constantize.new(id, unitid: unitid, title: title, repository: repository).to_s
86
+ end
87
+
76
88
  to_field 'title_filing_ssi', extract_xpath('/ead/eadheader/filedesc/titlestmt/titleproper[@type="filing"]')
77
89
  to_field 'title_ssm', extract_xpath('/ead/archdesc/did/unittitle')
78
90
  to_field 'title_tesim', extract_xpath('/ead/archdesc/did/unittitle')
@@ -100,22 +112,18 @@ end
100
112
  to_field 'unitid_ssm', extract_xpath('/ead/archdesc/did/unitid')
101
113
  to_field 'unitid_tesim', extract_xpath('/ead/archdesc/did/unitid')
102
114
 
103
- to_field 'normalized_title_ssm' do |_record, accumulator, context|
104
- dates = Arclight::NormalizedDate.new(
115
+ to_field 'normalized_date_ssm' do |_record, accumulator, context|
116
+ accumulator << settings['date_normalizer'].constantize.new(
105
117
  context.output_hash['unitdate_inclusive_ssm'],
106
118
  context.output_hash['unitdate_bulk_ssim'],
107
119
  context.output_hash['unitdate_other_ssim']
108
120
  ).to_s
109
- title = context.output_hash['title_ssm'].first
110
- accumulator << Arclight::NormalizedTitle.new(title, dates).to_s
111
121
  end
112
122
 
113
- to_field 'normalized_date_ssm' do |_record, accumulator, context|
114
- accumulator << Arclight::NormalizedDate.new(
115
- context.output_hash['unitdate_inclusive_ssm'],
116
- context.output_hash['unitdate_bulk_ssim'],
117
- context.output_hash['unitdate_other_ssim']
118
- ).to_s
123
+ to_field 'normalized_title_ssm' do |_record, accumulator, context|
124
+ title = context.output_hash['title_ssm']&.first
125
+ date = context.output_hash['normalized_date_ssm']&.first
126
+ accumulator << settings['title_normalizer'].constantize.new(title, date).to_s
119
127
  end
120
128
 
121
129
  to_field 'collection_title_tesim' do |_record, accumulator, context|
@@ -185,11 +193,41 @@ to_field 'digital_objects_ssm', extract_xpath('/ead/archdesc/did/dao|/ead/archde
185
193
  end
186
194
  end
187
195
 
188
- to_field 'extent_ssm', extract_xpath('/ead/archdesc/did/physdesc/extent')
189
- to_field 'extent_tesim', extract_xpath('/ead/archdesc/did/physdesc/extent')
196
+ # This accumulates direct text from a physdesc, ignoring child elements handled elsewhere
197
+ to_field 'physdesc_tesim', extract_xpath('/ead/archdesc/did/physdesc', to_text: false) do |_record, accumulator|
198
+ accumulator.map! do |element|
199
+ physdesc = []
200
+ element.children.map do |child|
201
+ next if child.instance_of?(Nokogiri::XML::Element)
202
+
203
+ physdesc << child.text&.strip unless child.text&.strip&.empty?
204
+ end.flatten
205
+ physdesc.join(' ') unless physdesc.empty?
206
+ end
207
+ end
208
+
209
+ to_field 'extent_ssm' do |record, accumulator|
210
+ physdescs = record.xpath('/ead/archdesc/did/physdesc')
211
+ extents_per_physdesc = physdescs.map do |physdesc|
212
+ extents = physdesc.xpath('./extent').map { |e| e.text.strip }
213
+ # Join extents within the same physdesc with an empty string
214
+ extents.join(' ') unless extents.empty?
215
+ end
216
+
217
+ # Add each physdesc separately to the accumulator
218
+ accumulator.concat(extents_per_physdesc)
219
+ end
220
+
221
+ to_field 'extent_tesim' do |_record, accumulator, context|
222
+ accumulator.concat context.output_hash['extent_ssm'] || []
223
+ end
224
+
225
+ to_field 'physfacet_tesim', extract_xpath('/ead/archdesc/did/physdesc/physfacet')
226
+ to_field 'dimensions_tesim', extract_xpath('/ead/archdesc/did/physdesc/dimensions')
227
+
190
228
  to_field 'genreform_ssim', extract_xpath('/ead/archdesc/controlaccess/genreform')
191
229
 
192
- to_field 'date_range_ssim', extract_xpath('/ead/archdesc/did/unitdate/@normal', to_text: false) do |_record, accumulator|
230
+ to_field 'date_range_isim', extract_xpath('/ead/archdesc/did/unitdate/@normal', to_text: false) do |_record, accumulator|
193
231
  range = Arclight::YearRange.new
194
232
  next range.years if accumulator.blank?
195
233
 
@@ -198,6 +236,9 @@ to_field 'date_range_ssim', extract_xpath('/ead/archdesc/did/unitdate/@normal',
198
236
  accumulator.replace range.years
199
237
  end
200
238
 
239
+ to_field 'indexes_html_tesm', extract_xpath('/ead/archdesc/index', to_text: false)
240
+ to_field 'indexes_tesim', extract_xpath('/ead/archdesc/index')
241
+
201
242
  SEARCHABLE_NOTES_FIELDS.map do |selector|
202
243
  to_field "#{selector}_html_tesm", extract_xpath("/ead/archdesc/#{selector}/*[local-name()!='head']", to_text: false) do |_record, accumulator|
203
244
  accumulator.map!(&:to_html)
@@ -208,6 +249,7 @@ end
208
249
 
209
250
  DID_SEARCHABLE_NOTES_FIELDS.map do |selector|
210
251
  to_field "#{selector}_html_tesm", extract_xpath("/ead/archdesc/did/#{selector}", to_text: false)
252
+ to_field "#{selector}_tesim", extract_xpath("/ead/archdesc/did/#{selector}")
211
253
  end
212
254
 
213
255
  NAME_ELEMENTS.map do |selector|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Arclight
4
- VERSION = '1.0.0'
4
+ VERSION = '1.6.2'
5
5
  end