arclight 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc +12 -0
  3. data/.gitignore +5 -0
  4. data/.rubocop.yml +66 -0
  5. data/.solr_wrapper +5 -0
  6. data/.travis.yml +30 -2
  7. data/CONTRIBUTING.md +43 -0
  8. data/Gemfile +36 -0
  9. data/LICENSE.txt +1 -0
  10. data/README.md +85 -12
  11. data/Rakefile +14 -3
  12. data/app/assets/images/blacklight/compact.svg +25 -0
  13. data/app/assets/images/blacklight/logo.png +0 -0
  14. data/app/assets/javascripts/arclight/arclight.js +9 -0
  15. data/app/assets/javascripts/arclight/collection_context.js +18 -0
  16. data/app/assets/javascripts/arclight/collection_navigation.js +114 -0
  17. data/app/assets/javascripts/arclight/collection_scrollspy.js +6 -0
  18. data/app/assets/javascripts/arclight/component_ancestors.js +56 -0
  19. data/app/assets/javascripts/arclight/oembed_viewer.js +39 -0
  20. data/app/assets/javascripts/arclight/truncator.js.erb +23 -0
  21. data/app/assets/stylesheets/arclight/application.scss +13 -0
  22. data/app/assets/stylesheets/arclight/bootstrap_overrides.scss +3 -0
  23. data/app/assets/stylesheets/arclight/modules/collection_search.scss +3 -0
  24. data/app/assets/stylesheets/arclight/modules/hierarchy_and_online_contents.scss +174 -0
  25. data/app/assets/stylesheets/arclight/modules/highlights.scss +10 -0
  26. data/app/assets/stylesheets/arclight/modules/layout.scss +33 -0
  27. data/app/assets/stylesheets/arclight/modules/mastheads.scss +60 -0
  28. data/app/assets/stylesheets/arclight/modules/repositories.scss +29 -0
  29. data/app/assets/stylesheets/arclight/modules/repository_card.scss +54 -0
  30. data/app/assets/stylesheets/arclight/modules/search_results.scss +75 -0
  31. data/app/assets/stylesheets/arclight/modules/show_collection.scss +78 -0
  32. data/app/assets/stylesheets/arclight/modules/sidebar.scss +16 -0
  33. data/app/assets/stylesheets/arclight/variables.scss +2 -0
  34. data/app/controllers/arclight/repositories_controller.rb +40 -0
  35. data/app/controllers/concerns/arclight/field_config_helpers.rb +86 -0
  36. data/app/factories/blacklight_field_configuration_factory.rb +29 -0
  37. data/app/helpers/arclight_helper.rb +173 -0
  38. data/app/models/arclight/parent.rb +24 -0
  39. data/app/models/arclight/parents.rb +34 -0
  40. data/app/models/arclight/requests/google_form.rb +44 -0
  41. data/app/models/concerns/arclight/catalog.rb +22 -0
  42. data/app/models/concerns/arclight/search_behavior.rb +46 -0
  43. data/app/models/concerns/arclight/solr_document.rb +131 -0
  44. data/app/presenters/arclight/index_presenter.rb +10 -0
  45. data/app/presenters/arclight/show_presenter.rb +27 -0
  46. data/app/views/arclight/.keep +0 -0
  47. data/app/views/arclight/repositories/_in_person_repository.html.erb +19 -0
  48. data/app/views/arclight/repositories/_repository.html.erb +62 -0
  49. data/app/views/arclight/repositories/index.html.erb +4 -0
  50. data/app/views/arclight/repositories/show.html.erb +38 -0
  51. data/app/views/arclight/requests/_google_form.html.erb +11 -0
  52. data/app/views/arclight/viewers/_oembed.html.erb +7 -0
  53. data/app/views/catalog/_arclight_document_index_header.html.erb +13 -0
  54. data/app/views/catalog/_arclight_document_index_header_hierarchy_default.html.erb +0 -0
  55. data/app/views/catalog/_arclight_document_index_header_online_contents_default.html.erb +0 -0
  56. data/app/views/catalog/_arclight_document_show_header.html.erb +15 -0
  57. data/app/views/catalog/_arclight_document_show_header_collection.html.erb +12 -0
  58. data/app/views/catalog/_arclight_index_compact_default.html.erb +15 -0
  59. data/app/views/catalog/_arclight_online_content_indicator.html.erb +5 -0
  60. data/app/views/catalog/_arclight_rangelimit.html.erb +24 -0
  61. data/app/views/catalog/_arclight_viewer_default.html.erb +1 -0
  62. data/app/views/catalog/_collection_contents.html.erb +12 -0
  63. data/app/views/catalog/_collection_count.html.erb +7 -0
  64. data/app/views/catalog/_collection_downloads.html.erb +17 -0
  65. data/app/views/catalog/_collection_online_contents.html.erb +17 -0
  66. data/app/views/catalog/_collection_overview.html.erb +7 -0
  67. data/app/views/catalog/_component_overview.html.erb +46 -0
  68. data/app/views/catalog/_context_card.html.erb +27 -0
  69. data/app/views/catalog/_context_sidebar.html.erb +8 -0
  70. data/app/views/catalog/_custom_metadata.html.erb +16 -0
  71. data/app/views/catalog/_home.html.erb +1 -0
  72. data/app/views/catalog/_index_breadcrumb_default.html.erb +3 -0
  73. data/app/views/catalog/_index_default.html.erb +17 -0
  74. data/app/views/catalog/_index_header.html.erb +7 -0
  75. data/app/views/catalog/_index_header_hierarchy_default.html.erb +42 -0
  76. data/app/views/catalog/_index_header_online_contents_default.html.erb +1 -0
  77. data/app/views/catalog/_index_hierarchy_default.html.erb +28 -0
  78. data/app/views/catalog/_index_online_contents_default.html.erb +6 -0
  79. data/app/views/catalog/_results_histogram.html.erb +10 -0
  80. data/app/views/catalog/_search_results.html.erb +31 -0
  81. data/app/views/catalog/_search_results_repository.html.erb +6 -0
  82. data/app/views/catalog/_search_within_form.html.erb +16 -0
  83. data/app/views/catalog/_show_breadcrumbs_default.html.erb +7 -0
  84. data/app/views/catalog/_show_collection.html.erb +40 -0
  85. data/app/views/catalog/_show_component_sidebar.html.erb +12 -0
  86. data/app/views/catalog/_show_default.html.erb +32 -0
  87. data/app/views/catalog/_show_header.html.erb +5 -0
  88. data/app/views/catalog/_show_sidebar.html.erb +30 -0
  89. data/app/views/catalog/index.html.erb +8 -0
  90. data/app/views/layouts/catalog_result.html.erb +7 -0
  91. data/app/views/shared/_breadcrumbs.html.erb +15 -0
  92. data/app/views/shared/_context_sidebar.html.erb +8 -0
  93. data/app/views/shared/_header_navbar.html.erb +54 -0
  94. data/app/views/shared/_main_menu_links.html.erb +6 -0
  95. data/arclight.gemspec +17 -4
  96. data/bin/rails +13 -0
  97. data/config/locales/arclight.en.yml +65 -0
  98. data/config/routes.rb +6 -0
  99. data/lib/arclight.rb +5 -0
  100. data/lib/arclight/custom_component.rb +98 -0
  101. data/lib/arclight/custom_document.rb +93 -0
  102. data/lib/arclight/digital_object.rb +26 -0
  103. data/lib/arclight/engine.rb +55 -0
  104. data/lib/arclight/exceptions.rb +18 -0
  105. data/lib/arclight/indexer.rb +9 -0
  106. data/lib/arclight/normalized_date.rb +45 -0
  107. data/lib/arclight/normalized_id.rb +25 -0
  108. data/lib/arclight/normalized_title.rb +30 -0
  109. data/lib/arclight/repository.rb +91 -0
  110. data/lib/arclight/shared_indexing_behavior.rb +97 -0
  111. data/lib/arclight/shared_terminology_behavior.rb +65 -0
  112. data/lib/arclight/solr_ead_indexer_ext.rb +159 -0
  113. data/lib/arclight/version.rb +3 -1
  114. data/lib/arclight/viewer.rb +45 -0
  115. data/lib/arclight/viewers/oembed.rb +56 -0
  116. data/lib/arclight/year_range.rb +102 -0
  117. data/lib/generators/arclight/install_generator.rb +63 -0
  118. data/lib/generators/arclight/templates/arclight.js +2 -0
  119. data/lib/generators/arclight/templates/arclight.scss +3 -0
  120. data/lib/generators/arclight/templates/catalog_controller.rb +347 -0
  121. data/lib/generators/arclight/templates/config/downloads.yml +13 -0
  122. data/lib/generators/arclight/templates/config/repositories.yml +42 -0
  123. data/lib/generators/arclight/update_generator.rb +22 -0
  124. data/lib/tasks/index.rake +87 -0
  125. data/package.json +24 -0
  126. data/solr/conf/_rest_managed.json +3 -0
  127. data/solr/conf/admin-extra.html +31 -0
  128. data/solr/conf/elevate.xml +36 -0
  129. data/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
  130. data/solr/conf/protwords.txt +21 -0
  131. data/solr/conf/schema.xml +631 -0
  132. data/solr/conf/scripts.conf +24 -0
  133. data/solr/conf/solrconfig.xml +393 -0
  134. data/solr/conf/spellings.txt +2 -0
  135. data/solr/conf/stopwords.txt +58 -0
  136. data/solr/conf/stopwords_en.txt +58 -0
  137. data/solr/conf/synonyms.txt +31 -0
  138. data/solr/conf/xslt/example.xsl +132 -0
  139. data/solr/conf/xslt/example_atom.xsl +67 -0
  140. data/solr/conf/xslt/example_rss.xsl +66 -0
  141. data/solr/conf/xslt/luke.xsl +337 -0
  142. data/tasks/arclight.rake +68 -0
  143. data/template.rb +15 -0
  144. data/vendor/assets/javascripts/responsiveTruncator.js +69 -0
  145. data/vendor/assets/javascripts/stickyfill.js +480 -0
  146. metadata +301 -6
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ ##
5
+ # A utility class to normalize dates, typically by joining inclusive and bulk dates
6
+ # e.g., "1990-2000, bulk 1990-1999"
7
+ # @see http://www2.archivists.org/standards/DACS/part_I/chapter_2/4_date
8
+ class NormalizedDate
9
+ # @param [String | Array<String>] `inclusive` from the `unitdate`
10
+ # @param [String] `bulk` from the `unitdate`
11
+ # @param [String] `other` from the `unitdate` when type is not specified
12
+ def initialize(inclusive, bulk = nil, other = nil)
13
+ if inclusive.is_a? Array # of YYYY-YYYY for ranges
14
+ @inclusive = YearRange.new(inclusive.include?('/') ? inclusive : inclusive.map { |v| v.tr('-', '/') }).to_s
15
+ elsif inclusive.present?
16
+ @inclusive = inclusive.strip
17
+ end
18
+ @bulk = bulk.strip if bulk.present?
19
+ @other = other.strip if other.present?
20
+ end
21
+
22
+ # @return [String] the normalized title/date
23
+ def to_s
24
+ normalize
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :inclusive, :bulk, :other
30
+
31
+ # @see http://www2.archivists.org/standards/DACS/part_I/chapter_2/4_date for rules
32
+ def normalize
33
+ if inclusive.present?
34
+ result = inclusive.to_s
35
+ result << ", bulk #{bulk}" if bulk.present?
36
+ elsif other.present?
37
+ result = other.to_s
38
+ else
39
+ result = nil
40
+ end
41
+ return if result.blank?
42
+ result.strip
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ ##
5
+ # A simple utility class to normalize identifiers
6
+ # to be used around the application for linking
7
+ class NormalizedId
8
+ def initialize(id)
9
+ @id = id
10
+ end
11
+
12
+ def to_s
13
+ normalize
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :id
19
+
20
+ def normalize
21
+ raise Arclight::Exceptions::IDNotFound if id.blank?
22
+ id.strip.tr('.', '-')
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ ##
5
+ # A utility class to normalize titles, typically by joining
6
+ # the title and date, e.g., "My Title, 1990-2000"
7
+ class NormalizedTitle
8
+ # @param [String] `title` from the `unittitle`
9
+ # @param [String] `date` from the `unitdate`
10
+ def initialize(title, date = nil)
11
+ @title = title.gsub(/\s*,\s*$/, '').strip if title.present?
12
+ @date = date.strip if date.present?
13
+ end
14
+
15
+ # @return [String] the normalized title/date
16
+ def to_s
17
+ normalize
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :title, :date, :default
23
+
24
+ def normalize
25
+ result = [title, date].compact.join(', ')
26
+ raise Arclight::Exceptions::TitleNotFound if result.blank?
27
+ result
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ #
5
+ # Static information about a given repository identified by a unique `slug`
6
+ #
7
+ class Repository
8
+ include ActiveModel::Conversion # for to_partial_path
9
+
10
+ FIELDS = %i[name
11
+ description
12
+ visit_note
13
+ building
14
+ address1
15
+ address2
16
+ city
17
+ state
18
+ zip
19
+ country
20
+ phone
21
+ contact_info
22
+ thumbnail_url
23
+ google_request_url
24
+ google_request_mappings
25
+ collection_count].freeze
26
+
27
+ attr_accessor :slug, *FIELDS
28
+
29
+ # @param [String] `slug` the unique identifier for the repository
30
+ # @param [Hash] `data`
31
+ def initialize(slug, data = {})
32
+ @slug = slug
33
+ FIELDS.each do |field|
34
+ value = data[field.to_s]
35
+ send("#{field}=", value) if value.present?
36
+ end
37
+ end
38
+
39
+ # @return [String] handles the formatting of "city, state zip, country"
40
+ def city_state_zip_country
41
+ state_zip = state
42
+ state_zip += " #{zip}" if zip
43
+ [city, state_zip, country].compact.join(', ')
44
+ end
45
+
46
+ # Load repository information from a YAML file
47
+ #
48
+ # @param [String] `filename`
49
+ # @return [Hash<Slug,Repository>]
50
+ def self.from_yaml(file)
51
+ repos = {}
52
+ data = YAML.safe_load(File.read(file))
53
+ data.keys.each do |slug|
54
+ repos[slug] = new(slug, data[slug])
55
+ end
56
+ repos
57
+ end
58
+
59
+ # Mimics ActiveRecord's `all` behavior
60
+ #
61
+ # @return [Array<Repository>]
62
+ def self.all
63
+ from_yaml(ENV['REPOSITORY_FILE'] || 'config/repositories.yml').values
64
+ end
65
+
66
+ # Mimics ActiveRecord dynamic `find_by` behavior for the slug or name
67
+ #
68
+ # @param [String] `slug` or `name`
69
+ # @return [Repository]
70
+ def self.find_by(slug: nil, name: nil)
71
+ if slug
72
+ all.find { |repo| repo.slug == slug }
73
+ elsif name
74
+ all.find { |repo| repo.name == name }
75
+ else
76
+ raise ArgumentError, 'Requires either slug or name parameters to find_by'
77
+ end
78
+ end
79
+
80
+ # Mimics ActiveRecord dynamic `find_by!` behavior for the slug or name
81
+ #
82
+ # @param [String] `slug` or `name` -- same as `find_by`
83
+ # @return [Repository]
84
+ # @raise [ActiveRecord::RecordNotFound] if cannot find repository
85
+ def self.find_by!(*args)
86
+ repository = find_by(*args)
87
+ raise ActiveRecord::RecordNotFound if repository.blank?
88
+ repository
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ ##
5
+ # A mixin intended to share indexing behavior between
6
+ # the CustomDocument and CustomComponent classes
7
+ module SharedIndexingBehavior
8
+ # @see http://eadiva.com/2/unitdate/
9
+ # @return [YearRange] all of the years between the given years
10
+ def unitdate_for_range
11
+ range = YearRange.new
12
+ return range if normal_unit_dates.blank?
13
+ range << range.parse_ranges(normal_unit_dates)
14
+ range
15
+ end
16
+
17
+ def subjects_array(elements, parent:)
18
+ xpath_elements = elements.map { |el| "local-name()='#{el}'" }.join(' or ')
19
+ subjects = search("//#{parent}/controlaccess/*[#{xpath_elements}]").to_a
20
+ clean_facets_array(subjects.flatten.map(&:text))
21
+ end
22
+
23
+ def names_array(elements, parent:)
24
+ xpath_elements = elements.map { |el| "local-name()='#{el}'" }.join(' or ')
25
+ names = search("//#{parent}/controlaccess/*[#{xpath_elements}]").to_a
26
+ clean_facets_array(names.flatten.map(&:text))
27
+ end
28
+
29
+ # Return a cleaned array of facets without marc subfields
30
+ #
31
+ # E.g. clean_facets_array(
32
+ # ['FacetValue1 |z FacetValue2','FacetValue3']
33
+ # ) => ['FacetValue1 -- FacetValue2', 'FacetValue3']
34
+ def clean_facets_array(facets_array)
35
+ Array(facets_array).map { |text| fix_subfield_demarcators(text) }.compact.uniq
36
+ end
37
+
38
+ # Replace MARC style subfield demarcators
39
+ #
40
+ # Usage: fix_subfield_demarcators("Subject 1 |z Sub-Subject 2") => "Subject 1 -- Sub-Subject 2"
41
+ def fix_subfield_demarcators(value)
42
+ value.gsub(/\|\w{1}/, '--')
43
+ end
44
+
45
+ # Wrap OM's find_by_xpath for convenience
46
+ def search(path)
47
+ find_by_xpath(path) # rubocop:disable DynamicFindBy
48
+ end
49
+
50
+ # If a repository slug is provided via an environment variable `REPOSITORY_ID`,
51
+ # then use that to lookup the name rather than the parsed out name from the EAD
52
+ # @param [String] `repository` the default repository name
53
+ def repository_as_configured(repository)
54
+ slug = ENV['REPOSITORY_ID']
55
+ if slug.present?
56
+ begin
57
+ Arclight::Repository.find_by(slug: slug).name
58
+ rescue => e
59
+ raise "The repository slug '#{slug}' was given but it is not found in the Repository configuration data: #{e}"
60
+ end
61
+ else
62
+ repository
63
+ end
64
+ end
65
+
66
+ def add_digital_content(prefix:, solr_doc:)
67
+ dao = ng_xml.xpath("#{prefix}/dao").to_a
68
+ return if dao.blank?
69
+ field_name = Solrizer.solr_name('digital_objects', :displayable)
70
+ solr_doc[field_name] = digital_objects(dao)
71
+ end
72
+
73
+ def digital_objects(objects)
74
+ objects.map do |dao|
75
+ label = dao.attributes['title'].try(:value) || dao.xpath('daodesc/p').try(:text)
76
+ href = (dao.attributes['href'] || dao.attributes['xlink:href']).try(:value)
77
+ Arclight::DigitalObject.new(label: label, href: href).to_json
78
+ end
79
+ end
80
+
81
+ def add_date_ranges(solr_doc)
82
+ Solrizer.insert_field(solr_doc, 'date_range', unitdate_for_range.years, :facetable)
83
+ end
84
+
85
+ def add_normalized_title(solr_doc)
86
+ dates = Arclight::NormalizedDate.new(unitdate_inclusive.first, unitdate_bulk.first, unitdate_other.first).to_s
87
+ title = Arclight::NormalizedTitle.new(solr_doc['title_ssm'].try(:first), dates).to_s
88
+ solr_doc['normalized_title_ssm'] = [title]
89
+ solr_doc['normalized_date_ssm'] = [dates]
90
+ title
91
+ end
92
+
93
+ def online_content?
94
+ search('//dao[@href]').present?
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ ##
5
+ # An extendable mixin intended to share terminology behavior between
6
+ # the CustomDocument and CustomComponent classes
7
+ module SharedTerminologyBehavior
8
+ def add_unitid(t, prefix)
9
+ t.unitid(path: prefix + 'did/unitid', index_as: %i[displayable searchable])
10
+ end
11
+
12
+ def add_extent(t, prefix)
13
+ t.extent(path: prefix + 'did/physdesc/extent', index_as: %i[displayable searchable])
14
+ end
15
+
16
+ # date indexing
17
+ def add_dates(t, prefix)
18
+ t.normal_unit_dates(path: prefix + 'did/unitdate/@normal')
19
+ t.unitdate_bulk(path: prefix + 'did/unitdate[@type=\'bulk\']', index_as: %i[displayable])
20
+ t.unitdate_inclusive(path: prefix + 'did/unitdate[@type=\'inclusive\']', index_as: %i[displayable])
21
+ t.unitdate_other(path: prefix + 'did/unitdate[not(@type)]', index_as: %i[displayable])
22
+ t.unitdate(path: prefix + 'did/unitdate', index_as: %i[displayable])
23
+ end
24
+
25
+ def add_searchable_notes(t, prefix) # rubocop: disable Metrics/MethodLength
26
+ # various searchable notes
27
+ %i[
28
+ accessrestrict
29
+ accruals
30
+ acqinfo
31
+ altformavail
32
+ appraisal
33
+ arrangement
34
+ bibliography
35
+ bioghist
36
+ custodhist
37
+ fileplan
38
+ note
39
+ odd
40
+ originalsloc
41
+ otherfindaid
42
+ phystech
43
+ prefercite
44
+ processinfo
45
+ relatedmaterial
46
+ scopecontent
47
+ separatedmaterial
48
+ userestrict
49
+ ].each do |k|
50
+ # many of the notes support various markup so we want everything but the heading
51
+ t.send(k, path: "#{prefix}#{k}/*[local-name()!=\"head\"]", index_as: %i[displayable searchable])
52
+ end
53
+
54
+ # various searchable notes in the did
55
+ %i[
56
+ abstract
57
+ materialspec
58
+ physloc
59
+ ].each do |k|
60
+ t.send(k, path: "#{prefix}did/#{k}", index_as: %i[displayable searchable])
61
+ end
62
+ t.did_note(path: "#{prefix}did/note", index_as: %i[displayable searchable]) # conflicts with top-level note
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ ##
5
+ # An module to extend SolrEad::Indexer behaviors to allow us to add
6
+ # or override behaviors that require knowledge of the entire XML document.
7
+ module SolrEadIndexerExt
8
+ def additional_component_fields(node, addl_fields = {})
9
+ solr_doc = super
10
+
11
+ add_count_of_child_compontents(node, solr_doc)
12
+ add_ancestral_titles(node, solr_doc)
13
+ add_ancestral_ids(node, solr_doc)
14
+
15
+ add_collection_creator_to_component(node, solr_doc)
16
+
17
+ add_self_or_parents_restrictions(node, solr_doc)
18
+
19
+ add_self_or_parents_terms(node, solr_doc)
20
+
21
+ solr_doc
22
+ end
23
+
24
+ def delete_all
25
+ solr.delete_by_query('*:*')
26
+ solr.commit
27
+ end
28
+
29
+ private
30
+
31
+ # Note that we need to redo what solr_ead does for ids due to our normalization process
32
+ def add_ancestral_ids(node, solr_doc)
33
+ @parent_id_name ||= Solrizer.solr_name('parent', :stored_sortable)
34
+ @parent_ids_field_name ||= Solrizer.solr_name('parent', :displayable)
35
+ @parent_ids_search_field_name ||= Solrizer.solr_name('parent', :searchable)
36
+
37
+ ids = ancestral_ids(node)
38
+ solr_doc[@parent_ids_field_name] = ids
39
+ solr_doc[@parent_ids_search_field_name] = ids
40
+ solr_doc[@parent_id_name] = ids.last
41
+ end
42
+
43
+ # Note that we need to redo what solr_ead does for titles due to our normalization process
44
+ def add_ancestral_titles(node, solr_doc)
45
+ @parent_titles_field_name ||= Solrizer.solr_name('parent_unittitles', :displayable)
46
+ @parent_titles_search_field_name ||= Solrizer.solr_name('parent_unittitles', :searchable)
47
+ @collection_facet_name ||= Solrizer.solr_name('collection', :facetable)
48
+ @collection_name ||= Solrizer.solr_name('collection', :displayable)
49
+
50
+ titles = ancestral_titles(node)
51
+ solr_doc[@parent_titles_field_name] = titles
52
+ solr_doc[@parent_titles_search_field_name] = titles
53
+ solr_doc[@collection_name] = [titles.first] # collection is always on top
54
+ solr_doc[@collection_facet_name] = [titles.first]
55
+ end
56
+
57
+ def add_count_of_child_compontents(node, solr_doc)
58
+ @child_component_count_name ||= Solrizer.solr_name('child_component_count', type: :integer)
59
+
60
+ solr_doc[@child_component_count_name] = node.xpath('count(c)').to_i
61
+ end
62
+
63
+ def ancestral_ids(node)
64
+ ancestral_visit(node, :normalized_component_id, :normalized_collection_id)
65
+ end
66
+
67
+ def ancestral_titles(node)
68
+ ancestral_visit(node, :normalized_component_title, :normalized_collection_title)
69
+ end
70
+
71
+ # visit each component's parent and finish with a visit on the collection
72
+ def ancestral_visit(node, component_fn, collection_fn, results = [])
73
+ while node.parent && node.parent.name == 'c'
74
+ parent = node.parent
75
+ results << send(component_fn, parent)
76
+ node = parent
77
+ end
78
+ results << send(collection_fn, node)
79
+ results.reverse
80
+ end
81
+
82
+ def normalized_component_title(node)
83
+ data = extract_title_and_dates(node)
84
+ normalize_title(data)
85
+ end
86
+
87
+ def normalized_collection_title(node)
88
+ data = extract_title_and_dates(node, '//archdesc/')
89
+ normalize_title(data)
90
+ end
91
+
92
+ def normalize_title(data)
93
+ Arclight::NormalizedTitle.new(
94
+ data[:title],
95
+ Arclight::NormalizedDate.new(
96
+ data[:unitdate_inclusive],
97
+ data[:unitdate_bulk],
98
+ data[:unitdate_other]
99
+ ).to_s
100
+ ).to_s
101
+ end
102
+
103
+ # TODO: these xpaths should be DRY'd up -- they're in both terminologies
104
+ def extract_title_and_dates(node, prefix = nil)
105
+ data = {
106
+ title: node.at_xpath("#{prefix}did/unittitle"),
107
+ unitdate_inclusive: node.at_xpath("#{prefix}did/unitdate[@type=\"inclusive\"]"),
108
+ unitdate_bulk: node.at_xpath("#{prefix}did/unitdate[@type=\"bulk\"]"),
109
+ unitdate_other: node.at_xpath("#{prefix}did/unitdate[not(@type)]")
110
+ }
111
+ data.each do |k, v|
112
+ data[k] = v.text if v
113
+ end
114
+ data
115
+ end
116
+
117
+ def normalized_component_id(node)
118
+ Arclight::NormalizedId.new(node['id'].to_s).to_s
119
+ end
120
+
121
+ def normalized_collection_id(node)
122
+ Arclight::NormalizedId.new(node.document.at_xpath('//eadid').text).to_s
123
+ end
124
+
125
+ # This mimics similar behavior in Arclight::CustomDocument
126
+ def add_collection_creator_to_component(node, solr_doc)
127
+ field_name = Solrizer.solr_name('collection_creator', :displayable)
128
+ repository = solr_doc[Solrizer.solr_name('repository', :displayable)]
129
+ creators = node.xpath('//archdesc/did/origination[@label="creator"]/*/text()').map(&:text)
130
+ solr_doc[field_name] = creators - [repository]
131
+ end
132
+
133
+ def parent_check_list(node, root_path, element_path, results = [])
134
+ orginal_node = node
135
+ results = node.xpath("#{root_path}/#{element_path}").map(&:text)
136
+ # if current restriction return, else go up to parent and check
137
+ while node.parent.name == 'c' && results.blank?
138
+ parent = node.parent
139
+ results = parent.xpath("#{root_path}/#{element_path}").map(&:text)
140
+ node = parent
141
+ end
142
+ # If no parental results, check the collection
143
+ results = orginal_node.xpath("//archdesc/#{element_path}").map(&:text) if results.blank?
144
+ results.flatten # can't use with flatten! because that returns nil
145
+ end
146
+
147
+ def add_self_or_parents_restrictions(node, solr_doc)
148
+ field_name = Solrizer.solr_name('parent_access_restrict', :displayable)
149
+ solr_doc[field_name] = parent_check_list(node, './', 'accessrestrict/p/text()')
150
+ solr_doc[field_name]
151
+ end
152
+
153
+ def add_self_or_parents_terms(node, solr_doc)
154
+ field_name = Solrizer.solr_name('parent_access_terms', :displayable)
155
+ solr_doc[field_name] = parent_check_list(node, './', 'userestrict/p/text()')
156
+ solr_doc[field_name]
157
+ end
158
+ end
159
+ end