arclight 0.4.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 (227) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc +11 -4
  3. data/.github/workflows/ruby.yml +37 -15
  4. data/.gitignore +1 -0
  5. data/.rspec +0 -1
  6. data/.rubocop.yml +498 -21
  7. data/.rubocop_todo.yml +69 -86
  8. data/.solr_wrapper +2 -1
  9. data/CONTRIBUTING.md +5 -5
  10. data/CONTRIBUTORS.md +1 -1
  11. data/Gemfile +15 -10
  12. data/README.md +20 -16
  13. data/app/assets/javascripts/arclight/arclight.js +4 -8
  14. data/app/assets/javascripts/arclight/oembed_controller.js +58 -0
  15. data/app/assets/javascripts/arclight/truncate_controller.js +27 -0
  16. data/app/assets/stylesheets/arclight/application.scss +2 -5
  17. data/app/assets/stylesheets/arclight/build.scss +4 -0
  18. data/app/assets/stylesheets/arclight/modules/hierarchy_and_online_contents.scss +108 -130
  19. data/app/assets/stylesheets/arclight/modules/icons.scss +19 -3
  20. data/app/assets/stylesheets/arclight/modules/layout.scss +129 -99
  21. data/app/assets/stylesheets/arclight/modules/mastheads.scss +23 -96
  22. data/app/assets/stylesheets/arclight/modules/repositories.scss +2 -2
  23. data/app/assets/stylesheets/arclight/modules/repository_card.scss +4 -48
  24. data/app/assets/stylesheets/arclight/modules/search_form.scss +9 -0
  25. data/app/assets/stylesheets/arclight/modules/search_results.scss +95 -45
  26. data/app/assets/stylesheets/arclight/modules/show_collection.scss +5 -32
  27. data/app/assets/stylesheets/arclight/modules/truncator.scss +58 -0
  28. data/app/assets/stylesheets/arclight/variables.scss +7 -4
  29. data/app/components/arclight/access_component.html.erb +14 -0
  30. data/app/components/arclight/access_component.rb +25 -0
  31. data/app/components/arclight/bookmark_component.html.erb +25 -0
  32. data/app/components/arclight/bookmark_component.rb +9 -0
  33. data/app/components/arclight/breadcrumb_component.rb +50 -0
  34. data/app/components/arclight/breadcrumbs_hierarchy_component.html.erb +34 -0
  35. data/app/components/arclight/breadcrumbs_hierarchy_component.rb +25 -0
  36. data/app/components/arclight/collection_context_component.html.erb +12 -0
  37. data/app/components/arclight/collection_context_component.rb +27 -0
  38. data/app/components/arclight/collection_info_component.html.erb +28 -0
  39. data/app/components/arclight/collection_info_component.rb +26 -0
  40. data/app/components/arclight/collection_sidebar_component.html.erb +15 -0
  41. data/app/components/arclight/collection_sidebar_component.rb +39 -0
  42. data/app/{views/catalog/_search_results_repository.html.erb → components/arclight/constraints_component.html.erb} +2 -1
  43. data/app/components/arclight/constraints_component.rb +17 -0
  44. data/app/components/arclight/document_collection_context_component.html.erb +29 -0
  45. data/app/components/arclight/document_collection_context_component.rb +28 -0
  46. data/app/components/arclight/document_collection_hierarchy_component.html.erb +50 -0
  47. data/app/components/arclight/document_collection_hierarchy_component.rb +62 -0
  48. data/app/components/arclight/document_component.html.erb +39 -0
  49. data/app/components/arclight/document_component.rb +49 -0
  50. data/app/components/arclight/document_components_hierarchy_component.html.erb +20 -0
  51. data/app/components/arclight/document_components_hierarchy_component.rb +34 -0
  52. data/app/components/arclight/document_download_component.html.erb +24 -0
  53. data/app/components/arclight/document_download_component.rb +71 -0
  54. data/app/components/arclight/embed_component.html.erb +10 -0
  55. data/app/components/arclight/embed_component.rb +43 -0
  56. data/app/components/arclight/expand_hierarchy_button_component.html.erb +5 -0
  57. data/app/components/arclight/expand_hierarchy_button_component.rb +16 -0
  58. data/app/components/arclight/group_component.html.erb +34 -0
  59. data/app/components/arclight/group_component.rb +23 -0
  60. data/app/components/arclight/header_component.html.erb +5 -0
  61. data/app/components/arclight/header_component.rb +10 -0
  62. data/app/components/arclight/index_metadata_field_component.html.erb +16 -0
  63. data/app/components/arclight/index_metadata_field_component.rb +20 -0
  64. data/app/components/arclight/masthead_component.html.erb +17 -0
  65. data/app/components/arclight/masthead_component.rb +10 -0
  66. data/app/components/arclight/metadata_section_component.html.erb +13 -0
  67. data/app/components/arclight/metadata_section_component.rb +22 -0
  68. data/app/components/arclight/oembed_viewer_component.html.erb +5 -0
  69. data/app/components/arclight/oembed_viewer_component.rb +16 -0
  70. data/app/components/arclight/online_content_filter_component.html.erb +15 -0
  71. data/app/components/arclight/online_content_filter_component.rb +17 -0
  72. data/app/components/arclight/online_status_indicator_component.rb +19 -0
  73. data/app/components/arclight/repository_breadcrumb_component.html.erb +10 -0
  74. data/app/components/arclight/repository_breadcrumb_component.rb +17 -0
  75. data/app/components/arclight/repository_location_component.html.erb +20 -0
  76. data/app/components/arclight/repository_location_component.rb +10 -0
  77. data/app/components/arclight/search_bar_component.html.erb +27 -0
  78. data/app/components/arclight/search_bar_component.rb +32 -0
  79. data/app/components/arclight/search_result_breadcrumbs_component.html.erb +6 -0
  80. data/app/components/arclight/search_result_breadcrumbs_component.rb +37 -0
  81. data/app/components/arclight/search_result_component.html.erb +20 -0
  82. data/app/components/arclight/search_result_component.rb +18 -0
  83. data/app/components/arclight/search_result_title_component.html.erb +15 -0
  84. data/app/components/arclight/search_result_title_component.rb +15 -0
  85. data/app/components/arclight/sidebar_component.html.erb +9 -0
  86. data/app/components/arclight/sidebar_component.rb +19 -0
  87. data/app/components/arclight/upper_metadata_layout_component.rb +10 -0
  88. data/app/components/blacklight/icons/bookmark_component.rb +12 -0
  89. data/app/components/blacklight/icons/collection_component.rb +14 -0
  90. data/app/components/blacklight/icons/compact_component.rb +12 -0
  91. data/app/components/blacklight/icons/container_component.rb +14 -0
  92. data/app/components/blacklight/icons/ead_component.rb +12 -0
  93. data/app/components/blacklight/icons/file_component.rb +14 -0
  94. data/app/components/blacklight/icons/folder_component.rb +12 -0
  95. data/app/components/blacklight/icons/online_component.rb +15 -0
  96. data/app/components/blacklight/icons/pdf_component.rb +12 -0
  97. data/app/components/blacklight/icons/repository_component.rb +12 -0
  98. data/app/controllers/arclight/repositories_controller.rb +4 -4
  99. data/app/{controllers/concerns → helpers}/arclight/ead_format_helpers.rb +91 -7
  100. data/app/helpers/arclight/field_config_helpers.rb +26 -0
  101. data/app/helpers/arclight_helper.rb +24 -251
  102. data/app/models/arclight/document_downloads.rb +12 -15
  103. data/app/models/arclight/parent.rb +6 -8
  104. data/app/models/arclight/parents.rb +7 -4
  105. data/app/models/arclight/requests/aeon_external_request.rb +1 -1
  106. data/app/models/arclight/requests/aeon_web_ead.rb +8 -3
  107. data/app/models/arclight/requests/google_form.rb +8 -3
  108. data/app/models/concerns/arclight/catalog.rb +25 -15
  109. data/app/models/concerns/arclight/search_behavior.rb +12 -26
  110. data/app/models/concerns/arclight/solr_document.rb +80 -73
  111. data/app/presenters/arclight/show_presenter.rb +34 -7
  112. data/app/views/arclight/_requests.html.erb +2 -2
  113. data/app/views/arclight/repositories/_repository.html.erb +21 -40
  114. data/app/views/arclight/repositories/index.html.erb +3 -1
  115. data/app/views/arclight/repositories/show.html.erb +4 -6
  116. data/app/views/arclight/requests/_aeon_external_request_endpoint.html.erb +4 -5
  117. data/app/views/arclight/requests/_aeon_web_ead.html.erb +3 -4
  118. data/app/views/arclight/requests/_google_form.html.erb +4 -6
  119. data/app/views/catalog/_document_list.html.erb +8 -0
  120. data/app/views/catalog/_group.html.erb +2 -19
  121. data/app/views/catalog/_group_toggle.html.erb +1 -1
  122. data/app/views/catalog/_search_results_header.html.erb +3 -0
  123. data/app/views/catalog/hierarchy.html.erb +19 -0
  124. data/app/views/catalog/index.html.erb +16 -7
  125. data/app/views/shared/_breadcrumbs.html.erb +2 -14
  126. data/app/views/shared/_main_menu_links.html.erb +1 -1
  127. data/arclight.gemspec +16 -14
  128. data/config/breadcrumbs.rb +24 -0
  129. data/config/i18n-tasks.yml +9 -8
  130. data/config/importmap.rb +3 -0
  131. data/config/locales/arclight.en.yml +31 -30
  132. data/config/routes.rb +1 -1
  133. data/docker-compose.yml +16 -0
  134. data/lib/arclight/digital_object.rb +2 -1
  135. data/lib/arclight/engine.rb +18 -37
  136. data/lib/arclight/exceptions.rb +1 -0
  137. data/lib/arclight/hash_absolute_xpath.rb +2 -1
  138. data/lib/arclight/normalized_date.rb +5 -10
  139. data/lib/arclight/normalized_id.rb +6 -2
  140. data/lib/arclight/normalized_title.rb +2 -0
  141. data/lib/arclight/repository.rb +46 -57
  142. data/lib/arclight/routes/hierarchy.rb +19 -0
  143. data/lib/arclight/routes.rb +8 -0
  144. data/lib/arclight/traject/ead2_component_config.rb +335 -0
  145. data/lib/arclight/traject/ead2_config.rb +120 -299
  146. data/lib/arclight/version.rb +1 -1
  147. data/lib/arclight/year_range.rb +4 -3
  148. data/lib/arclight.rb +6 -1
  149. data/lib/generators/arclight/install_generator.rb +94 -21
  150. data/lib/generators/arclight/templates/arclight.scss +6 -3
  151. data/lib/generators/arclight/templates/catalog_controller.rb +193 -165
  152. data/lib/generators/arclight/templates/config/locales/arclight.en.yml +61 -0
  153. data/lib/generators/arclight/templates/config/repositories.yml +22 -29
  154. data/lib/tasks/index.rake +14 -17
  155. data/package.json +13 -14
  156. data/solr/conf/schema.xml +49 -39
  157. data/solr/conf/solrconfig.xml +78 -58
  158. data/tasks/arclight.rake +17 -9
  159. data/template.rb +6 -7
  160. metadata +149 -123
  161. data/.babelrc +0 -3
  162. data/app/assets/javascripts/arclight/collection_navigation.js +0 -100
  163. data/app/assets/javascripts/arclight/collection_scrollspy.js +0 -6
  164. data/app/assets/javascripts/arclight/context_navigation.js +0 -351
  165. data/app/assets/javascripts/arclight/oembed_viewer.js +0 -46
  166. data/app/assets/javascripts/arclight/truncator.js.erb +0 -29
  167. data/app/assets/stylesheets/arclight/bootstrap_overrides.scss +0 -26
  168. data/app/assets/stylesheets/arclight/modules/context_navigation.scss +0 -75
  169. data/app/controllers/concerns/arclight/field_config_helpers.rb +0 -102
  170. data/app/factories/blacklight_field_configuration_factory.rb +0 -30
  171. data/app/views/arclight/repositories/_in_person_repository.html.erb +0 -19
  172. data/app/views/arclight/repositories/_repository_contact.html.erb +0 -9
  173. data/app/views/arclight/viewers/_oembed.html.erb +0 -8
  174. data/app/views/catalog/_access_contents.html.erb +0 -15
  175. data/app/views/catalog/_arclight_abstract_or_scope.html.erb +0 -5
  176. data/app/views/catalog/_arclight_bookmark_control.html.erb +0 -38
  177. data/app/views/catalog/_arclight_document_header_icon.html.erb +0 -1
  178. data/app/views/catalog/_arclight_document_index_header.html.erb +0 -13
  179. data/app/views/catalog/_arclight_document_index_header_hierarchy_default.html.erb +0 -0
  180. data/app/views/catalog/_arclight_document_index_header_online_contents_default.html.erb +0 -0
  181. data/app/views/catalog/_arclight_index_compact_default.html.erb +0 -22
  182. data/app/views/catalog/_arclight_index_default.html.erb +0 -45
  183. data/app/views/catalog/_arclight_index_group_document_compact_default.html.erb +0 -19
  184. data/app/views/catalog/_arclight_index_group_document_default.html.erb +0 -18
  185. data/app/views/catalog/_arclight_online_content_indicator.html.erb +0 -3
  186. data/app/views/catalog/_arclight_rangelimit.html.erb +0 -24
  187. data/app/views/catalog/_arclight_viewer_default.html.erb +0 -1
  188. data/app/views/catalog/_collection_contents.html.erb +0 -4
  189. data/app/views/catalog/_collection_context.html.erb +0 -15
  190. data/app/views/catalog/_collection_context_nav.html.erb +0 -12
  191. data/app/views/catalog/_collection_online_contents.html.erb +0 -17
  192. data/app/views/catalog/_component_context.html.erb +0 -5
  193. data/app/views/catalog/_containers.html.erb +0 -3
  194. data/app/views/catalog/_context_card.html.erb +0 -27
  195. data/app/views/catalog/_context_sidebar.html.erb +0 -8
  196. data/app/views/catalog/_custom_metadata.html.erb +0 -16
  197. data/app/views/catalog/_document_downloads.html.erb +0 -14
  198. data/app/views/catalog/_group_header_compact_default.html.erb +0 -15
  199. data/app/views/catalog/_group_header_default.html.erb +0 -20
  200. data/app/views/catalog/_home.html.erb +0 -1
  201. data/app/views/catalog/_index_breadcrumb_default.html.erb +0 -6
  202. data/app/views/catalog/_index_collection_context_default.html.erb +0 -53
  203. data/app/views/catalog/_index_default.html.erb +0 -17
  204. data/app/views/catalog/_index_header.html.erb +0 -7
  205. data/app/views/catalog/_index_header_online_contents_default.html.erb +0 -1
  206. data/app/views/catalog/_index_online_contents_default.html.erb +0 -6
  207. data/app/views/catalog/_online_content_label.html.erb +0 -5
  208. data/app/views/catalog/_search_form.html.erb +0 -34
  209. data/app/views/catalog/_search_results.html.erb +0 -28
  210. data/app/views/catalog/_show_actions_box_default.html.erb +0 -27
  211. data/app/views/catalog/_show_breadcrumbs_default.html.erb +0 -6
  212. data/app/views/catalog/_show_collection.html.erb +0 -66
  213. data/app/views/catalog/_show_default.html.erb +0 -70
  214. data/app/views/catalog/_show_upper_metadata_collection.html.erb +0 -1
  215. data/app/views/catalog/_show_upper_metadata_default.html.erb +0 -14
  216. data/app/views/catalog/_sort_and_per_page.html.erb +0 -8
  217. data/app/views/catalog/_within_collection_dropdown.html.erb +0 -26
  218. data/app/views/layouts/catalog_result.html.erb +0 -7
  219. data/app/views/shared/_context_sidebar.html.erb +0 -8
  220. data/app/views/shared/_header_navbar.html.erb +0 -61
  221. data/app/views/shared/_show_breadcrumbs.html.erb +0 -27
  222. data/lib/arclight/viewer.rb +0 -45
  223. data/lib/arclight/viewers/oembed.rb +0 -57
  224. data/solr/conf/scripts.conf +0 -24
  225. data/vendor/assets/javascripts/responsiveTruncator.js +0 -69
  226. data/vendor/assets/javascripts/stickyfill.js +0 -480
  227. /data/app/assets/images/{blacklight → arclight}/logo.png +0 -0
@@ -0,0 +1,335 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ require 'traject'
5
+ require 'traject/nokogiri_reader'
6
+ require 'traject_plus'
7
+ require 'traject_plus/macros'
8
+ require 'arclight/level_label'
9
+ require 'arclight/normalized_date'
10
+ require 'arclight/normalized_title'
11
+ require 'active_model/conversion' ## Needed for Arclight::Repository
12
+ require 'active_support/core_ext/array/wrap'
13
+ require 'arclight/digital_object'
14
+ require 'arclight/year_range'
15
+ require 'arclight/missing_id_strategy'
16
+ require 'arclight/traject/nokogiri_namespaceless_reader'
17
+
18
+ # rubocop:disable Style/MixinUsage
19
+ extend TrajectPlus::Macros
20
+ # rubocop:enable Style/MixinUsage
21
+
22
+ settings do
23
+ # provide 'root' # the root EAD collection indexing context
24
+ # provide 'parent' # the immediate parent component (or collection) indexing context
25
+ # provide 'counter' # a global component counter to provide a global sort order for nested components
26
+ # provide 'depth' # the current nesting depth of the component
27
+ provide 'component_traject_config', __FILE__
28
+ provide 'date_normalizer', 'Arclight::NormalizedDate'
29
+ provide 'title_normalizer', 'Arclight::NormalizedTitle'
30
+ provide 'reader_class_name', 'Arclight::Traject::NokogiriNamespacelessReader'
31
+ provide 'logger', Logger.new($stderr)
32
+ provide 'component_identifier_format', '%<root_id>s_%<ref_id>s'
33
+ end
34
+
35
+ NAME_ELEMENTS = %w[corpname famname name persname].freeze
36
+
37
+ SEARCHABLE_NOTES_FIELDS = %w[
38
+ accessrestrict
39
+ accruals
40
+ altformavail
41
+ appraisal
42
+ arrangement
43
+ bibliography
44
+ bioghist
45
+ custodhist
46
+ fileplan
47
+ note
48
+ odd
49
+ originalsloc
50
+ otherfindaid
51
+ phystech
52
+ prefercite
53
+ processinfo
54
+ relatedmaterial
55
+ scopecontent
56
+ separatedmaterial
57
+ userestrict
58
+ ].freeze
59
+
60
+ DID_SEARCHABLE_NOTES_FIELDS = %w[
61
+ abstract
62
+ materialspec
63
+ physloc
64
+ note
65
+ ].freeze
66
+
67
+ # ==================
68
+ # Component elements
69
+ #
70
+ # NOTE: All fields should be stored in Solr
71
+ # ==================
72
+ to_field 'ref_ssi' do |record, accumulator, context|
73
+ next if context.output_hash['ref_ssi']
74
+
75
+ accumulator << if record.attribute('id').blank?
76
+ strategy = Arclight::MissingIdStrategy.selected
77
+ hexdigest = strategy.new(record).to_hexdigest
78
+ parent_id = settings[:parent].output_hash['id'].first
79
+ root_id = settings[:root].output_hash['id'].first
80
+ logger.warn('MISSING ID WARNING') do
81
+ [
82
+ "A component in #{parent_id} did not have an ID so one was minted using the #{strategy} strategy.",
83
+ "The ID of this document will be #{root_id}#{hexdigest}."
84
+ ].join(' ')
85
+ end
86
+ record['id'] = hexdigest
87
+ hexdigest
88
+ else
89
+ record.attribute('id')&.value&.strip&.gsub('.', '-') # rubocop:disable Style/SafeNavigationChainLength
90
+ end
91
+ end
92
+ to_field 'ref_ssm' do |_record, accumulator, context|
93
+ accumulator.concat context.output_hash['ref_ssi']
94
+ end
95
+
96
+ to_field 'id' do |_record, accumulator, context|
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)
105
+ end
106
+
107
+ to_field 'title_filing_ssi', extract_xpath('./did/unittitle'), first_only
108
+ to_field 'title_ssm', extract_xpath('./did/unittitle')
109
+ to_field 'title_tesim', extract_xpath('./did/unittitle')
110
+
111
+ to_field 'unitdate_bulk_ssim', extract_xpath('./did/unitdate[@type="bulk"]')
112
+ to_field 'unitdate_inclusive_ssm', extract_xpath('./did/unitdate[@type="inclusive"]')
113
+ to_field 'unitdate_other_ssim', extract_xpath('./did/unitdate[not(@type)]')
114
+
115
+ to_field 'normalized_date_ssm' do |_record, accumulator, context|
116
+ accumulator << settings['date_normalizer'].constantize.new(
117
+ context.output_hash['unitdate_inclusive_ssm'],
118
+ context.output_hash['unitdate_bulk_ssim'],
119
+ context.output_hash['unitdate_other_ssim']
120
+ ).to_s
121
+ end
122
+
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
127
+ end
128
+
129
+ to_field 'component_level_isim' do |_record, accumulator|
130
+ accumulator << (settings[:depth] || 1)
131
+ end
132
+
133
+ to_field 'parent_ssim' do |_record, accumulator, _context|
134
+ accumulator.concat(settings[:parent].output_hash['parent_ssim'] || [])
135
+ accumulator.concat settings[:parent].output_hash['ref_ssi'] || settings[:parent].output_hash['id']
136
+ end
137
+
138
+ to_field 'parent_ssi' do |_record, accumulator, _context|
139
+ accumulator.concat settings[:parent].output_hash['ref_ssi'] || settings[:parent].output_hash['id']
140
+ end
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
+
147
+ to_field 'parent_unittitles_ssm' do |_rec, accumulator, _context|
148
+ accumulator.concat(settings[:parent].output_hash['parent_unittitles_ssm'] || [])
149
+ accumulator.concat settings[:parent].output_hash['normalized_title_ssm'] || []
150
+ end
151
+
152
+ to_field 'parent_unittitles_tesim' do |_record, accumulator, context|
153
+ accumulator.concat context.output_hash['parent_unittitles_ssm']
154
+ end
155
+
156
+ to_field 'parent_levels_ssm' do |_record, accumulator, _context|
157
+ ## Top level document
158
+ accumulator.concat settings[:parent].output_hash['parent_levels_ssm'] || []
159
+ accumulator.concat settings[:parent].output_hash['level_ssm'] || []
160
+ end
161
+
162
+ to_field 'unitid_ssm', extract_xpath('./did/unitid')
163
+ to_field 'repository_ssim' do |_record, accumulator, _context|
164
+ accumulator << settings[:root].clipboard[:repository]
165
+ end
166
+
167
+ to_field 'collection_ssim' do |_record, accumulator, _context|
168
+ accumulator.concat settings[:root].output_hash['normalized_title_ssm']
169
+ end
170
+
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')
205
+
206
+ to_field 'creator_ssim', extract_xpath('./did/origination')
207
+ to_field 'creator_sort' do |record, accumulator|
208
+ accumulator << record.xpath('./did/origination').map(&:text).join(', ')
209
+ end
210
+ to_field 'has_online_content_ssim', extract_xpath('.//dao') do |_record, accumulator|
211
+ accumulator.replace([accumulator.any?])
212
+ end
213
+ to_field 'child_component_count_isi' do |record, accumulator|
214
+ accumulator << record.xpath('c|c01|c02|c03|c04|c05|c06|c07|c08|c09|c10|c11|c12').count
215
+ end
216
+
217
+ to_field 'ref_ssm' do |record, accumulator|
218
+ accumulator << record.attribute('id')
219
+ end
220
+
221
+ to_field 'level_ssm' do |record, accumulator|
222
+ level = record.attribute('level')&.value
223
+ other_level = record.attribute('otherlevel')&.value
224
+ accumulator << Arclight::LevelLabel.new(level, other_level).to_s
225
+ end
226
+
227
+ to_field 'level_ssim' do |_record, accumulator, context|
228
+ next unless context.output_hash['level_ssm']
229
+
230
+ accumulator.concat context.output_hash['level_ssm']&.map(&:capitalize)
231
+ end
232
+
233
+ to_field 'sort_isi' do |_record, accumulator, _context|
234
+ accumulator.replace([settings[:counter].increment])
235
+ end
236
+
237
+ # Get the <accessrestrict> from the closest ancestor that has one (includes top-level)
238
+ to_field 'parent_access_restrict_tesm' do |record, accumulator|
239
+ accumulator.concat Array
240
+ .wrap(record.xpath('(./ancestor::*/accessrestrict)[last()]/*[local-name()!="head"]')
241
+ .map(&:text))
242
+ end
243
+
244
+ # Get the <userestrict> from self OR the closest ancestor that has one (includes top-level)
245
+ to_field 'parent_access_terms_tesm' do |record, accumulator|
246
+ accumulator.concat Array
247
+ .wrap(record.xpath('(./ancestor-or-self::*/userestrict)[last()]/*[local-name()!="head"]')
248
+ .map(&:text))
249
+ end
250
+
251
+ to_field 'digital_objects_ssm', extract_xpath('./dao|./did/dao', to_text: false) do |_record, accumulator|
252
+ accumulator.map! do |dao|
253
+ label = dao.attributes['title']&.value ||
254
+ dao.xpath('daodesc/p')&.text
255
+ href = (dao.attributes['href'] || dao.attributes['xlink:href'])&.value
256
+ Arclight::DigitalObject.new(label: label, href: href).to_json
257
+ end
258
+ end
259
+
260
+ to_field 'date_range_isim', extract_xpath('./did/unitdate/@normal', to_text: false) do |_record, accumulator|
261
+ range = Arclight::YearRange.new
262
+ next range.years if accumulator.blank?
263
+
264
+ ranges = accumulator.map(&:to_s)
265
+ range << range.parse_ranges(ranges)
266
+ accumulator.replace range.years
267
+ end
268
+
269
+ NAME_ELEMENTS.map do |selector|
270
+ to_field 'names_ssim', extract_xpath("./controlaccess/#{selector}"), unique
271
+ to_field 'names_ssim', extract_xpath(".//#{selector}"), unique
272
+ to_field "#{selector}_ssim", extract_xpath(".//#{selector}")
273
+ end
274
+
275
+ to_field 'geogname_ssim', extract_xpath('./controlaccess/geogname')
276
+ to_field 'geogname_ssm', extract_xpath('./controlaccess/geogname')
277
+ to_field 'places_ssim', extract_xpath('./controlaccess/geogname')
278
+
279
+ to_field 'access_subjects_ssim', extract_xpath('./controlaccess', to_text: false) do |_record, accumulator|
280
+ accumulator.map! do |element|
281
+ %w[subject function occupation genreform].map do |selector|
282
+ element.xpath(".//#{selector}").map(&:text)
283
+ end
284
+ end.flatten!
285
+ end
286
+
287
+ to_field 'access_subjects_ssm' do |_record, accumulator, context|
288
+ accumulator.concat(context.output_hash.fetch('access_subjects_ssim', []))
289
+ end
290
+
291
+ to_field 'acqinfo_ssim', extract_xpath('./acqinfo/*[local-name()!="head"]')
292
+ to_field 'acqinfo_ssim', extract_xpath('./descgrp/acqinfo/*[local-name()!="head"]')
293
+
294
+ to_field 'language_ssim', extract_xpath('./did/langmaterial')
295
+ to_field 'containers_ssim' do |record, accumulator|
296
+ record.xpath('./did/container').each do |node|
297
+ accumulator << [node.attribute('type'), node.text].join(' ').strip
298
+ end
299
+ end
300
+
301
+ SEARCHABLE_NOTES_FIELDS.map do |selector|
302
+ to_field "#{selector}_html_tesm", extract_xpath("./#{selector}/*[local-name()!='head']", to_text: false)
303
+ to_field "#{selector}_heading_ssm", extract_xpath("./#{selector}/head")
304
+ to_field "#{selector}_tesim", extract_xpath("./#{selector}/*[local-name()!='head']")
305
+ end
306
+ DID_SEARCHABLE_NOTES_FIELDS.map do |selector|
307
+ to_field "#{selector}_html_tesm", extract_xpath("./did/#{selector}", to_text: false)
308
+ to_field "#{selector}_tesim", extract_xpath("./did/#{selector}")
309
+ end
310
+
311
+ # =============================
312
+ # Each component child document
313
+ # <c> <c01> <c12>
314
+ # =============================
315
+
316
+ to_field 'components' do |record, accumulator, context|
317
+ child_components = record.xpath('c|c01|c02|c03|c04|c05|c06|c07|c08|c09|c10|c11|c12')
318
+ component_indexer = Traject::Indexer::NokogiriIndexer.new.tap do |i|
319
+ i.settings do
320
+ provide :parent, context
321
+ provide :root, context.settings[:root]
322
+ provide :counter, context.settings[:counter]
323
+ provide :depth, context.settings[:depth].to_i + 1
324
+ provide :component_traject_config, context.settings[:component_traject_config]
325
+ provide :component_identifier_format, context.settings[:component_identifier_format]
326
+ end
327
+
328
+ i.load_config_file(context.settings[:component_traject_config])
329
+ end
330
+
331
+ child_components.each do |child_component|
332
+ output = component_indexer.map_record(child_component)
333
+ accumulator << output if output.keys.any?
334
+ end
335
+ end