arclight 0.1.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 (247) hide show
  1. checksums.yaml +5 -5
  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 +2 -0
  7. data/.rspec +0 -1
  8. data/.rubocop.yml +503 -48
  9. data/.rubocop_todo.yml +122 -0
  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 +47 -32
  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 -9
  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 -3
  33. data/app/assets/stylesheets/arclight/build.scss +4 -0
  34. data/app/assets/stylesheets/arclight/modules/hierarchy_and_online_contents.scss +117 -136
  35. data/app/assets/stylesheets/arclight/modules/highlights.scss +3 -2
  36. data/app/assets/stylesheets/arclight/modules/icons.scss +20 -0
  37. data/app/assets/stylesheets/arclight/modules/layout.scss +203 -16
  38. data/app/assets/stylesheets/arclight/modules/mastheads.scss +27 -45
  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 +200 -25
  43. data/app/assets/stylesheets/arclight/modules/show_collection.scss +21 -61
  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 +22 -17
  146. data/config/breadcrumbs.rb +24 -0
  147. data/config/i18n-tasks.yml +134 -0
  148. data/config/importmap.rb +3 -0
  149. data/config/locales/arclight.en.yml +90 -55
  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 +23 -31
  154. data/lib/arclight/exceptions.rb +1 -0
  155. data/lib/arclight/hash_absolute_xpath.rb +62 -0
  156. data/lib/arclight/level_label.rb +46 -0
  157. data/lib/arclight/missing_id_strategy.rb +21 -0
  158. data/lib/arclight/normalized_date.rb +24 -20
  159. data/lib/arclight/normalized_id.rb +7 -2
  160. data/lib/arclight/normalized_title.rb +3 -0
  161. data/lib/arclight/repository.rb +72 -47
  162. data/lib/arclight/routes/hierarchy.rb +19 -0
  163. data/lib/arclight/routes.rb +8 -0
  164. data/lib/arclight/traject/ead2_component_config.rb +335 -0
  165. data/lib/arclight/traject/ead2_config.rb +316 -0
  166. data/lib/arclight/traject/nokogiri_namespaceless_reader.rb +22 -0
  167. data/lib/arclight/version.rb +1 -1
  168. data/lib/arclight/year_range.rb +13 -4
  169. data/lib/arclight.rb +6 -1
  170. data/lib/generators/arclight/install_generator.rb +110 -10
  171. data/lib/generators/arclight/templates/arclight.scss +6 -3
  172. data/lib/generators/arclight/templates/catalog_controller.rb +249 -173
  173. data/lib/generators/arclight/templates/config/downloads.yml +12 -0
  174. data/lib/generators/arclight/templates/config/locales/arclight.en.yml +61 -0
  175. data/lib/generators/arclight/templates/config/repositories.yml +42 -31
  176. data/lib/generators/arclight/update_generator.rb +1 -1
  177. data/lib/tasks/index.rake +25 -28
  178. data/package.json +15 -9
  179. data/solr/conf/schema.xml +101 -332
  180. data/solr/conf/solrconfig.xml +99 -164
  181. data/tasks/arclight.rake +22 -11
  182. data/template.rb +6 -11
  183. metadata +230 -105
  184. data/.travis.yml +0 -33
  185. data/app/assets/javascripts/arclight/collection_context.js +0 -18
  186. data/app/assets/javascripts/arclight/collection_navigation.js +0 -114
  187. data/app/assets/javascripts/arclight/collection_scrollspy.js +0 -6
  188. data/app/assets/javascripts/arclight/component_ancestors.js +0 -56
  189. data/app/assets/javascripts/arclight/oembed_viewer.js +0 -39
  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 -16
  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_document_show_header.html.erb +0 -15
  200. data/app/views/catalog/_arclight_document_show_header_collection.html.erb +0 -12
  201. data/app/views/catalog/_arclight_index_compact_default.html.erb +0 -15
  202. data/app/views/catalog/_arclight_online_content_indicator.html.erb +0 -5
  203. data/app/views/catalog/_arclight_rangelimit.html.erb +0 -24
  204. data/app/views/catalog/_arclight_viewer_default.html.erb +0 -1
  205. data/app/views/catalog/_collection_contents.html.erb +0 -12
  206. data/app/views/catalog/_collection_count.html.erb +0 -7
  207. data/app/views/catalog/_collection_downloads.html.erb +0 -17
  208. data/app/views/catalog/_collection_online_contents.html.erb +0 -17
  209. data/app/views/catalog/_collection_overview.html.erb +0 -7
  210. data/app/views/catalog/_component_overview.html.erb +0 -46
  211. data/app/views/catalog/_context_card.html.erb +0 -27
  212. data/app/views/catalog/_context_sidebar.html.erb +0 -8
  213. data/app/views/catalog/_custom_metadata.html.erb +0 -16
  214. data/app/views/catalog/_home.html.erb +0 -1
  215. data/app/views/catalog/_index_breadcrumb_default.html.erb +0 -3
  216. data/app/views/catalog/_index_default.html.erb +0 -17
  217. data/app/views/catalog/_index_header.html.erb +0 -7
  218. data/app/views/catalog/_index_header_hierarchy_default.html.erb +0 -42
  219. data/app/views/catalog/_index_header_online_contents_default.html.erb +0 -1
  220. data/app/views/catalog/_index_hierarchy_default.html.erb +0 -28
  221. data/app/views/catalog/_index_online_contents_default.html.erb +0 -6
  222. data/app/views/catalog/_results_histogram.html.erb +0 -10
  223. data/app/views/catalog/_search_results.html.erb +0 -31
  224. data/app/views/catalog/_search_within_form.html.erb +0 -16
  225. data/app/views/catalog/_show_breadcrumbs_default.html.erb +0 -7
  226. data/app/views/catalog/_show_collection.html.erb +0 -40
  227. data/app/views/catalog/_show_component_sidebar.html.erb +0 -12
  228. data/app/views/catalog/_show_default.html.erb +0 -32
  229. data/app/views/catalog/_show_header.html.erb +0 -5
  230. data/app/views/catalog/_show_sidebar.html.erb +0 -30
  231. data/app/views/layouts/catalog_result.html.erb +0 -7
  232. data/app/views/shared/_context_sidebar.html.erb +0 -8
  233. data/app/views/shared/_header_navbar.html.erb +0 -54
  234. data/lib/arclight/custom_component.rb +0 -98
  235. data/lib/arclight/custom_document.rb +0 -93
  236. data/lib/arclight/indexer.rb +0 -9
  237. data/lib/arclight/shared_indexing_behavior.rb +0 -97
  238. data/lib/arclight/shared_terminology_behavior.rb +0 -65
  239. data/lib/arclight/solr_ead_indexer_ext.rb +0 -159
  240. data/lib/arclight/viewer.rb +0 -45
  241. data/lib/arclight/viewers/oembed.rb +0 -56
  242. data/lib/generators/arclight/templates/arclight.js +0 -2
  243. data/solr/conf/scripts.conf +0 -24
  244. data/vendor/assets/javascripts/responsiveTruncator.js +0 -69
  245. data/vendor/assets/javascripts/stickyfill.js +0 -480
  246. /data/app/assets/images/{blacklight → arclight}/logo.png +0 -0
  247. /data/{app/views/catalog/_arclight_document_index_header_hierarchy_default.html.erb → config/repositories.yml} +0 -0
@@ -0,0 +1,316 @@
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_id'
10
+ require 'arclight/normalized_date'
11
+ require 'arclight/normalized_title'
12
+ require 'active_model/conversion' ## Needed for Arclight::Repository
13
+ require 'active_support/core_ext/array/wrap'
14
+ require 'arclight/digital_object'
15
+ require 'arclight/year_range'
16
+ require 'arclight/repository'
17
+ require 'arclight/traject/nokogiri_namespaceless_reader'
18
+
19
+ # rubocop:disable Style/MixinUsage
20
+ extend TrajectPlus::Macros
21
+ # rubocop:enable Style/MixinUsage
22
+
23
+ NAME_ELEMENTS = %w[corpname famname name persname].freeze
24
+
25
+ SEARCHABLE_NOTES_FIELDS = %w[
26
+ accessrestrict
27
+ accruals
28
+ altformavail
29
+ appraisal
30
+ arrangement
31
+ bibliography
32
+ bioghist
33
+ custodhist
34
+ fileplan
35
+ note
36
+ odd
37
+ originalsloc
38
+ otherfindaid
39
+ phystech
40
+ prefercite
41
+ processinfo
42
+ relatedmaterial
43
+ scopecontent
44
+ separatedmaterial
45
+ userestrict
46
+ ].freeze
47
+
48
+ DID_SEARCHABLE_NOTES_FIELDS = %w[
49
+ abstract
50
+ materialspec
51
+ physloc
52
+ note
53
+ ].freeze
54
+
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'
60
+ provide 'reader_class_name', 'Arclight::Traject::NokogiriNamespacelessReader'
61
+ provide 'solr_writer.commit_on_close', 'true'
62
+ provide 'repository', ENV.fetch('REPOSITORY_ID', nil)
63
+ provide 'logger', Logger.new($stderr)
64
+ end
65
+
66
+ each_record do |_record, context|
67
+ next unless settings['repository']
68
+
69
+ context.clipboard[:repository] = Arclight::Repository.find_by(
70
+ slug: settings['repository']
71
+ ).name
72
+ end
73
+
74
+ # ==================
75
+ # Top level document
76
+ #
77
+ # NOTE: All fields should be stored in Solr
78
+ # ==================
79
+
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
+
88
+ to_field 'title_filing_ssi', extract_xpath('/ead/eadheader/filedesc/titlestmt/titleproper[@type="filing"]')
89
+ to_field 'title_ssm', extract_xpath('/ead/archdesc/did/unittitle')
90
+ to_field 'title_tesim', extract_xpath('/ead/archdesc/did/unittitle')
91
+ to_field 'ead_ssi', extract_xpath('/ead/eadheader/eadid')
92
+
93
+ to_field 'unitdate_ssm', extract_xpath('/ead/archdesc/did/unitdate')
94
+ to_field 'unitdate_bulk_ssim', extract_xpath('/ead/archdesc/did/unitdate[@type="bulk"]')
95
+ to_field 'unitdate_inclusive_ssm', extract_xpath('/ead/archdesc/did/unitdate[@type="inclusive"]')
96
+ to_field 'unitdate_other_ssim', extract_xpath('/ead/archdesc/did/unitdate[not(@type)]')
97
+
98
+ # All top-level docs treated as 'collection' for routing / display purposes
99
+ to_field 'level_ssm' do |_record, accumulator|
100
+ accumulator << 'collection'
101
+ end
102
+
103
+ # Keep the original top-level archdesc/@level for Level facet in addition to 'Collection'
104
+ to_field 'level_ssim' do |record, accumulator|
105
+ level = record.at_xpath('/ead/archdesc').attribute('level')&.value
106
+ other_level = record.at_xpath('/ead/archdesc').attribute('otherlevel')&.value
107
+
108
+ accumulator << Arclight::LevelLabel.new(level, other_level).to_s
109
+ accumulator << 'Collection' unless level == 'collection'
110
+ end
111
+
112
+ to_field 'unitid_ssm', extract_xpath('/ead/archdesc/did/unitid')
113
+ to_field 'unitid_tesim', extract_xpath('/ead/archdesc/did/unitid')
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 'collection_title_tesim' do |_record, accumulator, context|
130
+ accumulator.concat context.output_hash.fetch('normalized_title_ssm', [])
131
+ end
132
+
133
+ to_field 'collection_ssim' do |_record, accumulator, context|
134
+ accumulator.concat context.output_hash.fetch('normalized_title_ssm', [])
135
+ end
136
+
137
+ to_field 'repository_ssm' do |_record, accumulator, context|
138
+ accumulator << context.clipboard[:repository]
139
+ end
140
+
141
+ to_field 'repository_ssim' do |_record, accumulator, context|
142
+ accumulator << context.clipboard[:repository]
143
+ end
144
+
145
+ to_field 'geogname_ssm', extract_xpath('/ead/archdesc/controlaccess/geogname')
146
+ to_field 'geogname_ssim', extract_xpath('/ead/archdesc/controlaccess/geogname')
147
+
148
+ to_field 'creator_ssm', extract_xpath('/ead/archdesc/did/origination')
149
+ to_field 'creator_ssim', extract_xpath('/ead/archdesc/did/origination')
150
+ to_field 'creator_sort' do |record, accumulator|
151
+ accumulator << record.xpath('/ead/archdesc/did/origination').map { |c| c.text.strip }.join(', ')
152
+ end
153
+
154
+ to_field 'creator_persname_ssim', extract_xpath('/ead/archdesc/did/origination/persname')
155
+ to_field 'creator_corpname_ssim', extract_xpath('/ead/archdesc/did/origination/corpname')
156
+ to_field 'creator_famname_ssim', extract_xpath('/ead/archdesc/did/origination/famname')
157
+
158
+ to_field 'creators_ssim' do |_record, accumulator, context|
159
+ accumulator.concat context.output_hash['creator_persname_ssim'] if context.output_hash['creator_persname_ssim']
160
+ accumulator.concat context.output_hash['creator_corpname_ssim'] if context.output_hash['creator_corpname_ssim']
161
+ accumulator.concat context.output_hash['creator_famname_ssim'] if context.output_hash['creator_famname_ssim']
162
+ end
163
+
164
+ to_field 'places_ssim', extract_xpath('/ead/archdesc/controlaccess/geogname')
165
+
166
+ to_field 'access_terms_ssm', extract_xpath('/ead/archdesc/userestrict/*[local-name()!="head"]')
167
+
168
+ to_field 'acqinfo_ssim', extract_xpath('/ead/archdesc/acqinfo/*[local-name()!="head"]')
169
+ to_field 'acqinfo_ssim', extract_xpath('/ead/archdesc/descgrp/acqinfo/*[local-name()!="head"]')
170
+
171
+ to_field 'access_subjects_ssim', extract_xpath('/ead/archdesc/controlaccess', to_text: false) do |_record, accumulator|
172
+ accumulator.map! do |element|
173
+ %w[subject function occupation genreform].map do |selector|
174
+ element.xpath(".//#{selector}").map(&:text)
175
+ end
176
+ end.flatten!
177
+ end
178
+
179
+ to_field 'access_subjects_ssm' do |_record, accumulator, context|
180
+ accumulator.concat Array.wrap(context.output_hash['access_subjects_ssim'])
181
+ end
182
+
183
+ to_field 'has_online_content_ssim', extract_xpath('.//dao') do |_record, accumulator|
184
+ accumulator.replace([accumulator.any?])
185
+ end
186
+
187
+ to_field 'digital_objects_ssm', extract_xpath('/ead/archdesc/did/dao|/ead/archdesc/dao', to_text: false) do |_record, accumulator|
188
+ accumulator.map! do |dao|
189
+ label = dao.attributes['title']&.value ||
190
+ dao.xpath('daodesc/p')&.text
191
+ href = (dao.attributes['href'] || dao.attributes['xlink:href'])&.value
192
+ Arclight::DigitalObject.new(label: label, href: href).to_json
193
+ end
194
+ end
195
+
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
+
228
+ to_field 'genreform_ssim', extract_xpath('/ead/archdesc/controlaccess/genreform')
229
+
230
+ to_field 'date_range_isim', extract_xpath('/ead/archdesc/did/unitdate/@normal', to_text: false) do |_record, accumulator|
231
+ range = Arclight::YearRange.new
232
+ next range.years if accumulator.blank?
233
+
234
+ ranges = accumulator.map(&:to_s)
235
+ range << range.parse_ranges(ranges)
236
+ accumulator.replace range.years
237
+ end
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
+
242
+ SEARCHABLE_NOTES_FIELDS.map do |selector|
243
+ to_field "#{selector}_html_tesm", extract_xpath("/ead/archdesc/#{selector}/*[local-name()!='head']", to_text: false) do |_record, accumulator|
244
+ accumulator.map!(&:to_html)
245
+ end
246
+ to_field "#{selector}_heading_ssm", extract_xpath("/ead/archdesc/#{selector}/head") unless selector == 'prefercite'
247
+ to_field "#{selector}_tesim", extract_xpath("/ead/archdesc/#{selector}/*[local-name()!='head']")
248
+ end
249
+
250
+ DID_SEARCHABLE_NOTES_FIELDS.map do |selector|
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}")
253
+ end
254
+
255
+ NAME_ELEMENTS.map do |selector|
256
+ to_field 'names_coll_ssim', extract_xpath("/ead/archdesc/controlaccess/#{selector}")
257
+ to_field 'names_ssim', extract_xpath("//#{selector}"), unique
258
+ to_field "#{selector}_ssim", extract_xpath("//#{selector}"), unique
259
+ end
260
+
261
+ to_field 'language_ssim', extract_xpath('/ead/archdesc/did/langmaterial')
262
+
263
+ to_field 'descrules_ssm', extract_xpath('/ead/eadheader/profiledesc/descrules')
264
+
265
+ # count all descendant components from the top-level
266
+ to_field 'total_component_count_is', first_only do |record, accumulator|
267
+ accumulator << record.xpath('//c|//c01|//c02|//c03|//c04|//c05|//c06|//c07|//c08|//c09|//c10|//c11|//c12').count
268
+ end
269
+
270
+ # count all digital objects from the top-level
271
+ to_field 'online_item_count_is', first_only do |record, accumulator|
272
+ accumulator << record.xpath('//dao').count
273
+ end
274
+
275
+ to_field 'component_level_isim' do |_record, accumulator, _context|
276
+ accumulator << 0
277
+ end
278
+
279
+ to_field 'sort_isi' do |_record, accumulator, _context|
280
+ accumulator << 0
281
+ end
282
+
283
+ # =============================
284
+ # Each component child document
285
+ # <c> <c01> <c12>
286
+ # =============================
287
+
288
+ to_field 'components' do |record, accumulator, context|
289
+ child_components = record.xpath("/ead/archdesc/dsc/c|#{('/ead/archdesc/dsc/c01'..'/ead/archdesc/dsc/c12').to_a.join('|')}")
290
+ next unless child_components.any?
291
+
292
+ counter = Class.new do
293
+ def increment
294
+ @counter ||= 0
295
+ @counter += 1
296
+ end
297
+ end.new
298
+
299
+ component_indexer = Traject::Indexer::NokogiriIndexer.new.tap do |i|
300
+ i.settings do
301
+ provide :parent, context
302
+ provide :root, context
303
+ provide :counter, counter
304
+ provide :depth, 1
305
+ provide :logger, context.settings[:logger]
306
+ provide :component_traject_config, context.settings[:component_traject_config]
307
+ end
308
+
309
+ i.load_config_file(context.settings[:component_traject_config])
310
+ end
311
+
312
+ child_components.each do |child_component|
313
+ output = component_indexer.map_record(child_component)
314
+ accumulator << output if output.keys.any?
315
+ end
316
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ module Traject
5
+ # Provides a Traject Reader for XML Documents which removes the namespaces
6
+ class NokogiriNamespacelessReader < ::Traject::NokogiriReader
7
+ # Overrides the #each method (which is used for iterating through each Document)
8
+ # @param args
9
+ # @see ::Traject::NokogiriReader#each
10
+ # @see Enumerable#each
11
+ def each(*args)
12
+ return to_enum(:each, *args) unless block_given?
13
+
14
+ super do |doc|
15
+ new_doc = doc.dup
16
+ new_doc.remove_namespaces!
17
+ yield new_doc
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Arclight
4
- VERSION = '0.1.0'
4
+ VERSION = '1.6.2'
5
5
  end
@@ -18,19 +18,20 @@ module Arclight
18
18
  def initialize(dates = nil)
19
19
  @years = []
20
20
  self << parse_ranges(dates) if dates.present?
21
- self
22
21
  end
23
22
 
24
23
  # @return [String] a concise, human-readable version of the year range, including gaps
25
24
  def to_s
26
25
  return if years.empty?
27
26
  return to_s_for_streak(years) unless gaps?
27
+
28
28
  to_s_with_gaps
29
29
  end
30
30
 
31
31
  # @param [Array<Integer>] `other` the set of years to add
32
32
  def <<(other)
33
33
  return self if other.blank?
34
+
34
35
  @years |= other # will remove duplicates
35
36
  @years.sort!
36
37
  self
@@ -40,10 +41,12 @@ module Arclight
40
41
  # @return [Array<Integer>] the set of years in the given range
41
42
  def parse_range(dates)
42
43
  return if dates.blank?
44
+
43
45
  start_year, end_year = dates.split('/').map { |date| to_year_from_iso8601(date) }
44
46
  return [start_year] if end_year.blank?
45
47
  raise ArgumentError, "Range is too large: #{dates}" if (end_year - start_year) > 1000
46
48
  raise ArgumentError, "Range is inverted: #{dates}" unless start_year <= end_year
49
+
47
50
  (start_year..end_year).to_a
48
51
  end
49
52
 
@@ -57,12 +60,14 @@ module Arclight
57
60
  # YYYY, YYYY-MM, YYYY-MM-DD, and YYYYMMDD
58
61
  def to_year_from_iso8601(date)
59
62
  return if date.blank?
63
+
60
64
  date.split('-').first[0..3].to_i # Time.parse doesn't work here
61
65
  end
62
66
 
63
67
  # @return [Boolean] are there gaps between the years, such as 1999, 2000, 2002?
64
68
  def gaps?
65
69
  return false if years.blank?
70
+
66
71
  (years.min..years.max).to_a != years
67
72
  end
68
73
 
@@ -71,12 +76,14 @@ module Arclight
71
76
  # Deals with making a human-readable range for years with 1 or more gaps.
72
77
  # It involves detection of streaks between the gaps.
73
78
  # @return [String] 1999-2000, 2002 for 1999, 2000, 2002
74
- def to_s_with_gaps # rubocop: disable Metrics/AbcSize, Metrics/MethodLength
79
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
80
+ def to_s_with_gaps
75
81
  raise ArgumentError if years.blank? || years.length < 2
82
+
76
83
  results = []
77
84
  streak = [years[0]]
78
85
  i = streak.first
79
- years[1..-1].each do |j|
86
+ years[1..].each do |j|
80
87
  i += 1
81
88
  if i == j
82
89
  streak << j
@@ -93,10 +100,12 @@ module Arclight
93
100
  results << to_s_for_streak(streak)
94
101
  results.join(', ')
95
102
  end
103
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
96
104
 
97
105
  def to_s_for_streak(streak)
98
106
  return streak.min.to_s if streak.min == streak.max
99
- [streak.min, streak.max].map(&:to_s).join('-')
107
+
108
+ streak.minmax.map(&:to_s).join('-')
100
109
  end
101
110
  end
102
111
  end
data/lib/arclight.rb CHANGED
@@ -5,6 +5,11 @@ require 'arclight/engine'
5
5
  require 'arclight/repository'
6
6
  require 'arclight/year_range'
7
7
 
8
+ # :nodoc:
8
9
  module Arclight
9
- # Your code goes here...
10
+ autoload :Routes, 'arclight/routes'
11
+
12
+ def self.deprecation
13
+ @deprecation ||= ActiveSupport::Deprecation.new('2.0', 'Arclight')
14
+ end
10
15
  end
@@ -5,8 +5,10 @@ require 'rails/generators'
5
5
  module Arclight
6
6
  ##
7
7
  # Arclight install generator
8
- class Install < Rails::Generators::Base
9
- source_root File.expand_path('../templates', __FILE__)
8
+ class Install < Rails::Generators::Base # rubocop:disable Metrics/ClassLength
9
+ source_root File.expand_path('templates', __dir__)
10
+
11
+ class_option :test, type: :boolean, default: false, aliases: '-t', desc: 'Indicates that app will be installed in a test environment'
10
12
 
11
13
  def create_blacklight_catalog
12
14
  remove_file 'app/controllers/catalog_controller.rb'
@@ -19,22 +21,52 @@ module Arclight
19
21
  end
20
22
  end
21
23
 
22
- def install_blacklight_range_limit
23
- generate 'blacklight_range_limit:install'
24
+ def install_blacklight_locale_picker
25
+ gem 'blacklight-locale_picker'
26
+
27
+ Bundler.with_unbundled_env do
28
+ run 'bundle install'
29
+ end
30
+
31
+ generate 'blacklight:locale_picker:install'
32
+
33
+ inject_into_file 'app/helpers/application_helper.rb', after: 'include Blacklight::LocalePicker::LocaleHelper' do
34
+ "\n\n def additional_locale_routing_scopes\n [blacklight, arclight_engine]\n end"
35
+ end
24
36
  end
25
37
 
26
38
  def add_custom_routes
27
39
  inject_into_file 'config/routes.rb', after: "mount Blacklight::Engine => '/'" do
28
- "\n mount Arclight::Engine => '/'\n"
40
+ "\n mount Arclight::Engine => '/'\n"
29
41
  end
42
+
43
+ gsub_file 'config/routes.rb', 'root to: "catalog#index"', 'root to: "arclight/repositories#index"'
30
44
  end
31
45
 
32
- def assets
46
+ def add_frontend
47
+ if ENV['CI']
48
+ run "yarn add file:#{Arclight::Engine.root}"
49
+ elsif options[:test]
50
+ run 'yarn link arclight'
51
+
52
+ # If a branch was specified (e.g. you are running a template.rb build
53
+ # against a test branch), use the latest version available on npm
54
+ elsif ENV['BRANCH']
55
+ run 'yarn add arclight@latest'
56
+
57
+ # Otherwise, pick the version from npm that matches the Arclight
58
+ # gem version
59
+ else
60
+ run "yarn add arclight@#{arclight_yarn_version}"
61
+ end
62
+ end
63
+
64
+ def add_stylesheets
33
65
  copy_file 'arclight.scss', 'app/assets/stylesheets/arclight.scss'
34
- copy_file 'arclight.js', 'app/assets/javascripts/arclight.js'
35
- inject_into_file 'app/assets/javascripts/application.js', after: '//= require blacklight/blacklight' do
36
- "\n//= require bootstrap/scrollspy\n" \
37
- "\n//= require bootstrap/tab\n"
66
+ append_to_file 'app/assets/stylesheets/application.bootstrap.scss' do
67
+ <<~CONTENT
68
+ @import "arclight";
69
+ CONTENT
38
70
  end
39
71
  end
40
72
 
@@ -56,8 +88,76 @@ module Arclight
56
88
  copy_file 'config/downloads.yml' unless File.exist?('config/downloads.yml')
57
89
  end
58
90
 
91
+ def add_i18n_config
92
+ copy_file 'config/locales/arclight.en.yml'
93
+ end
94
+
59
95
  def modify_blacklight_yml
60
96
  gsub_file 'config/locales/blacklight.en.yml', "application_name: 'Blacklight'", "application_name: 'Arclight'"
61
97
  end
98
+
99
+ def assets
100
+ if using_importmap?
101
+ pin_javascript_dependencies
102
+ import_arclight_javascript
103
+ else
104
+ install_javascript_dependencies
105
+ end
106
+ end
107
+
108
+ def inject_arclight_routes
109
+ inject_into_file 'config/routes.rb',
110
+ " concern :hierarchy, Arclight::Routes::Hierarchy.new\n",
111
+ after: /concern :exportable.*\n/
112
+ inject_into_file 'config/routes.rb',
113
+ " concerns :hierarchy\n",
114
+ after: /resources :solr_documents.*\n/
115
+ end
116
+
117
+ private
118
+
119
+ def root
120
+ @root ||= Pathname(destination_root)
121
+ end
122
+
123
+ def using_importmap?
124
+ @using_importmap ||= root.join('config/importmap.rb').exist?
125
+ end
126
+
127
+ # This is the last step because any failure here wouldn't prevent the other steps from running
128
+ def install_javascript_dependencies
129
+ inject_into_file 'app/assets/javascripts/application.js', after: '//= require blacklight/blacklight' do
130
+ "\n// Required by Arclight" \
131
+ "\n//= require arclight/arclight"
132
+ end
133
+ end
134
+
135
+ def pin_javascript_dependencies
136
+ say 'Arclight Importmap asset generation'
137
+
138
+ append_to_file 'config/importmap.rb', <<~RUBY
139
+ pin "arclight", to: "arclight/arclight.js"
140
+ # TODO: We may be able to move these to a single importmap for arclight.
141
+ pin "arclight/oembed_controller", to: "arclight/oembed_controller.js"
142
+ pin "arclight/truncate_controller", to: "arclight/truncate_controller.js"
143
+ RUBY
144
+ end
145
+
146
+ def import_arclight_javascript
147
+ append_to_file 'app/javascript/application.js', "\nimport \"arclight\""
148
+ end
149
+
150
+ def package_yarn_version(package_name, requested_version)
151
+ versions = JSON.parse(`yarn info #{package_name} versions --json`)['data']
152
+ exact_match = versions.find { |v| v == requested_version }
153
+ return exact_match if exact_match
154
+
155
+ major_version = Gem::Version.new(requested_version).segments.first
156
+ "^#{major_version}"
157
+ end
158
+
159
+ def arclight_yarn_version
160
+ package_yarn_version('arclight', Arclight::VERSION)
161
+ end
62
162
  end
63
163
  end
@@ -1,3 +1,6 @@
1
- /*
2
- *= require arclight/application
3
- */
1
+ :root {
2
+ --bs-breadcrumb-divider: "»";
3
+ }
4
+
5
+ // Arclight, imported from arclight frontend package
6
+ @import 'arclight/app/assets/stylesheets/arclight/application';