arclight 0.2.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 (246) hide show
  1. checksums.yaml +4 -4
  2. data/.all-contributorsrc +450 -0
  3. data/.codeclimate.yml +5 -0
  4. data/.eslintrc +14 -4
  5. data/.github/workflows/ruby.yml +51 -0
  6. data/.gitignore +1 -0
  7. data/.rspec +0 -1
  8. data/.rubocop.yml +504 -8
  9. data/.rubocop_todo.yml +62 -199
  10. data/.solr_wrapper +2 -1
  11. data/CONTRIBUTING.md +5 -5
  12. data/CONTRIBUTORS.md +79 -0
  13. data/Gemfile +15 -10
  14. data/README.md +36 -34
  15. data/Rakefile +0 -1
  16. data/app/assets/images/blacklight/bookmark.svg +1 -0
  17. data/app/assets/images/blacklight/collection.svg +5 -0
  18. data/app/assets/images/blacklight/compact.svg +1 -25
  19. data/app/assets/images/blacklight/container.svg +5 -0
  20. data/app/assets/images/blacklight/ead.svg +1 -0
  21. data/app/assets/images/blacklight/file.svg +5 -0
  22. data/app/assets/images/blacklight/folder.svg +1 -0
  23. data/app/assets/images/blacklight/list.svg +1 -0
  24. data/app/assets/images/blacklight/minus.svg +1 -0
  25. data/app/assets/images/blacklight/online.svg +5 -0
  26. data/app/assets/images/blacklight/pdf.svg +1 -0
  27. data/app/assets/images/blacklight/plus.svg +1 -0
  28. data/app/assets/images/blacklight/repository.svg +1 -0
  29. data/app/assets/javascripts/arclight/arclight.js +4 -10
  30. data/app/assets/javascripts/arclight/oembed_controller.js +58 -0
  31. data/app/assets/javascripts/arclight/truncate_controller.js +27 -0
  32. data/app/assets/stylesheets/arclight/application.scss +4 -4
  33. data/app/assets/stylesheets/arclight/build.scss +4 -0
  34. data/app/assets/stylesheets/arclight/modules/hierarchy_and_online_contents.scss +117 -146
  35. data/app/assets/stylesheets/arclight/modules/highlights.scss +2 -1
  36. data/app/assets/stylesheets/arclight/modules/icons.scss +20 -0
  37. data/app/assets/stylesheets/arclight/modules/layout.scss +183 -40
  38. data/app/assets/stylesheets/arclight/modules/mastheads.scss +24 -75
  39. data/app/assets/stylesheets/arclight/modules/repositories.scss +3 -7
  40. data/app/assets/stylesheets/arclight/modules/repository_card.scss +5 -50
  41. data/app/assets/stylesheets/arclight/modules/search_form.scss +9 -0
  42. data/app/assets/stylesheets/arclight/modules/search_results.scss +197 -26
  43. data/app/assets/stylesheets/arclight/modules/show_collection.scss +21 -69
  44. data/app/assets/stylesheets/arclight/modules/truncator.scss +58 -0
  45. data/app/assets/stylesheets/arclight/responsive.scss +13 -0
  46. data/app/assets/stylesheets/arclight/variables.scss +25 -2
  47. data/app/components/arclight/access_component.html.erb +14 -0
  48. data/app/components/arclight/access_component.rb +25 -0
  49. data/app/components/arclight/bookmark_component.html.erb +25 -0
  50. data/app/components/arclight/bookmark_component.rb +9 -0
  51. data/app/components/arclight/breadcrumb_component.rb +50 -0
  52. data/app/components/arclight/breadcrumbs_hierarchy_component.html.erb +34 -0
  53. data/app/components/arclight/breadcrumbs_hierarchy_component.rb +25 -0
  54. data/app/components/arclight/collection_context_component.html.erb +12 -0
  55. data/app/components/arclight/collection_context_component.rb +27 -0
  56. data/app/components/arclight/collection_info_component.html.erb +28 -0
  57. data/app/components/arclight/collection_info_component.rb +26 -0
  58. data/app/components/arclight/collection_sidebar_component.html.erb +15 -0
  59. data/app/components/arclight/collection_sidebar_component.rb +39 -0
  60. data/app/{views/catalog/_search_results_repository.html.erb → components/arclight/constraints_component.html.erb} +2 -1
  61. data/app/components/arclight/constraints_component.rb +17 -0
  62. data/app/components/arclight/document_collection_context_component.html.erb +29 -0
  63. data/app/components/arclight/document_collection_context_component.rb +28 -0
  64. data/app/components/arclight/document_collection_hierarchy_component.html.erb +50 -0
  65. data/app/components/arclight/document_collection_hierarchy_component.rb +62 -0
  66. data/app/components/arclight/document_component.html.erb +39 -0
  67. data/app/components/arclight/document_component.rb +49 -0
  68. data/app/components/arclight/document_components_hierarchy_component.html.erb +20 -0
  69. data/app/components/arclight/document_components_hierarchy_component.rb +34 -0
  70. data/app/components/arclight/document_download_component.html.erb +24 -0
  71. data/app/components/arclight/document_download_component.rb +71 -0
  72. data/app/components/arclight/embed_component.html.erb +10 -0
  73. data/app/components/arclight/embed_component.rb +43 -0
  74. data/app/components/arclight/expand_hierarchy_button_component.html.erb +5 -0
  75. data/app/components/arclight/expand_hierarchy_button_component.rb +16 -0
  76. data/app/components/arclight/group_component.html.erb +34 -0
  77. data/app/components/arclight/group_component.rb +23 -0
  78. data/app/components/arclight/header_component.html.erb +5 -0
  79. data/app/components/arclight/header_component.rb +10 -0
  80. data/app/components/arclight/index_metadata_field_component.html.erb +16 -0
  81. data/app/components/arclight/index_metadata_field_component.rb +20 -0
  82. data/app/components/arclight/masthead_component.html.erb +17 -0
  83. data/app/components/arclight/masthead_component.rb +10 -0
  84. data/app/components/arclight/metadata_section_component.html.erb +13 -0
  85. data/app/components/arclight/metadata_section_component.rb +22 -0
  86. data/app/components/arclight/oembed_viewer_component.html.erb +5 -0
  87. data/app/components/arclight/oembed_viewer_component.rb +16 -0
  88. data/app/components/arclight/online_content_filter_component.html.erb +15 -0
  89. data/app/components/arclight/online_content_filter_component.rb +17 -0
  90. data/app/components/arclight/online_status_indicator_component.rb +19 -0
  91. data/app/components/arclight/repository_breadcrumb_component.html.erb +10 -0
  92. data/app/components/arclight/repository_breadcrumb_component.rb +17 -0
  93. data/app/components/arclight/repository_location_component.html.erb +20 -0
  94. data/app/components/arclight/repository_location_component.rb +10 -0
  95. data/app/components/arclight/search_bar_component.html.erb +27 -0
  96. data/app/components/arclight/search_bar_component.rb +32 -0
  97. data/app/components/arclight/search_result_breadcrumbs_component.html.erb +6 -0
  98. data/app/components/arclight/search_result_breadcrumbs_component.rb +37 -0
  99. data/app/components/arclight/search_result_component.html.erb +20 -0
  100. data/app/components/arclight/search_result_component.rb +18 -0
  101. data/app/components/arclight/search_result_title_component.html.erb +15 -0
  102. data/app/components/arclight/search_result_title_component.rb +15 -0
  103. data/app/components/arclight/sidebar_component.html.erb +9 -0
  104. data/app/components/arclight/sidebar_component.rb +19 -0
  105. data/app/components/arclight/upper_metadata_layout_component.rb +10 -0
  106. data/app/components/blacklight/icons/bookmark_component.rb +12 -0
  107. data/app/components/blacklight/icons/collection_component.rb +14 -0
  108. data/app/components/blacklight/icons/compact_component.rb +12 -0
  109. data/app/components/blacklight/icons/container_component.rb +14 -0
  110. data/app/components/blacklight/icons/ead_component.rb +12 -0
  111. data/app/components/blacklight/icons/file_component.rb +14 -0
  112. data/app/components/blacklight/icons/folder_component.rb +12 -0
  113. data/app/components/blacklight/icons/online_component.rb +15 -0
  114. data/app/components/blacklight/icons/pdf_component.rb +12 -0
  115. data/app/components/blacklight/icons/repository_component.rb +12 -0
  116. data/app/controllers/arclight/repositories_controller.rb +4 -4
  117. data/app/helpers/arclight/ead_format_helpers.rb +309 -0
  118. data/app/helpers/arclight/field_config_helpers.rb +26 -0
  119. data/app/helpers/arclight_helper.rb +55 -120
  120. data/app/models/arclight/document_downloads.rb +122 -0
  121. data/app/models/arclight/parent.rb +9 -9
  122. data/app/models/arclight/parents.rb +9 -4
  123. data/app/models/arclight/requests/aeon_external_request.rb +42 -0
  124. data/app/models/arclight/requests/aeon_web_ead.rb +52 -0
  125. data/app/models/arclight/requests/google_form.rb +8 -3
  126. data/app/models/concerns/arclight/catalog.rb +33 -11
  127. data/app/models/concerns/arclight/search_behavior.rb +22 -21
  128. data/app/models/concerns/arclight/solr_document.rb +88 -59
  129. data/app/presenters/arclight/show_presenter.rb +34 -7
  130. data/app/views/arclight/_requests.html.erb +7 -0
  131. data/app/views/arclight/repositories/_repository.html.erb +22 -41
  132. data/app/views/arclight/repositories/index.html.erb +5 -0
  133. data/app/views/arclight/repositories/show.html.erb +7 -8
  134. data/app/views/arclight/requests/_aeon_external_request_endpoint.html.erb +8 -0
  135. data/app/views/arclight/requests/_aeon_web_ead.html.erb +6 -0
  136. data/app/views/arclight/requests/_google_form.html.erb +4 -5
  137. data/app/views/catalog/_document_list.html.erb +8 -0
  138. data/app/views/catalog/_group.html.erb +4 -0
  139. data/app/views/catalog/_group_toggle.html.erb +10 -0
  140. data/app/views/catalog/_search_results_header.html.erb +3 -0
  141. data/app/views/catalog/hierarchy.html.erb +19 -0
  142. data/app/views/catalog/index.html.erb +16 -7
  143. data/app/views/shared/_breadcrumbs.html.erb +3 -15
  144. data/app/views/shared/_main_menu_links.html.erb +1 -1
  145. data/arclight.gemspec +18 -17
  146. data/config/breadcrumbs.rb +24 -0
  147. data/config/i18n-tasks.yml +11 -9
  148. data/config/importmap.rb +3 -0
  149. data/config/locales/arclight.en.yml +68 -34
  150. data/config/routes.rb +1 -1
  151. data/docker-compose.yml +16 -0
  152. data/lib/arclight/digital_object.rb +2 -1
  153. data/lib/arclight/engine.rb +22 -31
  154. data/lib/arclight/exceptions.rb +1 -0
  155. data/lib/arclight/hash_absolute_xpath.rb +13 -8
  156. data/lib/arclight/level_label.rb +46 -0
  157. data/lib/arclight/normalized_date.rb +5 -10
  158. data/lib/arclight/normalized_id.rb +7 -2
  159. data/lib/arclight/normalized_title.rb +3 -0
  160. data/lib/arclight/repository.rb +70 -28
  161. data/lib/arclight/routes/hierarchy.rb +19 -0
  162. data/lib/arclight/routes.rb +8 -0
  163. data/lib/arclight/traject/ead2_component_config.rb +335 -0
  164. data/lib/arclight/traject/ead2_config.rb +172 -331
  165. data/lib/arclight/traject/nokogiri_namespaceless_reader.rb +22 -0
  166. data/lib/arclight/version.rb +1 -1
  167. data/lib/arclight/year_range.rb +13 -4
  168. data/lib/arclight.rb +6 -1
  169. data/lib/generators/arclight/install_generator.rb +100 -10
  170. data/lib/generators/arclight/templates/arclight.scss +6 -3
  171. data/lib/generators/arclight/templates/catalog_controller.rb +237 -179
  172. data/lib/generators/arclight/templates/config/downloads.yml +12 -0
  173. data/lib/generators/arclight/templates/config/locales/arclight.en.yml +61 -0
  174. data/lib/generators/arclight/templates/config/repositories.yml +42 -31
  175. data/lib/generators/arclight/update_generator.rb +1 -1
  176. data/lib/tasks/index.rake +25 -30
  177. data/package.json +15 -9
  178. data/solr/conf/schema.xml +97 -328
  179. data/solr/conf/solrconfig.xml +99 -164
  180. data/tasks/arclight.rake +18 -9
  181. data/template.rb +6 -7
  182. metadata +188 -129
  183. data/.travis.yml +0 -28
  184. data/app/assets/javascripts/arclight/collection_context.js +0 -18
  185. data/app/assets/javascripts/arclight/collection_navigation.js +0 -117
  186. data/app/assets/javascripts/arclight/collection_scrollspy.js +0 -6
  187. data/app/assets/javascripts/arclight/component_ancestors.js +0 -56
  188. data/app/assets/javascripts/arclight/oembed_viewer.js +0 -46
  189. data/app/assets/javascripts/arclight/search_results.js +0 -15
  190. data/app/assets/javascripts/arclight/truncator.js.erb +0 -23
  191. data/app/assets/stylesheets/arclight/bootstrap_overrides.scss +0 -3
  192. data/app/assets/stylesheets/arclight/modules/sidebar.scss +0 -21
  193. data/app/controllers/concerns/arclight/field_config_helpers.rb +0 -86
  194. data/app/factories/blacklight_field_configuration_factory.rb +0 -29
  195. data/app/views/arclight/repositories/_in_person_repository.html.erb +0 -19
  196. data/app/views/arclight/viewers/_oembed.html.erb +0 -7
  197. data/app/views/catalog/_arclight_document_index_header.html.erb +0 -13
  198. data/app/views/catalog/_arclight_document_index_header_online_contents_default.html.erb +0 -0
  199. data/app/views/catalog/_arclight_index_compact_default.html.erb +0 -15
  200. data/app/views/catalog/_arclight_online_content_indicator.html.erb +0 -5
  201. data/app/views/catalog/_arclight_rangelimit.html.erb +0 -24
  202. data/app/views/catalog/_arclight_viewer_default.html.erb +0 -1
  203. data/app/views/catalog/_collection_contents.html.erb +0 -12
  204. data/app/views/catalog/_collection_count.html.erb +0 -7
  205. data/app/views/catalog/_collection_downloads.html.erb +0 -15
  206. data/app/views/catalog/_collection_online_contents.html.erb +0 -17
  207. data/app/views/catalog/_collection_overview.html.erb +0 -7
  208. data/app/views/catalog/_component_contents.html.erb +0 -16
  209. data/app/views/catalog/_component_overview.html.erb +0 -40
  210. data/app/views/catalog/_context_card.html.erb +0 -27
  211. data/app/views/catalog/_context_sidebar.html.erb +0 -8
  212. data/app/views/catalog/_custom_metadata.html.erb +0 -16
  213. data/app/views/catalog/_home.html.erb +0 -1
  214. data/app/views/catalog/_index_breadcrumb_default.html.erb +0 -3
  215. data/app/views/catalog/_index_default.html.erb +0 -17
  216. data/app/views/catalog/_index_header.html.erb +0 -7
  217. data/app/views/catalog/_index_header_hierarchy_default.html.erb +0 -42
  218. data/app/views/catalog/_index_header_online_contents_default.html.erb +0 -1
  219. data/app/views/catalog/_index_hierarchy_default.html.erb +0 -28
  220. data/app/views/catalog/_index_online_contents_default.html.erb +0 -6
  221. data/app/views/catalog/_results_histogram.html.erb +0 -15
  222. data/app/views/catalog/_search_results.html.erb +0 -31
  223. data/app/views/catalog/_show_breadcrumbs_default.html.erb +0 -21
  224. data/app/views/catalog/_show_collection.html.erb +0 -48
  225. data/app/views/catalog/_show_component_sidebar.html.erb +0 -12
  226. data/app/views/catalog/_show_default.html.erb +0 -42
  227. data/app/views/catalog/_show_sidebar.html.erb +0 -22
  228. data/app/views/catalog/_show_upper_metadata_collection.html.erb +0 -1
  229. data/app/views/catalog/_show_upper_metadata_default.html.erb +0 -14
  230. data/app/views/layouts/catalog_result.html.erb +0 -7
  231. data/app/views/shared/_context_sidebar.html.erb +0 -8
  232. data/app/views/shared/_header_navbar.html.erb +0 -65
  233. data/lib/arclight/custom_component.rb +0 -99
  234. data/lib/arclight/custom_document.rb +0 -93
  235. data/lib/arclight/indexer.rb +0 -9
  236. data/lib/arclight/shared_indexing_behavior.rb +0 -97
  237. data/lib/arclight/shared_terminology_behavior.rb +0 -65
  238. data/lib/arclight/solr_ead_indexer_ext.rb +0 -155
  239. data/lib/arclight/viewer.rb +0 -45
  240. data/lib/arclight/viewers/oembed.rb +0 -56
  241. data/lib/generators/arclight/templates/arclight.js +0 -2
  242. data/solr/conf/scripts.conf +0 -24
  243. data/vendor/assets/javascripts/responsiveTruncator.js +0 -69
  244. data/vendor/assets/javascripts/stickyfill.js +0 -480
  245. /data/app/assets/images/{blacklight → arclight}/logo.png +0 -0
  246. /data/{app/views/catalog/_arclight_document_index_header_hierarchy_default.html.erb → config/repositories.yml} +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