arclight 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +6 -47
  4. data/.rubocop_todo.yml +259 -0
  5. data/.travis.yml +15 -20
  6. data/README.md +17 -4
  7. data/app/assets/images/blacklight/compact.svg +15 -15
  8. data/app/assets/javascripts/arclight/arclight.js +1 -0
  9. data/app/assets/javascripts/arclight/collection_navigation.js +5 -2
  10. data/app/assets/javascripts/arclight/oembed_viewer.js +11 -4
  11. data/app/assets/javascripts/arclight/search_results.js +15 -0
  12. data/app/assets/stylesheets/arclight/modules/hierarchy_and_online_contents.scss +6 -3
  13. data/app/assets/stylesheets/arclight/modules/layout.scss +24 -0
  14. data/app/assets/stylesheets/arclight/modules/mastheads.scss +33 -0
  15. data/app/helpers/arclight_helper.rb +1 -1
  16. data/app/models/concerns/arclight/search_behavior.rb +1 -1
  17. data/app/views/arclight/repositories/_in_person_repository.html.erb +1 -1
  18. data/app/views/catalog/_component_contents.html.erb +16 -0
  19. data/app/views/catalog/_component_overview.html.erb +0 -6
  20. data/app/views/catalog/_context_card.html.erb +1 -1
  21. data/app/views/catalog/_custom_metadata.html.erb +1 -1
  22. data/app/views/catalog/_index_default.html.erb +1 -1
  23. data/app/views/catalog/_index_header.html.erb +2 -2
  24. data/app/views/catalog/_index_header_hierarchy_default.html.erb +2 -2
  25. data/app/views/catalog/_index_hierarchy_default.html.erb +1 -1
  26. data/app/views/catalog/_results_histogram.html.erb +6 -1
  27. data/app/views/catalog/_show_breadcrumbs_default.html.erb +19 -5
  28. data/app/views/catalog/_show_default.html.erb +10 -0
  29. data/app/views/catalog/_show_sidebar.html.erb +0 -8
  30. data/app/views/catalog/_show_upper_metadata_collection.html.erb +1 -0
  31. data/app/views/catalog/_show_upper_metadata_default.html.erb +14 -0
  32. data/app/views/shared/_header_navbar.html.erb +56 -44
  33. data/app/views/shared/_main_menu_links.html.erb +1 -1
  34. data/arclight.gemspec +11 -7
  35. data/config/i18n-tasks.yml +132 -0
  36. data/config/locales/arclight.en.yml +53 -52
  37. data/lib/arclight/engine.rb +1 -0
  38. data/lib/arclight/hash_absolute_xpath.rb +57 -0
  39. data/lib/arclight/missing_id_strategy.rb +21 -0
  40. data/lib/arclight/normalized_date.rb +19 -10
  41. data/lib/arclight/repository.rb +3 -20
  42. data/lib/arclight/shared_indexing_behavior.rb +1 -1
  43. data/lib/arclight/solr_ead_indexer_ext.rb +5 -9
  44. data/lib/arclight/traject/ead2_config.rb +475 -0
  45. data/lib/arclight/version.rb +1 -1
  46. data/lib/generators/arclight/install_generator.rb +14 -0
  47. data/lib/generators/arclight/templates/catalog_controller.rb +43 -40
  48. data/lib/tasks/index.rake +4 -2
  49. data/solr/conf/schema.xml +7 -2
  50. data/tasks/arclight.rake +5 -1
  51. data/template.rb +1 -1
  52. metadata +94 -28
  53. data/app/views/catalog/_arclight_document_show_header.html.erb +0 -15
  54. data/app/views/catalog/_arclight_document_show_header_collection.html.erb +0 -12
  55. data/app/views/catalog/_search_within_form.html.erb +0 -16
  56. data/app/views/catalog/_show_header.html.erb +0 -5
@@ -1,6 +1,6 @@
1
1
  <li class="nav-item <%= repositories_active_class %>">
2
2
  <%= link_to t('arclight.routes.repositories'), arclight_engine.repositories_path, class: 'nav-link' %>
3
3
  </li>
4
- <li class="nav-item <%= collection_active_class %>">
4
+ <li class="nav-item ml-3 <%= collection_active_class %>">
5
5
  <%= link_to t('arclight.routes.collections'), arclight_engine.collections_path, class: 'nav-link' %>
6
6
  </li>
@@ -23,19 +23,23 @@ Gem::Specification.new do |spec|
23
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
24
  spec.require_paths = ['lib']
25
25
 
26
- spec.add_dependency 'blacklight', '7.0.0.rc1'
27
- spec.add_dependency 'blacklight_range_limit', '7.0.0.rc2'
26
+ spec.add_dependency 'blacklight', '~> 7.0', '>= 7.0.1'
27
+ spec.add_dependency 'blacklight_range_limit', '~> 7.1'
28
28
  spec.add_dependency 'rails', '~> 5.0'
29
29
  spec.add_dependency 'solr_ead'
30
+ spec.add_dependency 'traject', '~> 3.0'
31
+ spec.add_dependency 'traject_plus'
30
32
 
31
- spec.add_development_dependency 'bundler', '~> 1.14'
33
+ spec.add_development_dependency 'bundler', '> 1.14'
32
34
  spec.add_development_dependency 'capybara'
33
- spec.add_development_dependency 'coveralls'
34
35
  spec.add_development_dependency 'engine_cart'
35
- spec.add_development_dependency 'poltergeist'
36
+ spec.add_development_dependency 'i18n-tasks'
36
37
  spec.add_development_dependency 'rake', '~> 12.0'
37
- spec.add_development_dependency 'rubocop', '~> 0.48.1'
38
- spec.add_development_dependency 'rubocop-rspec', '~> 1.15.0'
38
+ spec.add_development_dependency 'rubocop', '~> 0.74.0'
39
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.35'
39
40
  spec.add_development_dependency 'rspec-rails', '~> 3.0'
41
+ spec.add_development_dependency 'selenium-webdriver'
42
+ spec.add_development_dependency 'simplecov'
40
43
  spec.add_development_dependency 'solr_wrapper'
44
+ spec.add_development_dependency 'webdrivers'
41
45
  end
@@ -0,0 +1,132 @@
1
+ # i18n-tasks finds and manages missing and unused translations: https://github.com/glebm/i18n-tasks
2
+
3
+ # The "main" locale.
4
+ base_locale: en
5
+ ## All available locales are inferred from the data by default. Alternatively, specify them explicitly:
6
+ # locales: [es, fr]
7
+ ## Reporting locale, default: en. Available: en, ru.
8
+ # internal_locale: en
9
+
10
+ # Read and write translations.
11
+ data:
12
+ ## Translations are read from the file system. Supported format: YAML, JSON.
13
+ ## Provide a custom adapter:
14
+ # adapter: I18n::Tasks::Data::FileSystem
15
+
16
+ # Locale files or `File.find` patterns where translations are read from:
17
+ read:
18
+ ## Default:
19
+ # - config/locales/%{locale}.yml
20
+ ## More files:
21
+ - config/locales/**/*.%{locale}.yml
22
+
23
+ # Locale files to write new keys to, based on a list of key pattern => file rules. Matched from top to bottom:
24
+ # `i18n-tasks normalize -p` will force move the keys according to these rules
25
+ write:
26
+ ## For example, write devise and simple form keys to their respective files:
27
+ # - ['{devise, simple_form}.*', 'config/locales/\1.%{locale}.yml']
28
+ ## Catch-all default:
29
+ # - config/locales/%{locale}.yml
30
+
31
+ # External locale data (e.g. gems).
32
+ # This data is not considered unused and is never written to.
33
+ external:
34
+ ## Example (replace %#= with %=):
35
+ # - "<%#= %x[bundle show vagrant].chomp %>/templates/locales/%{locale}.yml"
36
+
37
+ ## Specify the router (see Readme for details). Valid values: conservative_router, pattern_router, or a custom class.
38
+ # router: conservative_router
39
+
40
+ yaml:
41
+ write:
42
+ # do not wrap lines at 80 characters
43
+ line_width: -1
44
+
45
+ ## Pretty-print JSON:
46
+ # json:
47
+ # write:
48
+ # indent: ' '
49
+ # space: ' '
50
+ # object_nl: "\n"
51
+ # array_nl: "\n"
52
+
53
+ # Find translate calls
54
+ search:
55
+ ## Paths or `File.find` patterns to search in:
56
+ # paths:
57
+ # - app/
58
+
59
+ ## Root directories for relative keys resolution.
60
+ # relative_roots:
61
+ # - app/controllers
62
+ # - app/helpers
63
+ # - app/mailers
64
+ # - app/presenters
65
+ # - app/views
66
+
67
+ ## Files or `File.fnmatch` patterns to exclude from search. Some files are always excluded regardless of this setting:
68
+ ## %w(*.jpg *.png *.gif *.svg *.ico *.eot *.otf *.ttf *.woff *.woff2 *.pdf *.css *.sass *.scss *.less *.yml *.json)
69
+ exclude:
70
+ - app/assets/images
71
+ - app/assets/fonts
72
+ - app/assets/videos
73
+
74
+ ## Alternatively, the only files or `File.fnmatch patterns` to search in `paths`:
75
+ ## If specified, this settings takes priority over `exclude`, but `exclude` still applies.
76
+ # only: ["*.rb", "*.html.slim"]
77
+
78
+ ## If `strict` is `false`, guess usages such as t("categories.#{category}.title"). The default is `true`.
79
+ # strict: true
80
+
81
+ ## Multiple scanners can be used. Their results are merged.
82
+ ## The options specified above are passed down to each scanner. Per-scanner options can be specified as well.
83
+ ## See this example of a custom scanner: https://github.com/glebm/i18n-tasks/wiki/A-custom-scanner-example
84
+
85
+ ## Translation Services
86
+ # translation:
87
+ # # Google Translate
88
+ # # Get an API key and set billing info at https://code.google.com/apis/console to use Google Translate
89
+ # google_translate_api_key: "AbC-dEf5"
90
+ # # DeepL Pro Translate
91
+ # # Get an API key and subscription at https://www.deepl.com/pro to use DeepL Pro
92
+ # deepl_api_key: "48E92789-57A3-466A-9959-1A1A1A1A1A1A"
93
+
94
+ ## Do not consider these keys missing:
95
+ ignore_missing:
96
+ - '{blacklight}.*'
97
+
98
+ ## Consider these keys used:
99
+ # ignore_unused:
100
+ # - 'activerecord.attributes.*'
101
+ # - '{devise,kaminari,will_paginate}.*'
102
+ # - 'simple_form.{yes,no}'
103
+ # - 'simple_form.{placeholders,hints,labels}.*'
104
+ # - 'simple_form.{error_notification,required}.:'
105
+
106
+ ## Exclude these keys from the `i18n-tasks eq-base' report:
107
+ # ignore_eq_base:
108
+ # all:
109
+ # - common.ok
110
+ # fr,es:
111
+ # - common.brand
112
+
113
+ ## Exclude these keys from the `i18n-tasks check-consistent-interpolations` report:
114
+ # ignore_inconsistent_interpolations:
115
+ # - 'activerecord.attributes.*'
116
+
117
+ ## Ignore these keys completely:
118
+ # ignore:
119
+ # - kaminari.*
120
+
121
+ ## Sometimes, it isn't possible for i18n-tasks to match the key correctly,
122
+ ## e.g. in case of a relative key defined in a helper method.
123
+ ## In these cases you can use the built-in PatternMapper to map patterns to keys, e.g.:
124
+ #
125
+ # <%# I18n::Tasks.add_scanner 'I18n::Tasks::Scanners::PatternMapper',
126
+ # only: %w(*.html.haml *.html.slim),
127
+ # patterns: [['= title\b', '.page_title']] %>
128
+ #
129
+ # The PatternMapper can also match key literals via a special %{key} interpolation, e.g.:
130
+ #
131
+ # <%# I18n::Tasks.add_scanner 'I18n::Tasks::Scanners::PatternMapper',
132
+ # patterns: [['\bSpree\.t[( ]\s*%{key}', 'spree.%{key}']] %>
@@ -1,65 +1,66 @@
1
+ ---
1
2
  en:
2
3
  arclight:
3
- masthead_heading: 'Archival Collections at Institution'
4
+ breadcrumb_separator: " » "
4
5
  date_range_histogram:
5
- show_hide: 'Show/hide date distribution'
6
+ hide: Hide date distribution
7
+ show: Show date distribution
6
8
  hierarchy:
7
- view_all: 'View'
8
- scope_and_contents: 'Scope and Contents'
9
+ scope_and_contents: Scope and Contents
10
+ view_all: View
11
+ masthead_heading: Archival Collections at Institution
9
12
  request:
10
- container: 'Request'
13
+ container: Request
11
14
  routes:
12
- home: 'Home'
13
- collections: 'Collections'
14
- repositories: 'Repositories'
15
- search_results: 'Search results'
15
+ collections: Collections
16
+ home: Home
17
+ repositories: Repositories
18
+ search_results: Search results
19
+ truncation:
20
+ view_less: view less ▼
21
+ view_more: view more ▶
16
22
  views:
17
23
  index:
18
- number_of_children:
19
- zero: 'No children'
20
- one: '1 child'
21
- other: '%{count} children'
22
24
  collection_search:
23
- count: 'collection'
24
- online_content_indicator: 'online content'
25
+ count: collection
26
+ number_of_children:
27
+ one: 1 child
28
+ other: "%{count} children"
29
+ zero: No children
30
+ online_content_indicator: online content
31
+ repositories:
32
+ number_of_collections:
33
+ one: 1 collection
34
+ other: "%{count} collections"
35
+ zero: No collections
36
+ view_all_collections: View all of our collections
37
+ view_more: View more
25
38
  show:
26
39
  collection_id: 'Collection ID: %{id}'
27
- overview: 'Overview'
28
- online_content: 'Online content'
29
- no_online_content: 'No online content'
30
- no_contents: 'No content inventory'
31
- search_within: 'Search within this collection'
32
- navigation_sidebar:
33
- title: 'Navigation overview'
34
40
  context_sidebar:
35
- in_person_field: 'In person'
36
- terms_field: 'Terms & Conditions'
37
- cite_field: 'How to cite this collection'
38
- component_terms_field: "Terms & Conditions"
39
- sections:
40
- summary_field: 'Summary'
41
- access_field: 'Access and Use'
42
- background_field: 'Background'
43
- scope_and_arrangement_field: 'Scope and Arrangement'
44
- related_field: 'Related'
45
- indexed_terms_field: 'Indexed Terms'
46
- admin_info_field: 'Administrative Information'
47
- component_field: 'About this %{level}'
48
- component_indexed_terms_field: 'Indexed Terms'
49
- collection_context_field: 'Collection Context'
50
- our_collections: 'Our Collections'
41
+ cite_field: How to cite this collection
42
+ component_terms_field: Terms & Conditions
43
+ in_person_field: In person
44
+ terms_field: Terms & Conditions
51
45
  download:
52
- default: 'Download'
53
- pdf: 'Collection PDF (%{size})'
54
- ead: 'Collection EAD (%{size})'
55
- repositories:
56
- number_of_collections:
57
- zero: 'No collections'
58
- one: '1 collection'
59
- other: '%{count} collections'
60
- view_more: 'View more'
61
- view_all_collections: 'View all of our collections'
62
- breadcrumb_separator: ' » '
63
- truncation:
64
- view_more: 'view more ▶'
65
- view_less: 'view less ▼'
46
+ default: Download
47
+ ead: Collection EAD (%{size})
48
+ pdf: Collection PDF (%{size})
49
+ navigation_sidebar:
50
+ title: Navigation overview
51
+ no_contents: No content inventory
52
+ no_online_content: No online content
53
+ online_content: Online content
54
+ our_collections: Our Collections
55
+ overview: Overview
56
+ sections:
57
+ access_field: Access and Use
58
+ admin_info_field: Administrative Information
59
+ background_field: Background
60
+ collection_context_field: Collection Context
61
+ component_field: About this %{level}
62
+ component_indexed_terms_field: Indexed Terms
63
+ indexed_terms_field: Indexed Terms
64
+ related_field: Related
65
+ scope_and_arrangement_field: Scope and Arrangement
66
+ summary_field: Summary
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'blacklight'
4
4
  require 'solr_ead'
5
+ require 'traject'
5
6
  require 'arclight/exceptions'
6
7
  require 'arclight/normalized_date'
7
8
  require 'arclight/normalized_id'
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ module Arclight
6
+ ##
7
+ # Take a Nokogiri node and get its absolute path (inserting our own indexes for component levels)
8
+ # and hash that outout. This is intended as a potential strategy for handling missing IDs in EADs.
9
+ class HashAbsoluteXpath
10
+ class << self
11
+ attr_writer :hash_algorithm
12
+
13
+ def hash_algorithm
14
+ return Digest::SHA1 unless defined? @hash_algorithm
15
+
16
+ @hash_algorithm
17
+ end
18
+ end
19
+
20
+ COMPONENT_NODE_NAME_REGEX = /^c\d{,2}$/.freeze
21
+ attr_reader :node
22
+ def initialize(node)
23
+ @node = node
24
+ end
25
+
26
+ def to_hexdigest
27
+ self.class.hash_algorithm.hexdigest(absolute_xpath)
28
+ end
29
+
30
+ def absolute_xpath
31
+ ancestor_tree = node.ancestors.map do |ancestor|
32
+ if ancestor.name =~ COMPONENT_NODE_NAME_REGEX
33
+ index = component_siblings_for_node(ancestor).index(ancestor)
34
+ "#{ancestor.name}#{index}"
35
+ else
36
+ ancestor.name
37
+ end
38
+ end
39
+
40
+ "#{[ancestor_tree.reverse, node.name].flatten.join('/')}#{current_index}"
41
+ end
42
+
43
+ private
44
+
45
+ def current_index
46
+ siblings.index(node)
47
+ end
48
+
49
+ def component_siblings_for_node(xml_node)
50
+ xml_node.parent.children.select { |n| n.name =~ COMPONENT_NODE_NAME_REGEX }
51
+ end
52
+
53
+ def siblings
54
+ @siblings ||= component_siblings_for_node(node)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'arclight/hash_absolute_xpath'
4
+
5
+ module Arclight
6
+ ##
7
+ # A class to configure a selected MissingIdStrategy.
8
+ # Defaults to Arclight::HashAbsoluteXpath
9
+ # This can be updated in an initializer to be any other class
10
+ class MissingIdStrategy
11
+ class << self
12
+ attr_writer :selected
13
+
14
+ def selected
15
+ return Arclight::HashAbsoluteXpath unless defined? @selected
16
+
17
+ @selected
18
+ end
19
+ end
20
+ end
21
+ end
@@ -7,16 +7,21 @@ module Arclight
7
7
  # @see http://www2.archivists.org/standards/DACS/part_I/chapter_2/4_date
8
8
  class NormalizedDate
9
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?
10
+ # @param [Array<String>] `bulk` from the `unitdate`
11
+ # @param [Array<String>] `other` from the `unitdate` when type is not specified
12
+ def initialize(inclusive, bulk = [], other = [])
13
+ @inclusive = (inclusive || []).map do |inclusive_text|
14
+ if inclusive_text.is_a? Array # of YYYY-YYYY for ranges
15
+ # NOTE: This code is not routable AFAICT in actual indexing.
16
+ # We pass arrays of strings (or xml nodes) here, and never a multidimensional array
17
+ year_range(inclusive_text)
18
+ elsif inclusive_text.present?
19
+ inclusive_text.strip
20
+ end
21
+ end&.join(', ')
22
+
23
+ @bulk = Array.wrap(bulk).compact.map(&:strip).join(', ')
24
+ @other = Array.wrap(other).compact.map(&:strip).join(', ')
20
25
  end
21
26
 
22
27
  # @return [String] the normalized title/date
@@ -28,6 +33,10 @@ module Arclight
28
33
 
29
34
  attr_reader :inclusive, :bulk, :other
30
35
 
36
+ def year_range(date_array)
37
+ YearRange.new(date_array.include?('/') ? date_array : date_array.map { |v| v.tr('-', '/') }).to_s
38
+ end
39
+
31
40
  # @see http://www2.archivists.org/standards/DACS/part_I/chapter_2/4_date for rules
32
41
  def normalize
33
42
  if inclusive.present?
@@ -7,31 +7,14 @@ module Arclight
7
7
  class Repository
8
8
  include ActiveModel::Conversion # for to_partial_path
9
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
10
+ attr_accessor :slug, :collection_count
28
11
 
29
12
  # @param [String] `slug` the unique identifier for the repository
30
13
  # @param [Hash] `data`
31
14
  def initialize(slug, data = {})
32
15
  @slug = slug
33
- FIELDS.each do |field|
34
- value = data[field.to_s]
16
+ data.each do |field, value|
17
+ self.class.attr_accessor field.to_sym
35
18
  send("#{field}=", value) if value.present?
36
19
  end
37
20
  end