├── .all-contributorsrc
├── .codeclimate.yml
├── .eslintrc
├── .github
└── workflows
│ └── ruby.yml
├── .gitignore
├── .rspec
├── .rubocop.yml
├── .rubocop_todo.yml
├── .solr_wrapper
├── CONTRIBUTING.md
├── CONTRIBUTORS.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── app
├── assets
│ ├── images
│ │ ├── arclight
│ │ │ └── logo.png
│ │ └── blacklight
│ │ │ ├── bookmark.svg
│ │ │ ├── collection.svg
│ │ │ ├── compact.svg
│ │ │ ├── container.svg
│ │ │ ├── ead.svg
│ │ │ ├── file.svg
│ │ │ ├── folder.svg
│ │ │ ├── list.svg
│ │ │ ├── minus.svg
│ │ │ ├── online.svg
│ │ │ ├── pdf.svg
│ │ │ ├── plus.svg
│ │ │ └── repository.svg
│ ├── javascripts
│ │ └── arclight
│ │ │ ├── arclight.js
│ │ │ ├── oembed_controller.js
│ │ │ └── truncate_controller.js
│ └── stylesheets
│ │ └── arclight
│ │ ├── application.scss
│ │ ├── build.scss
│ │ ├── modules
│ │ ├── collection_search.scss
│ │ ├── hierarchy_and_online_contents.scss
│ │ ├── highlights.scss
│ │ ├── icons.scss
│ │ ├── layout.scss
│ │ ├── mastheads.scss
│ │ ├── repositories.scss
│ │ ├── repository_card.scss
│ │ ├── search_form.scss
│ │ ├── search_results.scss
│ │ ├── show_collection.scss
│ │ └── truncator.scss
│ │ ├── responsive.scss
│ │ └── variables.scss
├── components
│ ├── arclight
│ │ ├── access_component.html.erb
│ │ ├── access_component.rb
│ │ ├── bookmark_component.html.erb
│ │ ├── bookmark_component.rb
│ │ ├── breadcrumb_component.rb
│ │ ├── breadcrumbs_hierarchy_component.html.erb
│ │ ├── breadcrumbs_hierarchy_component.rb
│ │ ├── collection_context_component.html.erb
│ │ ├── collection_context_component.rb
│ │ ├── collection_info_component.html.erb
│ │ ├── collection_info_component.rb
│ │ ├── collection_sidebar_component.html.erb
│ │ ├── collection_sidebar_component.rb
│ │ ├── constraints_component.html.erb
│ │ ├── constraints_component.rb
│ │ ├── document_collection_context_component.html.erb
│ │ ├── document_collection_context_component.rb
│ │ ├── document_collection_hierarchy_component.html.erb
│ │ ├── document_collection_hierarchy_component.rb
│ │ ├── document_component.html.erb
│ │ ├── document_component.rb
│ │ ├── document_components_hierarchy_component.html.erb
│ │ ├── document_components_hierarchy_component.rb
│ │ ├── document_download_component.html.erb
│ │ ├── document_download_component.rb
│ │ ├── embed_component.html.erb
│ │ ├── embed_component.rb
│ │ ├── expand_hierarchy_button_component.html.erb
│ │ ├── expand_hierarchy_button_component.rb
│ │ ├── group_component.html.erb
│ │ ├── group_component.rb
│ │ ├── header_component.html.erb
│ │ ├── header_component.rb
│ │ ├── hierarchy_toggle_component.html.erb
│ │ ├── hierarchy_toggle_component.rb
│ │ ├── index_metadata_field_component.html.erb
│ │ ├── index_metadata_field_component.rb
│ │ ├── masthead_component.html.erb
│ │ ├── masthead_component.rb
│ │ ├── metadata_section_component.html.erb
│ │ ├── metadata_section_component.rb
│ │ ├── oembed_viewer_component.html.erb
│ │ ├── oembed_viewer_component.rb
│ │ ├── online_content_filter_component.html.erb
│ │ ├── online_content_filter_component.rb
│ │ ├── online_status_indicator_component.rb
│ │ ├── repository_breadcrumb_component.html.erb
│ │ ├── repository_breadcrumb_component.rb
│ │ ├── repository_location_component.html.erb
│ │ ├── repository_location_component.rb
│ │ ├── search_bar_component.html.erb
│ │ ├── search_bar_component.rb
│ │ ├── search_result_breadcrumbs_component.html.erb
│ │ ├── search_result_breadcrumbs_component.rb
│ │ ├── search_result_component.html.erb
│ │ ├── search_result_component.rb
│ │ ├── search_result_title_component.html.erb
│ │ ├── search_result_title_component.rb
│ │ ├── sidebar_component.html.erb
│ │ ├── sidebar_component.rb
│ │ └── upper_metadata_layout_component.rb
│ └── blacklight
│ │ └── icons
│ │ ├── bookmark_component.rb
│ │ ├── collection_component.rb
│ │ ├── compact_component.rb
│ │ ├── container_component.rb
│ │ ├── ead_component.rb
│ │ ├── file_component.rb
│ │ ├── folder_component.rb
│ │ ├── minus_component.rb
│ │ ├── online_component.rb
│ │ ├── pdf_component.rb
│ │ ├── plus_component.rb
│ │ └── repository_component.rb
├── controllers
│ └── arclight
│ │ └── repositories_controller.rb
├── helpers
│ ├── arclight
│ │ ├── ead_format_helpers.rb
│ │ └── field_config_helpers.rb
│ └── arclight_helper.rb
├── models
│ ├── arclight
│ │ ├── document_downloads.rb
│ │ ├── parent.rb
│ │ ├── parents.rb
│ │ └── requests
│ │ │ ├── aeon_external_request.rb
│ │ │ ├── aeon_web_ead.rb
│ │ │ └── google_form.rb
│ └── concerns
│ │ └── arclight
│ │ ├── catalog.rb
│ │ ├── search_behavior.rb
│ │ └── solr_document.rb
├── presenters
│ └── arclight
│ │ ├── index_presenter.rb
│ │ └── show_presenter.rb
└── views
│ ├── arclight
│ ├── .keep
│ ├── _requests.html.erb
│ ├── repositories
│ │ ├── _repository.html.erb
│ │ ├── index.html.erb
│ │ └── show.html.erb
│ └── requests
│ │ ├── _aeon_external_request_endpoint.html.erb
│ │ ├── _aeon_web_ead.html.erb
│ │ └── _google_form.html.erb
│ ├── catalog
│ ├── _document_list.html.erb
│ ├── _group.html.erb
│ ├── _group_toggle.html.erb
│ ├── _search_results_header.html.erb
│ ├── hierarchy.html.erb
│ └── index.html.erb
│ └── shared
│ ├── _breadcrumbs.html.erb
│ └── _main_menu_links.html.erb
├── arclight.gemspec
├── bin
├── console
├── rails
└── setup
├── config
├── breadcrumbs.rb
├── i18n-tasks.yml
├── importmap.rb
├── locales
│ └── arclight.en.yml
├── repositories.yml
└── routes.rb
├── docker-compose.yml
├── lib
├── arclight.rb
├── arclight
│ ├── digital_object.rb
│ ├── engine.rb
│ ├── exceptions.rb
│ ├── hash_absolute_xpath.rb
│ ├── level_label.rb
│ ├── missing_id_strategy.rb
│ ├── normalized_date.rb
│ ├── normalized_id.rb
│ ├── normalized_title.rb
│ ├── repository.rb
│ ├── routes.rb
│ ├── routes
│ │ └── hierarchy.rb
│ ├── traject
│ │ ├── ead2_component_config.rb
│ │ ├── ead2_config.rb
│ │ └── nokogiri_namespaceless_reader.rb
│ ├── version.rb
│ └── year_range.rb
├── generators
│ └── arclight
│ │ ├── install_generator.rb
│ │ ├── templates
│ │ ├── arclight.scss
│ │ ├── catalog_controller.rb
│ │ └── config
│ │ │ ├── downloads.yml
│ │ │ ├── locales
│ │ │ └── arclight.en.yml
│ │ │ └── repositories.yml
│ │ └── update_generator.rb
└── tasks
│ └── index.rake
├── package.json
├── solr
└── conf
│ ├── _rest_managed.json
│ ├── admin-extra.html
│ ├── elevate.xml
│ ├── mapping-ISOLatin1Accent.txt
│ ├── protwords.txt
│ ├── schema.xml
│ ├── solrconfig.xml
│ ├── spellings.txt
│ ├── stopwords.txt
│ ├── stopwords_en.txt
│ ├── stopwords_punctuation.txt
│ ├── synonyms.txt
│ └── xslt
│ ├── example.xsl
│ ├── example_atom.xsl
│ ├── example_rss.xsl
│ └── luke.xsl
├── spec
├── arclight_spec.rb
├── components
│ └── arclight
│ │ ├── breadcrumb_component_spec.rb
│ │ ├── collection_sidebar_component_spec.rb
│ │ ├── document_download_component_spec.rb
│ │ ├── expand_hierarchy_button_component_spec.rb
│ │ ├── hierarchy_toggle_component_spec.rb
│ │ ├── repository_location_component_spec.rb
│ │ └── search_bar_component_spec.rb
├── controllers
│ ├── arclight
│ │ └── repositories_controller_spec.rb
│ └── catalog_controller_spec.rb
├── features
│ ├── aeon_web_ead_request_spec.rb
│ ├── arclight_spec.rb
│ ├── autocomplete_spec.rb
│ ├── bookmarks_spec.rb
│ ├── collection_context_spec.rb
│ ├── collection_filtering_spec.rb
│ ├── collection_page_spec.rb
│ ├── compact_search_results_spec.rb
│ ├── component_page_spec.rb
│ ├── document_tools_spec.rb
│ ├── fielded_search_results_spec.rb
│ ├── google_form_request_spec.rb
│ ├── grouped_results_spec.rb
│ ├── highlighted_search_results_spec.rb
│ ├── item_breadcrumbs_spec.rb
│ ├── many_component_ead_spec.rb
│ ├── masthead_links_spec.rb
│ ├── online_content_spec.rb
│ ├── repositories_page_spec.rb
│ ├── search_breadcrumb_spec.rb
│ ├── search_query_spec.rb
│ ├── search_results_spec.rb
│ └── traject
│ │ └── ead2_indexing_spec.rb
├── fixtures
│ ├── config
│ │ ├── downloads.yml
│ │ └── repositories.yml
│ ├── ead
│ │ ├── nlm
│ │ │ ├── alphaomegaalpha.xml
│ │ │ └── ncaids544-id-test.xml
│ │ ├── sample
│ │ │ ├── deeply-nested-components.xml
│ │ │ ├── large-components-list.xml
│ │ │ └── no-ids-recordgrp-level.xml
│ │ └── sul-spec
│ │ │ ├── a0011.xml
│ │ │ ├── m0198_from_ASpace.xml
│ │ │ └── pc0170.xml
│ └── xsd
│ │ └── ead.xsd
├── helpers
│ ├── arclight
│ │ └── ead_format_helpers_spec.rb
│ └── arclight_helper_spec.rb
├── i18n_spec.rb
├── lib
│ └── arclight
│ │ ├── digital_object_spec.rb
│ │ ├── hash_absolute_xpath_spec.rb
│ │ ├── level_label_spec.rb
│ │ ├── missing_id_strategy_spec.rb
│ │ ├── normalized_date_spec.rb
│ │ ├── normalized_id_spec.rb
│ │ ├── normalized_title_spec.rb
│ │ ├── repository_spec.rb
│ │ └── year_range_spec.rb
├── models
│ ├── arclight
│ │ ├── document_downloads_spec.rb
│ │ ├── parents_spec.rb
│ │ └── requests
│ │ │ ├── aeon_external_request_spec.rb
│ │ │ ├── aeon_web_ead_spec.rb
│ │ │ └── google_form_spec.rb
│ └── concerns
│ │ └── arclight
│ │ ├── search_behavior_spec.rb
│ │ └── solr_document_spec.rb
├── presenters
│ └── arclight
│ │ ├── index_presenter_spec.rb
│ │ └── show_presenter_spec.rb
├── routing
│ ├── arclight
│ │ └── repositories_spec.rb
│ └── collections_spec.rb
├── spec_helper.rb
├── support
│ └── controller_level_helpers.rb
├── test_app_templates
│ ├── Gemfile.extra
│ └── lib
│ │ └── generators
│ │ └── test_app_generator.rb
└── views
│ ├── _requests.html.erb_spec.rb
│ └── repositories
│ ├── index.html.erb_spec.rb
│ └── show.html.erb_spec.rb
├── tasks
└── arclight.rake
└── template.rb
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | version: "2"
2 | exclude_patterns:
3 | - "app/assets/javascripts/arclight/context_navigation.js"
4 | - "vendor/assets/javascripts/**/*.js"
5 | - "spec/**/*.rb"
6 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true
4 | },
5 | "parserOptions": {
6 | "ecmaVersion": 2022,
7 | "sourceType": "module"
8 | },
9 | "extends": "airbnb-base/legacy",
10 | "globals": {
11 | "Blacklight": true,
12 | "bootstrap": true,
13 | "Stimulus": true,
14 | },
15 | "rules": {
16 | "no-param-reassign": [2, { "props": false }],
17 | "func-names": ["error", "never"],
18 | "indent": ["warn"],
19 | "quotes": ["error", "single", { "avoidEscape": true }],
20 | "semi": ["warn", "never"]
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.github/workflows/ruby.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 |
9 | jobs:
10 | test:
11 | runs-on: ubuntu-latest
12 | name: test (ruby ${{ matrix.ruby }} / rails ${{ matrix.rails_version }} ${{ matrix.additional_name }})
13 | strategy:
14 | matrix:
15 | rails_version: [8.0.1]
16 | ruby: ["3.4"]
17 | additional_engine_cart_rails_options: [""]
18 | additional_name: [""]
19 | include:
20 | - ruby: "3.3"
21 | rails_version: 7.2.2.1
22 | - ruby: "3.2"
23 | rails_version: 7.1.5.1
24 | - ruby: "3.4"
25 | rails_version: "8.0.1"
26 | blacklight_version: 9.0.0.beta1
27 | additional_name: "/ Blacklight 9"
28 | env:
29 | RAILS_VERSION: ${{ matrix.rails_version }}
30 | ENGINE_CART_RAILS_OPTIONS: "--skip-git --skip-listen --skip-spring --skip-keeps --skip-kamal --skip-solid --skip-coffee --skip-test --css bootstrap -a propshaft -j importmap ${{ matrix.additional_engine_cart_rails_options }}"
31 | BLACKLIGHT_VERSION: ${{ matrix.blacklight_version }}
32 | steps:
33 | - uses: actions/checkout@v4
34 | - name: Set up Ruby ${{ matrix.ruby }}
35 | uses: ruby/setup-ruby@v1
36 | with:
37 | bundler: latest
38 | ruby-version: ${{ matrix.ruby }}
39 | - name: Install dependencies with Rails ${{ matrix.rails_version }}
40 | run: bundle install
41 | - name: Run tests
42 | run: bundle exec rake ci
43 | lint:
44 | runs-on: ubuntu-latest
45 | steps:
46 | - name: Checkout code
47 | uses: actions/checkout@v4
48 | - name: Install Ruby and gems
49 | uses: ruby/setup-ruby@v1
50 | with:
51 | bundler: latest
52 | ruby-version: 3.2
53 | - name: Install dependencies with Bundler
54 | run: bundle install
55 | - name: Install dependencies with yarn
56 | run: yarn install
57 | - name: Lint Ruby files
58 | run: bundle exec rake rubocop eslint
59 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.bundle/
2 | /.ruby-version
3 | /.yardoc
4 | /Gemfile.lock
5 | /yarn.lock
6 | /_yardoc/
7 | /coverage/
8 | /doc/
9 | /pkg/
10 | /spec/reports/
11 | /tmp/
12 | /app/assets/builds/
13 |
14 | # rspec failure tracking
15 | .rspec_status
16 |
17 | .internal_test_app
18 |
19 | /node_modules/
20 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 |
--------------------------------------------------------------------------------
/.solr_wrapper:
--------------------------------------------------------------------------------
1 | # Place any default configuration for solr_wrapper here
2 | # port: 8983
3 | collection:
4 | dir: solr/conf
5 | name: blacklight-core
6 | version: 9.7.0
7 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing
2 |
3 | ArcLight is a collaborative open source project where contributions are welcome. This contributing guide is borrowed in part from the [GeoBlacklight Contributing Guide](https://github.com/geoblacklight/geoblacklight/blob/main/CONTRIBUTING.md).
4 |
5 | ### Reporting issues
6 |
7 | Did you find a bug in ArcLight or interested in a new feature? Please make sure to add an issue for it in the [issue tracker](https://github.com/projectblacklight/arclight/issues) as follows:
8 |
9 | - Make sure you have a [GitHub account](https://github.com/signup/free)
10 | - Submit a new [Github issue](https://github.com/projectblacklight/arclight/issues) by:
11 | - Clearly describing the issue
12 | - Provide a descriptive summary
13 | - Explain the expected behavior
14 | - Explain the actual behavior
15 | - Provide steps to reproduce the actual behavior
16 |
17 | ### Contributing code or documentation
18 |
19 | ArcLight also welcomes code and documentation contributions. We follow the [pull request](https://help.github.com/articles/using-pull-requests/) model for contributing on GitHub.
20 |
21 | #### Pull request overview
22 |
23 | 1. Fork it ( http://github.com/projectblacklight/arclight/fork )
24 | 2. Create your feature branch (`git checkout -b my-new-feature`)
25 | 3. Commit your changes (`git commit -am 'Add some feature'`)
26 | 4. Push to the branch (`git push origin my-new-feature`)
27 | 5. Create new Pull Request
28 |
29 | #### Merging Changes
30 |
31 | Here are some general rules to follow for merging pull requests.
32 |
33 | - It is considered "poor form" to merge your own request.
34 | - Please take the time to review the changes and get a sense of what is being changed. Things to consider:
35 | - Does the commit message explain what is going on?
36 | - Does the code changes have tests? _Not all changes need new tests, some changes are refactorings_
37 | - Do all new methods, modules, and classes have comments? Do changed methods, modules, and classes have comments?
38 | - Does the commit contain more than it should? Are two separate concerns being addressed in one commit?
39 | - Did the Travis tests complete successfully?
40 | - If you are uncertain, bring other contributors into the conversation by creating a comment that includes their `@username`.
41 | - If you like the pull request, but want others to chime in, create a +1 comment and tag a user.
42 |
43 | If you wish to ask questions or participate further, see our general [README](https://github.com/projectblacklight/arclight/blob/main/README.md).
44 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source 'https://rubygems.org'
4 |
5 | # Specify your gem's dependencies in arclight.gemspec
6 | gemspec
7 |
8 | # BEGIN ENGINE_CART BLOCK
9 | # engine_cart: 2.5.0
10 | # engine_cart stanza: 2.5.0
11 | # the below comes from engine_cart, a gem used to test this Rails engine gem in the context of a Rails app.
12 | file = File.expand_path('Gemfile', ENV['ENGINE_CART_DESTINATION'] || ENV['RAILS_ROOT'] || File.expand_path('.internal_test_app', File.dirname(__FILE__)))
13 | if File.exist?(file)
14 | begin
15 | eval_gemfile file
16 | rescue Bundler::GemfileError => e
17 | Bundler.ui.warn '[EngineCart] Skipping Rails application dependencies:'
18 | Bundler.ui.warn e.message
19 | end
20 | else
21 | Bundler.ui.warn "[EngineCart] Unable to find test application dependencies in #{file}, using placeholder dependencies"
22 |
23 | if ENV['RAILS_VERSION']
24 | if ENV['RAILS_VERSION'] == 'edge'
25 | gem 'rails', github: 'rails/rails'
26 | ENV['ENGINE_CART_RAILS_OPTIONS'] = '--edge --skip-turbolinks'
27 | else
28 | gem 'rails', ENV['RAILS_VERSION']
29 | end
30 |
31 | case ENV['RAILS_VERSION']
32 | when /^6.0/
33 | gem 'sass-rails', '>= 6'
34 | gem 'webpacker', '~> 4.0'
35 | when /^5.[12]/
36 | gem 'sass-rails', '~> 5.0'
37 | gem 'sprockets', '~> 3.7'
38 | gem 'thor', '~> 0.20'
39 | end
40 | end
41 | end
42 | # END ENGINE_CART BLOCK
43 |
44 | # Used by engine_cart if you need to change a specific version of a dependency
45 | eval_gemfile File.expand_path('spec/test_app_templates/Gemfile.extra', File.dirname(__FILE__))
46 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2017 The Board of Trustees of the Leland Stanford Junior University
2 | Copyright (c) 2017, Regents of the University of Michigan.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bundler/gem_tasks'
4 | require 'rspec/core/rake_task'
5 |
6 | RSpec::Core::RakeTask.new(:spec)
7 |
8 | require 'rubocop/rake_task'
9 | RuboCop::RakeTask.new(:rubocop)
10 |
11 | Dir.glob('./tasks/*.rake').each { |f| load f }
12 | Dir.glob('./lib/tasks/*.rake').each { |f| load f }
13 |
14 | require 'engine_cart/rake_task'
15 |
16 | task default: %i[rubocop eslint ci]
17 |
--------------------------------------------------------------------------------
/app/assets/images/arclight/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/projectblacklight/arclight/2fcfe481afcec80a5fe78b6ce45a415c70b123e9/app/assets/images/arclight/logo.png
--------------------------------------------------------------------------------
/app/assets/images/blacklight/bookmark.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/assets/images/blacklight/collection.svg:
--------------------------------------------------------------------------------
1 |
5 |
6 |
--------------------------------------------------------------------------------
/app/assets/images/blacklight/compact.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/assets/images/blacklight/container.svg:
--------------------------------------------------------------------------------
1 |
5 |
6 |
--------------------------------------------------------------------------------
/app/assets/images/blacklight/ead.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/assets/images/blacklight/file.svg:
--------------------------------------------------------------------------------
1 |
5 |
6 |
--------------------------------------------------------------------------------
/app/assets/images/blacklight/folder.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/assets/images/blacklight/list.svg:
--------------------------------------------------------------------------------
1 | file
2 |
--------------------------------------------------------------------------------
/app/assets/images/blacklight/minus.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/images/blacklight/online.svg:
--------------------------------------------------------------------------------
1 |
5 |
6 |
--------------------------------------------------------------------------------
/app/assets/images/blacklight/pdf.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/assets/images/blacklight/plus.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/images/blacklight/repository.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/assets/javascripts/arclight/arclight.js:
--------------------------------------------------------------------------------
1 | import Truncate from 'arclight/truncate_controller'
2 | Stimulus.register('arclight-truncate', Truncate)
3 | import Oembed from 'arclight/oembed_controller'
4 | Stimulus.register('arclight-oembed', Oembed)
5 |
--------------------------------------------------------------------------------
/app/assets/javascripts/arclight/oembed_controller.js:
--------------------------------------------------------------------------------
1 | import { Controller } from '@hotwired/stimulus'
2 |
3 | export default class OembedController extends Controller {
4 | static values = {
5 | url: String
6 | }
7 |
8 | connect() {
9 | const loadedAttr = this.element.getAttribute('loaded')
10 |
11 | if (loadedAttr && loadedAttr === 'loaded') {
12 | return
13 | }
14 |
15 | fetch(this.urlValue)
16 | .then((response) => {
17 | if (response.ok) return response.text()
18 | throw new Error(`HTTP error, status = ${response.status}`)
19 | })
20 | .then((body) => {
21 | const oEmbedEndPoint = this.findOEmbedEndPoint(body)
22 | if (!oEmbedEndPoint || oEmbedEndPoint.length === 0) {
23 | console.warn(`No oEmbed endpoint found in
at ${this.urlValue}`)
24 | return
25 | }
26 | this.loadEndPoint(oEmbedEndPoint)
27 | }).catch(error => {
28 | console.error(error)
29 | })
30 | }
31 |
32 | // We are choosing not to make this class static so that downstream classes
33 | // can override it and access values to populate extraParams.
34 | // E.g. https://github.com/sul-dlss/vt-arclight/blob/main/app/javascript/controllers/sul_embed_controller.js
35 | findOEmbedEndPoint(body, extraParams = {}) { // eslint-disable-line class-methods-use-this
36 | // Parse out link elements so image assets are not loaded
37 | const template = document.createElement('template')
38 | template.innerHTML = body.match(/ a list of metadata section names
17 | def section_names
18 | return Array(collection_access_items) if collection?
19 |
20 | Array(component_access_items)
21 | end
22 |
23 | delegate :collection_access_items, :component_access_items, to: :show_config
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/components/arclight/bookmark_component.html.erb:
--------------------------------------------------------------------------------
1 | <% if current_or_guest_user %>
2 | <%= form_tag(bookmark_path,
3 | authenticity_token: false,
4 | method: bookmarked? ? :delete : :put,
5 | class: "bookmark-toggle ms-2",
6 | data: {
7 | 'doc-id' => document.id,
8 | present: "#{blacklight_icon(:bookmark).html_safe}#{t('blacklight.search.bookmarks.present')} ",
9 | absent: "#{blacklight_icon(:bookmark).html_safe}#{t('blacklight.search.bookmarks.absent')} ",
10 | inprogress: t('blacklight.search.bookmarks.inprogress')
11 | }) do %>
12 |
13 |
14 | >
15 | <%= bookmarked? ? "#{blacklight_icon(:bookmark).html_safe}#{t('blacklight.search.bookmarks.present')} ".html_safe : "#{blacklight_icon(:bookmark).html_safe}#{t('blacklight.search.bookmarks.absent')} ".html_safe %>
16 |
17 |
18 |
19 | <%= submit_tag(t(bookmarked? ? 'remove.button' : 'add.button', scope: 'blacklight.bookmarks'),
20 | id: "bookmark_toggle_#{document.id.to_s.parameterize}",
21 | class: "bookmark-#{bookmarked? ? 'remove' : 'add'} btn btn-outline-secondary") %>
22 | <% end %>
23 | <% else %>
24 |
25 | <% end %>
26 |
--------------------------------------------------------------------------------
/app/components/arclight/bookmark_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # A component to render a bookmark button for a document
5 | class BookmarkComponent < Blacklight::Document::BookmarkComponent
6 | delegate :current_or_guest_user, :blacklight_icon, to: :helpers
7 | attr_accessor :document
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/components/arclight/breadcrumb_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Display the document hierarchy as "breadcrumbs"
5 | class BreadcrumbComponent < ViewComponent::Base
6 | # @param [Integer] count if provided the number of bookmarks is limited to this number
7 | def initialize(document:, count: nil, offset: 0)
8 | @document = document
9 | @count = count
10 | @offset = offset
11 | super
12 | end
13 |
14 | def call
15 | breadcrumb_links = components.drop(@offset)
16 |
17 | if @count && breadcrumb_links.length > @count
18 | breadcrumb_links = breadcrumb_links.first(@count)
19 | breadcrumb_links << tag.li('…'.html_safe, class: 'breadcrumb-item')
20 | end
21 | tag.ol class: 'breadcrumb' do
22 | safe_join(breadcrumb_links)
23 | end
24 | end
25 |
26 | def components
27 | return to_enum(:components) unless block_given?
28 |
29 | yield build_repository_link
30 |
31 | @document.parents.each do |parent|
32 | yield tag.li(class: 'breadcrumb-item') { link_to(parent.label, solr_document_path(parent.id)) }
33 | end
34 | end
35 |
36 | def build_repository_link
37 | render Arclight::RepositoryBreadcrumbComponent.new(document: @document)
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/app/components/arclight/breadcrumbs_hierarchy_component.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to t('arclight.routes.home'), root_path %>
4 |
5 |
6 | <%= blacklight_icon :repository, classes: 'al-repository-content-icon' %>
7 | <%= repository %>
8 |
9 | <% if collection %>
10 |
11 | <%= blacklight_icon :collection, classes: 'al-collection-content-icon' %>
12 | <%= link_to collection.label, solr_document_path(collection.id) %>
13 |
14 |
15 | <% parents_under_collection.each do |parent| %>
16 |
17 | <%= link_to parent.label, solr_document_path(parent.id) %>
18 |
19 | <% end %>
20 |
21 |
22 | <%= @presenter.heading %>
23 |
24 |
25 |
26 | <% else # this is a collection %>
27 |
28 | <%= blacklight_icon :collection, classes: 'al-collection-content-icon' %>
29 | <%= @presenter.heading %>
30 |
31 | <% end %>
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/components/arclight/breadcrumbs_hierarchy_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render the hierarchy for a document
5 | class BreadcrumbsHierarchyComponent < ViewComponent::Base
6 | delegate :document, to: :@presenter
7 | delegate :blacklight_icon, to: :helpers
8 |
9 | def initialize(presenter:)
10 | super
11 |
12 | @presenter = presenter
13 | collections, @parents_under_collection = document.parents.partition(&:collection?)
14 | @collection = collections.first
15 | end
16 |
17 | attr_reader :collection, :parents_under_collection
18 |
19 | def repository
20 | return tag.span(t('arclight.show_breadcrumb_label')) if document.repository_config.blank?
21 |
22 | link_to(document.repository_config.name, helpers.arclight_engine.repository_path(document.repository_config.slug))
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/components/arclight/collection_context_component.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Collection
5 |
<%= title %>
6 |
7 |
8 | <%= document_download %>
9 | <%= collection_info %>
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/components/arclight/collection_context_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render various actions for a collection (e.g. requesting, download links, etc)
5 | class CollectionContextComponent < ViewComponent::Base
6 | def initialize(presenter:, download_component:)
7 | super
8 |
9 | @collection = presenter.document.collection
10 | @download_component = download_component
11 | end
12 |
13 | attr_reader :collection
14 |
15 | def title
16 | collection.normalized_title
17 | end
18 |
19 | def document_download
20 | render @download_component.new(downloads: collection.downloads) || Arclight::DocumentDownloadComponent.new(downloads: collection.downloads)
21 | end
22 |
23 | def collection_info
24 | render Arclight::CollectionInfoComponent.new(collection: collection)
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/components/arclight/collection_info_component.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= info_icon %> <%= t('.more_info') %>
6 |
7 |
8 |
9 |
10 | <%= t('.collection_id') %>
11 | <%= collection_unitid %>
12 |
13 |
14 | <%= t('.total_components') %>
15 | <%= total_component_count %>
16 |
17 |
18 | <%= t('.online_items') %>
19 | <%= tag.span blacklight_icon(:online), class: 'al-online-content-icon' %><%= online_item_count %>
20 |
21 |
22 | <%= t('.last_indexed') %>
23 | <%= last_indexed.strftime('%F') %>
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/components/arclight/collection_info_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render information about the collection
5 | class CollectionInfoComponent < ViewComponent::Base
6 | def initialize(collection:)
7 | super
8 |
9 | @collection = collection
10 | end
11 |
12 | attr_reader :collection
13 |
14 | delegate :total_component_count, :online_item_count, :last_indexed, :collection_unitid, to: :collection
15 | delegate :blacklight_icon, to: :helpers
16 |
17 | def info_icon
18 | icon = <<~SVG
19 |
20 |
21 |
22 | SVG
23 | icon.html_safe # rubocop:disable Rails/OutputSafety
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/components/arclight/collection_sidebar_component.html.erb:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/components/arclight/collection_sidebar_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Draw the links to the collection info in the sidebar
5 | class CollectionSidebarComponent < ViewComponent::Base
6 | def initialize(document:, partials:, collection_presenter:)
7 | super
8 |
9 | @document = document
10 | @partials = Array(partials)
11 | @collection_presenter = collection_presenter
12 | end
13 |
14 | attr_reader :document, :partials, :collection_presenter
15 |
16 | def has_section?(section)
17 | # Access field data comes from repositories.yml not from solr, so handle it in a different way.
18 | return true if section == :access_field
19 |
20 | collection_presenter.with_field_group(section).fields_to_render.any?
21 | end
22 |
23 | def document_section_path(section)
24 | [document_path, section_anchor(section)].join
25 | end
26 |
27 | def section_label(section)
28 | t("arclight.views.show.sections.#{section}")
29 | end
30 |
31 | def document_path
32 | @document_path ||= solr_document_path(document.root)
33 | end
34 |
35 | def section_anchor(section)
36 | "##{t("arclight.views.show.sections.#{section}").parameterize}"
37 | end
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/app/components/arclight/constraints_component.html.erb:
--------------------------------------------------------------------------------
1 | <% if repository %>
2 |
3 | <%= render repository %>
4 |
5 | <% end %>
6 |
7 | <%= render Blacklight::ConstraintsComponent.new(**@kwargs) %>
8 |
--------------------------------------------------------------------------------
/app/components/arclight/constraints_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Extend the upstream constraints with breadcrumbs and
5 | # repository context information
6 | class ConstraintsComponent < Blacklight::ConstraintsComponent
7 | def initialize(**kwargs)
8 | super
9 |
10 | @kwargs = kwargs
11 | end
12 |
13 | def repository
14 | @repository ||= helpers.repository_faceted_on
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/components/arclight/document_collection_context_component.html.erb:
--------------------------------------------------------------------------------
1 | <%= tag.tr(
2 | id: @document.id,
3 | data: {
4 | 'document-id': @document.id.to_s.parameterize,
5 | 'document-counter': @counter,
6 | },
7 | itemscope: true,
8 | itemtype: @document.itemtype,
9 | class: classes.join(' ')) do %>
10 |
11 |
12 | <%= helpers.link_to_document document, counter: @counter %>
13 | <% if document.children? %>
14 | <%= document.number_of_children %><%= t(:'arclight.views.index.number_of_components', count: document.number_of_children) %>
15 | <% end %>
16 |
17 |
18 |
19 | <%= tag.td class: 'text-muted' do %>
20 | <%= document.containers.join(', ') %>
21 | <% end %>
22 |
23 | <%= tag.td class: 'text-end' do %>
24 | <%= helpers.render_index_doc_actions document, wrapping_class: 'd-inline-flex justify-content-end' %>
25 | <% end %>
26 |
27 |
28 | <%= render Arclight::IndexMetadataFieldComponent.with_collection(@presenter&.field_presenters.select { |field| field.field_config.collection_context }, classes: ['col pl-0 my-0']) %>
29 | <% end %>
30 |
--------------------------------------------------------------------------------
/app/components/arclight/document_collection_context_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Display a single document in the collection
5 | class DocumentCollectionContextComponent < Arclight::SearchResultComponent
6 | # @param [SolrDocument] document
7 | def initialize(document: nil, blacklight_config: nil, **kwargs)
8 | super(document: document, **kwargs)
9 | @blacklight_config = blacklight_config
10 | end
11 |
12 | attr_reader :blacklight_config
13 |
14 | def classes
15 | (super - ['row'] + ['al-collection-context']).flatten
16 | end
17 |
18 | private
19 |
20 | def online_status
21 | render online_status_component.new(document: @document)
22 | end
23 |
24 | def online_status_component
25 | blacklight_config.show.online_status_component || Arclight::OnlineStatusIndicatorComponent
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/app/components/arclight/document_collection_hierarchy_component.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_tag :li,
2 | id: "#{@document.id}-hierarchy-item",
3 | data: {
4 | 'document-id': @document.id.to_s.parameterize,
5 | 'document-counter': @counter,
6 | },
7 | itemscope: true,
8 | itemtype: @document.itemtype,
9 | class: (classes.flatten + ['al-collection-context', ('al-hierarchy-highlight' if current_target?)].compact).join(' ') do %>
10 |
27 |
28 | <%= render Arclight::IndexMetadataFieldComponent.with_collection(@presenter&.field_presenters.select { |field| field.field_config.collection_context }, classes: ['col pl-0 my-0']) %>
29 | <% if document.number_of_children > 0 %>
30 | <%= content_tag(:div, id: "collapsible-hierarchy-#{document.id}",
31 | class: "collapse al-collection-context-collapsible al-hierarchy-level-#{document.component_level} #{'show' if show_expanded?}"
32 | ) do %>
33 | <%= render Arclight::DocumentComponentsHierarchyComponent.new(document: @document, target_index: target_index) %>
34 | <% end %>
35 | <% end %>
36 | <% end %>
37 |
--------------------------------------------------------------------------------
/app/components/arclight/document_collection_hierarchy_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Display a single document in the collection hierarchy
5 | class DocumentCollectionHierarchyComponent < Arclight::SearchResultComponent
6 | # @param [Blacklight::DocumentPresenter] document
7 | # @param [String] nest_path determines which element to highlight
8 | def initialize(document: nil, nest_path: nil, blacklight_config: nil, **kwargs)
9 | super(document: document, **kwargs)
10 | @nest_path = nest_path
11 | @blacklight_config = blacklight_config
12 | end
13 |
14 | attr_reader :blacklight_config
15 |
16 | def classes
17 | super - ['row']
18 | end
19 |
20 | # we want to eager-load this document's children if we're in the
21 | # target document's component hierarchy
22 | def show_expanded?
23 | within_original_tree?
24 | end
25 |
26 | # Solr nest paths are constructed using the path name and an index, e.g.
27 | # `/components#5/components#3`.
28 | #
29 | # @return [String] the targeted index for this level of the hierarchy
30 | def target_index
31 | return -1 unless within_original_tree?
32 |
33 | remaining_path = nest_path.sub("#{@document.nest_path}/", '')
34 | current_component, _rest = remaining_path.split('/', 2)
35 | _name, index = current_component.split('#', 2)
36 |
37 | index&.to_i
38 | end
39 |
40 | private
41 |
42 | # We're in the targeted document's original tree if the target nest path
43 | # includes this document's path.
44 | def within_original_tree?
45 | nest_path&.start_with? "#{@document.nest_path}/"
46 | end
47 |
48 | def current_target?
49 | nest_path == @document.nest_path
50 | end
51 |
52 | def online_status
53 | render online_status_component.new(document: @document)
54 | end
55 |
56 | def online_status_component
57 | blacklight_config.show.online_status_component || Arclight::OnlineStatusIndicatorComponent
58 | end
59 |
60 | attr_reader :nest_path
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/app/components/arclight/document_component.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= render breadcrumb_component.new(presenter: presenter) %>
5 |
6 |
7 |
8 |
9 |
10 | <%= content_tag :h1 do %>
11 | <%= document.normalized_title %>
12 | <% end %>
13 | <%= render 'arclight/requests', document: document %>
14 | <%= render Arclight::BookmarkComponent.new document: document, action: bookmark_config %>
15 | <%= toggle_sidebar %>
16 | <%= online_filter %>
17 |
18 |
19 | <%= render Arclight::MetadataSectionComponent.with_collection(component_metadata_partials,
20 | metadata_attr: { layout: Arclight::UpperMetadataLayoutComponent },
21 | presenter: presenter) unless document.collection? %>
22 |
23 | <%= embed %>
24 |
25 | <% if document.collection? %>
26 |
27 |
<%= t 'arclight.views.show.context' %>
28 | <%= render Arclight::MetadataSectionComponent.with_collection(metadata_partials,
29 | metadata_attr: { layout: Arclight::UpperMetadataLayoutComponent },
30 | presenter: presenter, heading: true) %>
31 |
32 | <% elsif document.children? %>
33 |
34 |
<%= t 'arclight.views.show.contents' %>
35 | <%= helpers.turbo_frame_tag "al-hierarchy-#{document.id}-document", loading: 'lazy', src: helpers.hierarchy_solr_document_path(id: document.id, paginate: true, key: '-document', per_page: 50) %>
36 |
37 | <% end %>
38 |
39 | <%= access %>
40 |
--------------------------------------------------------------------------------
/app/components/arclight/document_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render a single document
5 | class DocumentComponent < Blacklight::DocumentComponent
6 | attr_reader :document
7 |
8 | def online_content?
9 | document.online_content? && (document.collection? || document.children?)
10 | end
11 |
12 | def blacklight_config
13 | presenter.configuration
14 | end
15 |
16 | # @return [Blacklight::Configuration::ToolConfig] the configuration for the bookmark
17 | def bookmark_config
18 | blacklight_config.index.document_actions.arclight_bookmark_control
19 | end
20 |
21 | def breadcrumb_component
22 | blacklight_config.show.breadcrumb_component || Arclight::BreadcrumbsHierarchyComponent
23 | end
24 |
25 | def metadata_partials
26 | blacklight_config.show.metadata_partials || []
27 | end
28 |
29 | def component_metadata_partials
30 | blacklight_config.show.component_metadata_partials || []
31 | end
32 |
33 | def online_filter
34 | render Arclight::OnlineContentFilterComponent.new(document: document)
35 | end
36 |
37 | def access
38 | render (blacklight_config.show.access_component || Arclight::AccessComponent).new(presenter: presenter)
39 | end
40 |
41 | def toggle_sidebar
42 | button_tag(t('arclight.views.show.toggle_sidebar'),
43 | type: :button,
44 | class: 'btn btn-sm btn-secondary d-lg-none sidebar-toggle',
45 | data: { bs_toggle: 'offcanvas', bs_target: '#sidebar' },
46 | aria: { controls: 'sidebar' })
47 | end
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/app/components/arclight/document_components_hierarchy_component.html.erb:
--------------------------------------------------------------------------------
1 | <% if paginate? && @target_index >= 0 && @target_index >= @maximum_left_gap %>
2 | <%# render the hierarchy as: outer (left) window ... window ... because our current document hierarchy is so far down the list it'd be buried %>
3 | <%= helpers.turbo_frame_tag "al-hierarchy-#{@document.id}-left", loading: 'lazy', src: hierarchy_path(limit: @left_outer_window, key: '-left') %>
4 | <%= tag.turbo_frame id: "al-hierarchy-#{@document.id}-gap" do %>
5 | <%= render expand_hierarchy_component.new(path: hierarchy_path(offset: @left_outer_window, limit: @target_index - @left_outer_window - (@window / 2), key: '-gap')) %>
6 | <% end %>
7 | <%= helpers.turbo_frame_tag "al-hierarchy-#{@document.id}-window", src: hierarchy_path(offset: @target_index - (@window / 2), limit: @window, key: '-window') %>
8 | <%= tag.turbo_frame id: "al-hierarchy-#{@document.id}-right" do %>
9 | <%= render expand_hierarchy_component.new(path: hierarchy_path(offset: @target_index + (@window / 2), key: '-right')) %>
10 | <% end %>
11 | <% elsif paginate? %>
12 | <%# render the first N documents, and let the user expand the remaining if desired %>
13 | <%= helpers.turbo_frame_tag "al-hierarchy-#{@document.id}-sidebar", loading: ('lazy' unless @target_index >= 0), src: hierarchy_path(limit: @maximum_left_gap, key: '-sidebar') %>
14 | <%= tag.turbo_frame id: "al-hierarchy-#{@document.id}-right" do %>
15 | <%= render expand_hierarchy_component.new(path: hierarchy_path(offset: @maximum_left_gap, key: '-right')) %>
16 | <% end %>
17 | <% else %>
18 | <%# there aren't enough to bother paginating, so load them all at once %>
19 | <%= helpers.turbo_frame_tag "al-hierarchy-#{@document.id}-sidebar", loading: ('lazy' unless @target_index >= 0), src: hierarchy_path(key: '-sidebar') %>
20 | <% end %>
21 |
--------------------------------------------------------------------------------
/app/components/arclight/document_components_hierarchy_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Display a document's constituent components with appropriate lazy-loading
5 | # to keep the page load time reasonable.
6 | class DocumentComponentsHierarchyComponent < ViewComponent::Base
7 | # rubocop:disable Metrics/ParameterLists
8 | def initialize(document: nil, target_index: -1, minimum_pagination_size: 20, left_outer_window: 3, maximum_left_gap: 10, window: 10)
9 | super
10 |
11 | @document = document
12 | @target_index = target_index&.to_i || -1
13 | @minimum_pagination_size = minimum_pagination_size
14 | @left_outer_window = left_outer_window
15 | @maximum_left_gap = maximum_left_gap
16 | @window = window
17 | end
18 | # rubocop:enable Metrics/ParameterLists
19 |
20 | def paginate?
21 | @document.number_of_children > @minimum_pagination_size
22 | end
23 |
24 | def hierarchy_path(**kwargs)
25 | helpers.hierarchy_solr_document_path(id: @document.id, hierarchy: true, nest_path: params[:nest_path], **kwargs)
26 | end
27 |
28 | def expand_hierarchy_component
29 | blacklight_config.show.expand_hierarchy_component || Arclight::ExpandHierarchyButtonComponent
30 | end
31 |
32 | delegate :blacklight_config, to: :helpers
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/app/components/arclight/document_download_component.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <% case files.size %>
3 | <% when 2 %>
4 |
5 |
6 | <%= t("arclight.views.show.download.default") %>
7 |
8 |
16 |
17 | <% when 1 %>
18 | <% files.each do |file| %>
19 | <%= link_to(file.href, class: "btn btn-secondary btn-sm", **@link_options) do %>
20 | <%= download_icon %> <%= label(file) %>
21 | <% end %>
22 | <% end %>
23 | <% end %>
24 |
25 |
--------------------------------------------------------------------------------
/app/components/arclight/embed_component.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
<%= t('arclight.views.show.embedded_content') %>
3 | <%= render Arclight::OembedViewerComponent.with_collection(embeddable_resources, document: @document) %>
4 |
5 | <% linked_resources.each do |resource| %>
6 |
7 | <%= link_to(resource.label, resource.href) %>
8 |
9 | <% end %>
10 |
11 |
--------------------------------------------------------------------------------
/app/components/arclight/embed_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render digital object links for a document
5 | class EmbedComponent < ViewComponent::Base
6 | def initialize(document:, presenter:, **kwargs)
7 | super
8 |
9 | @document = document
10 | @presenter = presenter
11 | end
12 |
13 | def render?
14 | resources.any?
15 | end
16 |
17 | def embeddable_resources
18 | resources.first(1).select { |object| embeddable?(object) }
19 | end
20 |
21 | def linked_resources
22 | resources - embeddable_resources
23 | end
24 |
25 | def resources
26 | @resources ||= @document.digital_objects || []
27 | end
28 |
29 | def depth
30 | @document.parents.length || 0
31 | end
32 |
33 | def embeddable?(object)
34 | exclude_patterns.none? do |pattern|
35 | object.href =~ pattern
36 | end
37 | end
38 |
39 | def exclude_patterns
40 | Arclight::Engine.config.oembed_resource_exclude_patterns
41 | end
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/app/components/arclight/expand_hierarchy_button_component.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= expand_link %>
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/components/arclight/expand_hierarchy_button_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Component for rendering an expand button inside the hierarchy view
5 | class ExpandHierarchyButtonComponent < Blacklight::Component
6 | def initialize(path:, classes: 'btn btn-secondary btn-sm')
7 | super
8 | @path = path
9 | @classes = classes
10 | end
11 |
12 | def expand_link
13 | link_to t('arclight.views.show.expand'), @path, class: @classes
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/components/arclight/group_component.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <% if document.repository_config.present? %>
5 |
6 | <%= link_to(document.repository_config.name, helpers.arclight_engine.repository_path(document.repository_config.slug)) %>
7 |
8 | <% end %>
9 |
<%= helpers.link_to_document document %>
10 | <% document.extent.each do |extent| %>
11 | <%= tag.span extent, class: 'al-document-extent badge' unless compact? %>
12 | <% end %>
13 |
14 | <%= render Arclight::IndexMetadataFieldComponent.with_collection(presenter.field_presenters.select { |field| !compact? || field.field_config.compact }) %>
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | <% if @group.total > 3 %>
23 | <%= t('arclight.views.index.top_group_results', count: 3) %>
24 | <%= link_to(
25 | t('arclight.views.index.all_group_results', count: @group.total),
26 | search_within_collection_url)
27 | %>
28 | <% else %>
29 | <%= t('arclight.views.index.group_results_count', count: @group.total) %>
30 | <% end %>
31 |
32 |
33 | <%= helpers.render_document_index @group.docs %>
34 |
35 |
--------------------------------------------------------------------------------
/app/components/arclight/group_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render a collection document for a
5 | # grouped search result view
6 | class GroupComponent < Blacklight::Document::GroupComponent
7 | def compact?
8 | helpers.document_index_view_type.to_s == 'compact'
9 | end
10 |
11 | def document
12 | @document ||= @group.docs.first.collection
13 | end
14 |
15 | def presenter
16 | @presenter ||= Arclight::ShowPresenter.new(document, helpers).with_field_group('group_header_field')
17 | end
18 |
19 | def search_within_collection_url
20 | search_catalog_path(helpers.search_without_group.deep_merge(f: { collection: [document.collection_name] }))
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app/components/arclight/header_component.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= top_bar %>
3 | <%= masthead %>
4 | <%= search_bar %>
5 |
6 |
--------------------------------------------------------------------------------
/app/components/arclight/header_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Provides a header with a masthead
5 | class HeaderComponent < Blacklight::HeaderComponent
6 | def masthead
7 | render Arclight::MastheadComponent.new
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/components/arclight/hierarchy_toggle_component.html.erb:
--------------------------------------------------------------------------------
1 | <%= link_to(
2 | "#collapsible-hierarchy-#{document.id}",
3 | class: "al-toggle-view-children#{' collapsed' unless expanded }",
4 | aria: {
5 | label: t('arclight.hierarchy.view_all'),
6 | expanded: expanded
7 | },
8 | data: {
9 | bs_toggle: 'collapse',
10 | toggle: 'collapse'
11 | }
12 | ) do %>
13 |
14 | <%= render Blacklight::Icons::PlusComponent.new(classes: 'icon-plus') %>
15 | <%= render Blacklight::Icons::MinusComponent.new(classes: 'icon-minus') %>
16 |
17 | <% end %>
--------------------------------------------------------------------------------
/app/components/arclight/hierarchy_toggle_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Component for rendering the plus/minus icons at each level of expandable hierarchy components
5 | class HierarchyToggleComponent < ViewComponent::Base
6 | attr_reader :document, :expanded
7 |
8 | def initialize(document:, expanded:)
9 | @document = document
10 | @expanded = expanded
11 | super
12 | end
13 |
14 | delegate :blacklight_icon, to: :helpers
15 |
16 | def render?
17 | @document.children?
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/components/arclight/index_metadata_field_component.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= tag.dt @field.label, class: 'visually-hidden' %>
3 | <% if truncate? %>
4 | <%= tag.dd(class: @classes + ['truncator'], data: { controller: 'arclight-truncate' }) do %>
5 | <%= tag.div @field.render, class: 'content', data: { arclight_truncate_target: 'content' } %>
6 | <%= button_tag(type: :button, class: 'btn btn-sm btn-link px-0',
7 | data: { action: 'click->arclight-truncate#trigger' }) do %>
8 | <%= tag.span t('arclight.truncation.view_more'), class: 'view-more' %>
9 | <%= tag.span t('arclight.truncation.view_less'), class: 'view-less' %>
10 | <%= tag.span(class: 'icon') %>
11 | <% end %>
12 | <% end %>
13 | <% else %>
14 | <%= tag.dd @field.render, class: @classes %>
15 | <% end %>
16 |
17 |
--------------------------------------------------------------------------------
/app/components/arclight/index_metadata_field_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render a simple metadata field (e.g. without labels) in a .row div
5 | class IndexMetadataFieldComponent < Blacklight::MetadataFieldComponent
6 | def initialize(field:, classes: ['col'], **kwargs)
7 | super(field: field, **kwargs)
8 |
9 | @classes = classes + ["al-document-#{@field.key.dasherize}"]
10 | end
11 |
12 | def render?
13 | helpers.document_index_view_type != :compact || @field.field_config.compact
14 | end
15 |
16 | def truncate?
17 | !!@field.field_config.truncate
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/components/arclight/masthead_component.html.erb:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/app/components/arclight/masthead_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render the masthead
5 | class MastheadComponent < Blacklight::Component
6 | def heading
7 | t('arclight.masthead_heading_html')
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/components/arclight/metadata_section_component.html.erb:
--------------------------------------------------------------------------------
1 | <%= tag.div id: t("arclight.views.show.sections.#{@section}").parameterize do %>
2 | <% if @heading %>
3 |
4 | <%= t("arclight.views.show.sections.#{@section}", level: @presenter.document.level) %>
5 |
6 | <% end %>
7 |
8 | <%= tag.dl class: @classes do %>
9 | <% @presenter.field_presenters.each do |field_presenter| %>
10 | <%= render (field_presenter.component || Blacklight::MetadataFieldComponent).new(field: field_presenter, show: true, **@metadata_attr) %>
11 | <% end %>
12 | <% end %>
13 | <% end %>
14 |
--------------------------------------------------------------------------------
/app/components/arclight/metadata_section_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render a simple metadata field (e.g. without labels) in a .row div
5 | class MetadataSectionComponent < ViewComponent::Base
6 | with_collection_parameter :section
7 |
8 | def initialize(section:, presenter:, metadata_attr: {}, classes: %w[row dl-invert], heading: false)
9 | super
10 |
11 | @classes = classes
12 | @section = section
13 | @presenter = presenter.with_field_group(section)
14 | @heading = heading
15 | @metadata_attr = metadata_attr
16 | end
17 |
18 | def render?
19 | @presenter.fields_to_render.any?
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/components/arclight/oembed_viewer_component.html.erb:
--------------------------------------------------------------------------------
1 | <%= tag.div(class: 'al-oembed-viewer', data: { controller: 'arclight-oembed', arclight_oembed_url_value: @resource.href }) do %>
2 |
3 | <%= link_to(@resource.label, @resource.href) %>
4 |
5 | <% end %>
6 |
--------------------------------------------------------------------------------
/app/components/arclight/oembed_viewer_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render an oembed viewer for a document
5 | class OembedViewerComponent < ViewComponent::Base
6 | with_collection_parameter :resource
7 |
8 | def initialize(resource:, document:, depth: 0)
9 | super
10 |
11 | @resource = resource
12 | @document = document
13 | @depth = depth
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/components/arclight/online_content_filter_component.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= helpers.blacklight_icon :online %>
5 |
6 | Filter
7 | <%= t('arclight.views.show.online_content.title') %>
8 |
9 |
10 |
11 |
<%= t('arclight.views.show.online_content.description') %>
12 | <%= link_to t('arclight.views.show.online_content.link_text'), helpers.search_action_path(f: { collection: [collection_name], access: ['online'] }) %>
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/components/arclight/online_content_filter_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render the box that displays a link to filter only for online content
5 | class OnlineContentFilterComponent < Blacklight::Component
6 | def initialize(document:)
7 | @document = document
8 | super
9 | end
10 |
11 | def render?
12 | @document.collection? && @document.online_content?
13 | end
14 |
15 | delegate :collection_name, to: :@document
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/components/arclight/online_status_indicator_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render an online status indicator for a document
5 | class OnlineStatusIndicatorComponent < Blacklight::Component
6 | def initialize(document:, **)
7 | @document = document
8 | super
9 | end
10 |
11 | def render?
12 | @document.online_content?
13 | end
14 |
15 | def call
16 | tag.span helpers.blacklight_icon(:online), class: 'al-online-content-icon'
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/components/arclight/repository_breadcrumb_component.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= blacklight_icon(:repository, classes: 'al-repository-content-icon') %>
4 |
5 | <% if repository_path %>
6 | <%= link_to(@document.repository, helpers.arclight_engine.repository_path(repository_path)) %>
7 | <% else %>
8 | <%= @document.repository %>
9 | <% end %>
10 |
11 |
--------------------------------------------------------------------------------
/app/components/arclight/repository_breadcrumb_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Draws the repository breadcrumb item for a search result
5 | class RepositoryBreadcrumbComponent < ViewComponent::Base
6 | def initialize(document:)
7 | super
8 | @document = document
9 | end
10 |
11 | delegate :blacklight_icon, to: :helpers
12 |
13 | def repository_path
14 | @document.repository_config&.slug
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/components/arclight/repository_location_component.html.erb:
--------------------------------------------------------------------------------
1 | <%= render(@layout.new(field: @field)) do |component| %>
2 | <% component.with_label do %>
3 | <%= label %>
4 | <% end %>
5 | <% component.with_value do %>
6 |
7 | <% if repository.thumbnail_url %>
8 | <%= image_tag repository.thumbnail_url, alt: '', class: 'img-fluid float-left' %>
9 | <% end %>
10 |
11 | <%= link_to(repository.name, helpers.arclight_engine.repository_path(repository.slug)) %>
12 |
13 |
14 |
15 |
16 | <%= repository.location %>
17 |
18 |
19 | <% end %>
20 | <% end %>
21 |
--------------------------------------------------------------------------------
/app/components/arclight/repository_location_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render the repository location card as a metadata field
5 | class RepositoryLocationComponent < Blacklight::MetadataFieldComponent
6 | def repository
7 | @field.values.first
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/components/arclight/search_bar_component.html.erb:
--------------------------------------------------------------------------------
1 | <%= render(Blacklight::SearchBarComponent.new(
2 | **@kwargs,
3 | params: @params.merge(f: (@params[:f] || {}).except(:collection)),
4 | q: @q,
5 | search_field: @search_field)) do |c| %>
6 |
7 | <% c.with_before_input_group do %>
8 |
9 |
10 | <%= t('arclight.within_collection_dropdown.label_html') %>
11 |
12 | <%= select_tag ('f[collection][]' if collection_name.present?), within_collection_options, id: 'within_collection', class: 'form-select search-field rounded-end' %>
13 |
14 | <% end %>
15 |
16 | <% c.with_prepend do %>
17 | <%= prepend %>
18 | <% end %>
19 |
20 | <% c.with_append do %>
21 | <%= append %>
22 | <% end if append? %>
23 |
24 | <% c.with_search_button do %>
25 | <%= search_button %>
26 | <% end if respond_to?(:search_button?) && search_button? # only BL8 has this option %>
27 | <% end %>
28 |
--------------------------------------------------------------------------------
/app/components/arclight/search_bar_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Override Blacklight's SearchBarComponent to add a dropdown for choosing
5 | # the context of the search (within "this collection" or "all collections").
6 | # If a collection has not been chosen, it displays a dropdown with only "all collections"
7 | # as the only selectable option.
8 | class SearchBarComponent < Blacklight::SearchBarComponent
9 | def initialize(**kwargs)
10 | super
11 |
12 | @kwargs = kwargs
13 | end
14 |
15 | def within_collection_options
16 | value = collection_name || 'none-selected'
17 | options_for_select(
18 | [
19 | [t('arclight.within_collection_dropdown.all_collections'), ''],
20 | [t('arclight.within_collection_dropdown.this_collection'), value]
21 | ],
22 | selected: collection_name,
23 | disabled: 'none-selected'
24 | )
25 | end
26 |
27 | def collection_name
28 | @collection_name ||= Array(@params.dig(:f, :collection)).reject(&:empty?).first ||
29 | helpers.current_context_document&.collection_name
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/app/components/arclight/search_result_breadcrumbs_component.html.erb:
--------------------------------------------------------------------------------
1 | Collection Context
2 |
3 |
4 | <%= rendered_breadcrumbs %>
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/components/arclight/search_result_breadcrumbs_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render the breadcrumbs for a search result document
5 | class SearchResultBreadcrumbsComponent < Blacklight::MetadataFieldComponent
6 | delegate :document, to: :@field
7 |
8 | def initialize(field:, **kwargs)
9 | @field = field
10 | super
11 | end
12 |
13 | def breadcrumbs
14 | offset = grouped? ? 2 : 0
15 |
16 | Arclight::BreadcrumbComponent.new(document: document, count: breadcrumb_count, offset: offset)
17 | end
18 |
19 | def rendered_breadcrumbs
20 | @rendered_breadcrumbs ||= capture { render breadcrumbs }
21 | end
22 |
23 | def render?
24 | rendered_breadcrumbs.present?
25 | end
26 |
27 | def breadcrumb_count
28 | @field.field_config.compact&.dig(:count) if compact?
29 | end
30 |
31 | delegate :grouped?, to: :helpers
32 |
33 | def compact?
34 | helpers.document_index_view_type == :compact
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/app/components/arclight/search_result_component.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_tag @component,
2 | id: @id,
3 | data: {
4 | 'document-id': @document.id.to_s.parameterize,
5 | 'document-counter': @counter,
6 | },
7 | itemscope: true,
8 | itemtype: @document.itemtype,
9 | class: classes.flatten.join(' ') do %>
10 |
11 | <%= icon %>
12 |
13 |
14 | <%= render Arclight::SearchResultTitleComponent.new(document: document, compact: compact?) %>
15 |
16 |
17 | <%= metadata %>
18 |
19 |
20 | <% end %>
21 |
--------------------------------------------------------------------------------
/app/components/arclight/search_result_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render a document for a search result; this works with
5 | # both the compact and list views for grouped or ungrouped
6 | # results.
7 | class SearchResultComponent < Blacklight::DocumentComponent
8 | attr_reader :document
9 |
10 | def compact?
11 | presenter.view_config.key.to_s == 'compact'
12 | end
13 |
14 | def icon
15 | helpers.blacklight_icon helpers.document_or_parent_icon(@document)
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/app/components/arclight/search_result_title_component.html.erb:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/app/components/arclight/search_result_title_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Render a document title for a search result
5 | class SearchResultTitleComponent < Blacklight::DocumentTitleComponent
6 | def initialize(compact:, **args)
7 | @compact = compact
8 | super(**args)
9 | end
10 |
11 | def compact?
12 | @compact
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/components/arclight/sidebar_component.html.erb:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/app/components/arclight/sidebar_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # A sidebar with collection context widget and tools
5 | class SidebarComponent < Blacklight::Document::SidebarComponent
6 | delegate :blacklight_config, :document_presenter, :should_render_field?,
7 | :turbo_frame_tag, to: :helpers
8 |
9 | def collection_context
10 | render Arclight::CollectionContextComponent.new(presenter: document_presenter(document), download_component: Arclight::DocumentDownloadComponent)
11 | end
12 |
13 | def collection_sidebar
14 | render Arclight::CollectionSidebarComponent.new(document: document,
15 | collection_presenter: document_presenter(document.collection),
16 | partials: blacklight_config.show.metadata_partials)
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/components/arclight/upper_metadata_layout_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Override upstream to add an offset bootstrap column class
5 | class UpperMetadataLayoutComponent < Blacklight::MetadataFieldLayoutComponent
6 | def initialize(field:, label_class: 'col-md-3 offset-md-1', value_class: 'col-md-8')
7 | super
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/components/blacklight/icons/bookmark_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Blacklight
4 | module Icons
5 | # The bookmark icon
6 | class BookmarkComponent < Blacklight::Icons::IconComponent
7 | self.svg = <<~SVG
8 |
9 | SVG
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/components/blacklight/icons/collection_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Blacklight
4 | module Icons
5 | # The collection icon
6 | class CollectionComponent < Blacklight::Icons::IconComponent
7 | # Used unmodified from https://fontawesome.com
8 | # CC BY 4.0 https://creativecommons.org/licenses/by/4.0/
9 | self.svg = <<~SVG
10 |
11 | SVG
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/components/blacklight/icons/compact_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Blacklight
4 | module Icons
5 | # The compact icon
6 | class CompactComponent < Blacklight::Icons::IconComponent
7 | self.svg = <<~SVG
8 |
9 | SVG
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/components/blacklight/icons/container_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Blacklight
4 | module Icons
5 | # The container icon
6 | class ContainerComponent < Blacklight::Icons::IconComponent
7 | # Used unmodified from https://fontawesome.com
8 | # CC BY 4.0 https://creativecommons.org/licenses/by/4.0/
9 | self.svg = <<~SVG
10 |
11 | SVG
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/components/blacklight/icons/ead_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Blacklight
4 | module Icons
5 | # The ead icon
6 | class EadComponent < Blacklight::Icons::IconComponent
7 | self.svg = <<~SVG
8 |
9 | SVG
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/components/blacklight/icons/file_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Blacklight
4 | module Icons
5 | # The file icon
6 | class FileComponent < Blacklight::Icons::IconComponent
7 | # Used unmodified from https://fontawesome.com
8 | # CC BY 4.0 https://creativecommons.org/licenses/by/4.0/
9 | self.svg = <<~SVG
10 |
11 | SVG
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/components/blacklight/icons/folder_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Blacklight
4 | module Icons
5 | # The folder icon
6 | class FolderComponent < Blacklight::Icons::IconComponent
7 | self.svg = <<~SVG
8 |
9 | SVG
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/components/blacklight/icons/minus_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Blacklight
4 | module Icons
5 | # The minus icon
6 | class MinusComponent < Blacklight::Icons::IconComponent
7 | self.svg = <<~SVG
8 |
9 | SVG
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/components/blacklight/icons/online_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Blacklight
4 | module Icons
5 | # The online icon
6 | class OnlineComponent < Blacklight::Icons::IconComponent
7 | # Used unmodified from https://fontawesome.com
8 | # CC BY 4.0 https://creativecommons.org/licenses/by/4.0/
9 | self.svg = <<~SVG
10 |
11 |
12 | SVG
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/components/blacklight/icons/pdf_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Blacklight
4 | module Icons
5 | # The pdf icon
6 | class PdfComponent < Blacklight::Icons::IconComponent
7 | self.svg = <<~SVG
8 |
9 | SVG
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/components/blacklight/icons/plus_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Blacklight
4 | module Icons
5 | # The plus icon
6 | class PlusComponent < Blacklight::Icons::IconComponent
7 | self.svg = <<~SVG
8 |
9 | SVG
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/components/blacklight/icons/repository_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Blacklight
4 | module Icons
5 | # The repository icon
6 | class RepositoryComponent < Blacklight::Icons::IconComponent
7 | self.svg = <<~SVG
8 |
9 | SVG
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/controllers/arclight/repositories_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Controller for our /repositories index page
5 | class RepositoriesController < ApplicationController
6 | def index
7 | @repositories = Arclight::Repository.all
8 | load_collection_counts
9 | end
10 |
11 | def show
12 | @repository = Arclight::Repository.find_by!(slug: params[:id])
13 | search_service = Blacklight.repository_class.new(blacklight_config)
14 | @response = search_service.search(
15 | q: "level_ssim:Collection repository_ssim:\"#{@repository.name}\"",
16 | rows: 100
17 | )
18 | @collections = @response.documents
19 | end
20 |
21 | private
22 |
23 | def load_collection_counts
24 | counts = fetch_collection_counts
25 | @repositories.each do |repository|
26 | repository.collection_count = counts[repository.name] || 0
27 | end
28 | end
29 |
30 | def fetch_collection_counts
31 | search_service = Blacklight.repository_class.new(blacklight_config)
32 | results = search_service.search(
33 | q: 'level_ssim:Collection',
34 | 'facet.field': 'repository_ssim',
35 | rows: 0
36 | )
37 | Hash[*results.facet_fields['repository_ssim']]
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/app/helpers/arclight/field_config_helpers.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | ##
5 | # A module to add configuration helpers for certain fields used by Arclight
6 | module FieldConfigHelpers
7 | include Arclight::EadFormatHelpers
8 |
9 | def link_to_name_facet(args)
10 | options = args[:config]&.separator_options || {}
11 | values = args[:value] || []
12 |
13 | values.map do |value|
14 | link_to(
15 | value,
16 | search_action_path(f: { names: [value] })
17 | )
18 | end.to_sentence(options).html_safe
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/models/arclight/parent.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | ##
5 | # Logic containing information about Solr_Ead "Parent"
6 | # https://github.com/awead/solr_ead/blob/8cf7ffaa66e0e4c9c0b12f5646d6c2e20984cd99/lib/solr_ead/behaviors.rb#L54-L57
7 | class Parent
8 | attr_reader :id, :label, :eadid, :level
9 |
10 | def initialize(id:, label:, eadid:, level:)
11 | @id = id
12 | @label = label
13 | @eadid = eadid
14 | @level = level
15 | end
16 |
17 | def collection?
18 | level == 'collection'
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/models/arclight/parents.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | ##
5 | # Object for parsing and formalizing Solr_Ead "Parents"
6 | # https://github.com/awead/solr_ead/blob/8cf7ffaa66e0e4c9c0b12f5646d6c2e20984cd99/lib/solr_ead/behaviors.rb#L54-L57
7 | class Parents
8 | attr_reader :ids, :labels, :levels
9 |
10 | def initialize(ids:, labels:, eadid:, levels:)
11 | @ids = ids
12 | @labels = labels
13 | @eadid = eadid
14 | @levels = levels
15 | end
16 |
17 | def eadid
18 | Arclight::NormalizedId.new(@eadid).to_s
19 | end
20 |
21 | ##
22 | # @return [Array[Arclight::Parent]]
23 | def as_parents
24 | ids.map.with_index { |id, idx| Arclight::Parent.new(id: id, label: labels[idx], eadid: eadid, level: levels[idx]) }
25 | end
26 |
27 | ##
28 | # @param [SolrDocument] document
29 | def self.from_solr_document(document)
30 | ids = document.parent_ids
31 | labels = document.parent_labels
32 | eadid = document.eadid
33 | levels = document.parent_levels
34 | new(ids: ids, labels: labels, eadid: eadid, levels: levels)
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/app/models/arclight/requests/aeon_external_request.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | module Requests
5 | ##
6 | # This class should be used to turn configuration into a URL and
7 | # POST form specifically aimed at Aeon's external request
8 | # endpoint (https://support.atlas-sys.com/hc/en-us/articles/360011820054-External-Request-Endpoint)
9 | class AeonExternalRequest
10 | def initialize(document, presenter)
11 | @document = document
12 | @presenter = presenter
13 | end
14 |
15 | def config
16 | @config ||= @document.repository_config.request_config_for_type('aeon_external_request_endpoint')
17 | end
18 |
19 | def url
20 | "#{config['request_url']}?#{url_params}"
21 | end
22 |
23 | def form_mapping
24 | static_mappings.merge(dynamic_mappings)
25 | end
26 |
27 | def static_mappings
28 | config['request_mappings']['static']
29 | end
30 |
31 | def dynamic_mappings
32 | config['request_mappings']['accessor'].transform_values do |v|
33 | @document.public_send(v.to_sym)
34 | end
35 | end
36 |
37 | def url_params
38 | config['request_mappings']['url_params'].to_query
39 | end
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/app/models/arclight/requests/aeon_web_ead.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | module Requests
5 | ##
6 | # This object relies on the ability to respond to attributes passed in as
7 | # query parameters from the form mapping configuration
8 | class AeonWebEad
9 | attr_reader :document, :ead_url
10 |
11 | ##
12 | # @param [SolrDocument] document
13 | # @param [String] ead_url
14 | def initialize(document, ead_url)
15 | @document = document
16 | @ead_url = ead_url
17 | end
18 |
19 | ##
20 | # Url target for Aeon request params
21 | def request_url
22 | request_config['request_url']
23 | end
24 |
25 | ##
26 | # Constructed request URL
27 | def url
28 | "#{request_url}?#{form_mapping.to_query}"
29 | end
30 |
31 | ##
32 | # Converts mappings as a query url param into a Hash used for sending
33 | # messages
34 | # If a defined method is provided as a value, that method will be invoked
35 | # "collection_name=entry.123" => { "collection_name" => "entry.123" }
36 | # @return [Hash]
37 | def form_mapping
38 | form_hash = Rack::Utils.parse_nested_query(
39 | request_config['request_mappings']
40 | )
41 | form_hash.each do |key, value|
42 | respond_to?(value) && form_hash[key] = send(value)
43 | end
44 | form_hash
45 | end
46 |
47 | def request_config
48 | document.repository_config.request_config_for_type('aeon_web_ead')
49 | end
50 | end
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/app/models/arclight/requests/google_form.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | module Requests
5 | ##
6 | # This object relies on the ability to respond to attributes passed in as
7 | # query parameters from the form mapping configuratino
8 | class GoogleForm
9 | attr_reader :document, :presenter, :document_url
10 |
11 | delegate :collection_name, :collection_creator, :eadid, :containers, to: :document
12 |
13 | ##
14 | # @param [SolrDocument] document
15 | # @param [Arclight::ShowPresenter] presenter
16 | # @param [String] document_url
17 | def initialize(document, presenter, document_url)
18 | @document = document
19 | @presenter = presenter
20 | @document_url = document_url
21 | end
22 |
23 | ##
24 | # Url of form to fill
25 | def url
26 | request_config['request_url']
27 | end
28 |
29 | ##
30 | # Converts mappings as a query url param into a Hash used for sending
31 | # messages and providing pre-filled form fields
32 | # "collection_name=entry.123" => { "collection_name" => "entry.123" }
33 | # @return [Hash]
34 | def form_mapping
35 | Rack::Utils.parse_nested_query(
36 | request_config['request_mappings']
37 | )
38 | end
39 |
40 | def title
41 | presenter.heading
42 | end
43 |
44 | def request_config
45 | document.repository_config&.request_config_for_type('google_form')
46 | end
47 | end
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/app/models/concerns/arclight/catalog.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | ##
5 | # Arclight specific methods for the Catalog
6 | module Catalog
7 | extend ActiveSupport::Concern
8 |
9 | included do
10 | before_action only: :index do
11 | if (params.dig(:f, :collection) || []).any?(&:blank?)
12 | params[:f][:collection].compact_blank!
13 | params[:f].delete(:collection) if params[:f][:collection].blank?
14 | end
15 | end
16 |
17 | before_action only: :hierarchy do
18 | blacklight_config.search_state_fields += %i[id limit offset]
19 | end
20 |
21 | Blacklight::Configuration.define_field_access :summary_field, Blacklight::Configuration::ShowField
22 | Blacklight::Configuration.define_field_access :background_field, Blacklight::Configuration::ShowField
23 | Blacklight::Configuration.define_field_access :related_field, Blacklight::Configuration::ShowField
24 | Blacklight::Configuration.define_field_access :indexed_terms_field, Blacklight::Configuration::ShowField
25 | Blacklight::Configuration.define_field_access :in_person_field, Blacklight::Configuration::ShowField
26 | Blacklight::Configuration.define_field_access :cite_field, Blacklight::Configuration::ShowField
27 | Blacklight::Configuration.define_field_access :contact_field, Blacklight::Configuration::ShowField
28 | Blacklight::Configuration.define_field_access :component_field, Blacklight::Configuration::ShowField
29 | Blacklight::Configuration.define_field_access :component_indexed_terms_field, Blacklight::Configuration::ShowField
30 | Blacklight::Configuration.define_field_access :terms_field, Blacklight::Configuration::ShowField
31 | Blacklight::Configuration.define_field_access :component_terms_field, Blacklight::Configuration::ShowField
32 | Blacklight::Configuration.define_field_access :group_header_field, Blacklight::Configuration::IndexField
33 | end
34 |
35 | def hierarchy
36 | @response = search_service.search_results
37 | end
38 |
39 | # Overrides blacklight search state so we can exclude some parameters from being passed into the SearchState
40 | def search_state
41 | @search_state ||= search_state_class.new(params.except('hierarchy', 'nest_path'), blacklight_config, self)
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/app/models/concerns/arclight/search_behavior.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | ##
5 | # Customized Search Behavior for Arclight
6 | module SearchBehavior
7 | extend ActiveSupport::Concern
8 |
9 | included do
10 | self.default_processor_chain += %i[
11 | add_highlighting
12 | add_grouping
13 | add_hierarchy_behavior
14 | ]
15 | end
16 |
17 | # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
18 | def add_hierarchy_behavior(solr_parameters)
19 | return unless search_state.controller&.action_name == 'hierarchy'
20 |
21 | solr_parameters[:fq] ||= []
22 | solr_parameters[:fq] << "_nest_parent_:#{blacklight_params[:id]}"
23 | solr_parameters[:rows] = blacklight_params[:per_page]&.to_i || blacklight_params[:limit]&.to_i || 999_999_999
24 | solr_parameters[:start] = blacklight_params[:offset] if blacklight_params[:offset]
25 | solr_parameters[:sort] = 'sort_isi asc'
26 | solr_parameters[:facet] = false
27 | end
28 | # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
29 |
30 | ##
31 | # Add highlighting
32 | def add_highlighting(solr_params)
33 | solr_params['hl'] = true
34 | solr_params['hl.fl'] = CatalogController.blacklight_config.highlight_field
35 | solr_params['hl.snippets'] = 3
36 | solr_params
37 | end
38 |
39 | ##
40 | # Adds grouping parameters for Solr if enabled
41 | def add_grouping(solr_params)
42 | solr_params.merge!(Arclight::Engine.config.catalog_controller_group_query_params) if blacklight_params[:group] == 'true'
43 |
44 | solr_params
45 | end
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/app/presenters/arclight/index_presenter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Custom presentation methods for index partials
5 | class IndexPresenter < Blacklight::IndexPresenter
6 | def label(*)
7 | document.normalized_title
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/presenters/arclight/show_presenter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Custom presentation methods for show partial
5 | class ShowPresenter < Blacklight::ShowPresenter
6 | attr_accessor :field_group
7 |
8 | delegate :collection?, to: :document
9 |
10 | def heading
11 | document.normalized_title
12 | end
13 |
14 | def with_field_group(group)
15 | if block_given?
16 | old_group = field_group
17 |
18 | begin
19 | self.field_group = group
20 | yield(self)
21 | ensure
22 | self.field_group = old_group if block_given?
23 | end
24 | else
25 | dup.tap { |x| x.field_group = group }
26 | end
27 | end
28 |
29 | private
30 |
31 | # @return [Hash] all the fields for this index view
32 | def fields
33 | if field_group
34 | configuration["#{field_group}s"] || []
35 | else
36 | super
37 | end
38 | end
39 |
40 | def field_config(field)
41 | return super unless field_group
42 |
43 | fields.fetch(field) do
44 | if defined?(Blacklight::Configuration::NullDisplayField)
45 | # For Blacklight 8:
46 | Blacklight::Configuration::NullDisplayField.new(field)
47 | else
48 | # Blacklight 7
49 | Blacklight::Configuration::NullField.new(field)
50 | end
51 | end
52 | end
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/app/views/arclight/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/projectblacklight/arclight/2fcfe481afcec80a5fe78b6ce45a415c70b123e9/app/views/arclight/.keep
--------------------------------------------------------------------------------
/app/views/arclight/_requests.html.erb:
--------------------------------------------------------------------------------
1 | <% if document.requestable? %>
2 |
3 | <% document.repository_config.available_request_types.each do |request_type| %>
4 | <%= render partial: "arclight/requests/#{request_type}", locals: { document: document } %>
5 | <% end %>
6 |
7 | <% end %>
8 |
--------------------------------------------------------------------------------
/app/views/arclight/repositories/_repository.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= image_tag repository.thumbnail_url, alt: repository.name, class: 'img-fluid d-block mx-auto' %>
6 |
7 |
8 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/views/arclight/repositories/index.html.erb:
--------------------------------------------------------------------------------
1 | <% breadcrumb :repositories %>
2 |
3 | <%= t('arclight.repositories') %>
4 |
5 |
6 | <% @page_title = t('arclight.views.repositories.title') %>
7 | <%= render 'shared/breadcrumbs' %>
8 | <%= render @repositories %>
9 |
10 |
--------------------------------------------------------------------------------
/app/views/arclight/repositories/show.html.erb:
--------------------------------------------------------------------------------
1 | <% @page_title = t(:'arclight.views.repositories.show', name: @repository.name) %>
2 | <% breadcrumb :repository, @repository %>
3 | <%= render 'shared/breadcrumbs' %>
4 | <%= render @repository %>
5 |
17 |
18 |
19 | <% @collections.each do |document| %>
20 | <% doc_presenter = Arclight::IndexPresenter.new(document, self, CatalogController.blacklight_config) %>
21 |
22 |
23 |
24 |
25 |
<%= link_to doc_presenter.label, solr_document_path(document.id) %>
26 | <%= render Arclight::IndexMetadataFieldComponent.with_collection(doc_presenter.field_presenters.select { |p| p.field_config.repository_context }) %>
27 |
28 | <% if document.unitid %>
29 |
30 | <%= t(:'arclight.views.show.collection_id', id: document.unitid) %>
31 |
32 | <% end %>
33 |
34 |
35 |
36 | <% end %>
37 |
38 |
--------------------------------------------------------------------------------
/app/views/arclight/requests/_aeon_external_request_endpoint.html.erb:
--------------------------------------------------------------------------------
1 | <% aeon_external_request ||= Arclight::Requests::AeonExternalRequest.new(document, document_presenter(document)) %>
2 |
3 | <%= form_tag aeon_external_request.url, method: :post, class: 'd-inline-block al-request-form' do |f| %>
4 | <%= render(Blacklight::HiddenSearchStateComponent.new(params: aeon_external_request.form_mapping)) %>
5 |
6 | <%= t('arclight.request.container') %>
7 |
8 | <% end %>
9 |
--------------------------------------------------------------------------------
/app/views/arclight/requests/_aeon_web_ead.html.erb:
--------------------------------------------------------------------------------
1 | <% if parsed_files = document.ead_file %>
2 | <% aeon_web_ead ||= Arclight::Requests::AeonWebEad.new(document, parsed_files.href) %>
3 | <% aeon_web_ead_url = aeon_web_ead&.url %>
4 |
5 | <%= link_to 'Request', aeon_web_ead_url, class: 'al-request-button btn btn-primary btn-sm me-1' %>
6 | <% end %>
7 |
--------------------------------------------------------------------------------
/app/views/arclight/requests/_google_form.html.erb:
--------------------------------------------------------------------------------
1 | <% google_form ||= Arclight::Requests::GoogleForm.new(document, document_presenter(document), solr_document_path(document)) %>
2 |
3 | <%= form_tag google_form.url, method: :get, class: 'd-inline-block al-request-form' do |f| %>
4 | <% google_form.form_mapping.each do |key, value| %>
5 | <%= hidden_field_tag value, google_form.public_send(key) %>
6 | <% end %>
7 |
8 | <%= t('arclight.request.container') %>
9 |
10 | <% end %>
11 |
--------------------------------------------------------------------------------
/app/views/catalog/_document_list.html.erb:
--------------------------------------------------------------------------------
1 | <% # Override to conditionally add id="documents" on div. When documents are grouped it causes duplicate id issues %>
2 | <% # https://github.com/projectblacklight/blacklight/blob/v8.1.0/app/views/catalog/_document_list.html.erb %>
3 | <% # container for all documents in index list view -%>
4 | <% view_config = local_assigns[:view_config] || blacklight_config&.view_config(document_index_view_type) %>
5 | class="al-document-listings documents-<%= view_config&.key || document_index_view_type %>">
6 | <% document_presenters = documents.map { |doc| document_presenter(doc) } -%>
7 | <%= render view_config.document_component.with_collection(document_presenters, partials: view_config.partials, counter_offset: @response&.start || 0) %>
8 |
--------------------------------------------------------------------------------
/app/views/catalog/_group.html.erb:
--------------------------------------------------------------------------------
1 | <% # container for all groups in index view -%>
2 |
3 | <%= render (blacklight_config.view_config(document_index_view_type).group_component || Arclight::GroupComponent).with_collection(@response.groups) %>
4 |
5 |
--------------------------------------------------------------------------------
/app/views/catalog/_group_toggle.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to t('arclight.views.index.all_results'),
3 | search_catalog_path(search_without_group),
4 | class: "btn btn-outline-secondary #{'active' unless grouped?}"
5 | %>
6 | <%= link_to t('arclight.views.index.group_by_collection'),
7 | search_catalog_path(search_with_group),
8 | class: "btn btn-outline-secondary #{'active' if grouped?}"
9 | %>
10 |
11 |
--------------------------------------------------------------------------------
/app/views/catalog/_search_results_header.html.erb:
--------------------------------------------------------------------------------
1 | <%= render 'shared/breadcrumbs' %>
2 |
3 | <%= search_results_header_text %>
4 |
--------------------------------------------------------------------------------
/app/views/catalog/hierarchy.html.erb:
--------------------------------------------------------------------------------
1 | <%= params[:id] %>
2 | <%= turbo_frame_tag "al-hierarchy-#{params[:id]}#{params[:key]}" do %>
3 | <%= render partial: "paginate_compact", object: @response if show_pagination? && params[:paginate] %>
4 | <% presenters = @response.documents.map{ | document | document_presenter(document) } %>
5 | <% if params[:hierarchy].present? %>
6 |
7 | <%= render Arclight::DocumentCollectionHierarchyComponent.with_collection(presenters,
8 | blacklight_config: blacklight_config,
9 | nest_path: params[:nest_path]) %>
10 |
11 | <% else %>
12 |
13 | <%= render Arclight::DocumentCollectionContextComponent.with_collection(presenters,
14 | blacklight_config: blacklight_config) %>
15 |
16 | <% end %>
17 |
18 | <%= render 'results_pagination' if params[:paginate] %>
19 | <% end %>
20 |
--------------------------------------------------------------------------------
/app/views/catalog/index.html.erb:
--------------------------------------------------------------------------------
1 | <% content_for(:sidebar) do %>
2 | <% conf = blacklight_config.view_config(document_index_view_type) %>
3 | <%= render conf.sidebar_component.new(blacklight_config: blacklight_config,
4 | response: @response,
5 | view_config: conf) %>
6 | <% end %>
7 |
8 | <% breadcrumb :search_results, search_state %>
9 |
10 | <%= render 'search_results' %>
11 |
--------------------------------------------------------------------------------
/app/views/shared/_breadcrumbs.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= breadcrumbs style: :bootstrap5, aria_current: 'page', autoroot: true %>
3 |
4 |
--------------------------------------------------------------------------------
/app/views/shared/_main_menu_links.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to t('arclight.routes.repositories'), arclight_engine.repositories_path, class: 'nav-link' %>
3 |
4 |
5 | <%= link_to t('arclight.routes.collections'), arclight_engine.collections_path, class: 'nav-link' %>
6 |
7 |
--------------------------------------------------------------------------------
/arclight.gemspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | lib = File.expand_path('lib', __dir__)
4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5 | require 'arclight/version'
6 |
7 | Gem::Specification.new do |spec|
8 | spec.name = 'arclight'
9 | spec.version = Arclight::VERSION
10 | spec.authors = ['Darren Hardy', 'Jessie Keck', 'Gordon Leacock', 'Jack Reed']
11 | spec.email = ['drh@stanford.edu', 'jessie.keck@gmail.com', 'gordonl@umich.edu', 'phillipjreed@gmail.com']
12 |
13 | spec.summary = 'A Blacklight-based environment to support discovery and delivery for archives and special collections'
14 | spec.description = ''
15 | spec.homepage = 'https://library.stanford.edu/projects/arclight'
16 | spec.metadata = { 'source_code_uri' => 'https://github.com/projectblacklight/arclight' }
17 | spec.license = 'Apache-2.0'
18 |
19 | spec.required_ruby_version = '>= 3.0.0'
20 | spec.files = `git ls-files -z`.split("\x0").reject do |f|
21 | f.match(%r{^(test|spec|features)/})
22 | end
23 | spec.bindir = 'exe'
24 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25 | spec.require_paths = ['lib']
26 |
27 | spec.add_dependency 'blacklight', '>= 8.0.0', '< 10'
28 | spec.add_dependency 'gretel'
29 | spec.add_dependency 'rails', '>= 7.1', '< 9'
30 | spec.add_dependency 'traject', '~> 3.0'
31 | spec.add_dependency 'traject_plus', '~> 2.0'
32 |
33 | spec.add_development_dependency 'bundler'
34 | spec.add_development_dependency 'capybara'
35 | spec.add_development_dependency 'engine_cart'
36 | spec.add_development_dependency 'i18n-tasks'
37 | spec.add_development_dependency 'rake', '>= 12.0'
38 | spec.add_development_dependency 'rspec-rails'
39 | spec.add_development_dependency 'rubocop', '~> 1.8'
40 | spec.add_development_dependency 'rubocop-rails', '~> 2.8'
41 | spec.add_development_dependency 'rubocop-rake'
42 | spec.add_development_dependency 'rubocop-rspec', '~> 2.3'
43 | spec.add_development_dependency 'selenium-webdriver'
44 | spec.add_development_dependency 'simplecov'
45 | spec.add_development_dependency 'solr_wrapper'
46 | end
47 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "bundler/setup"
4 | require "arclight"
5 |
6 | # You can add fixtures and/or initialization code here to make experimenting
7 | # with your gem easier. You can also use a different console, if you like.
8 |
9 | # (If you use this, don't forget to add pry to your Gemfile!)
10 | # require "pry"
11 | # Pry.start
12 |
13 | require "irb"
14 | IRB.start(__FILE__)
15 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails gems
3 | # installed from the root of your application.
4 |
5 | ENGINE_ROOT = File.expand_path('../..', __FILE__)
6 | ENGINE_PATH = File.expand_path('../../lib/arclight/engine', __FILE__)
7 |
8 | # Set up gems listed in the Gemfile.
9 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
10 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
11 |
12 | require 'rails/all'
13 | require 'rails/engine/commands'
14 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 | set -vx
5 |
6 | bundle install
7 |
8 | # Do any other automated setup that you need to do here
9 |
--------------------------------------------------------------------------------
/config/breadcrumbs.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Root crumb
4 | crumb :root do
5 | link t('arclight.routes.home'), root_path
6 | end
7 |
8 | crumb :repositories do
9 | link t('arclight.routes.repositories'), arclight_engine.repositories_path
10 | end
11 |
12 | crumb :repository do |repository|
13 | link repository.name, arclight_engine.repository_path(repository.slug)
14 |
15 | parent :repositories
16 | end
17 |
18 | crumb :search_results do |search_state|
19 | if search_state.filter('level').values == ['Collection']
20 | link t('arclight.routes.collections')
21 | else
22 | link t('arclight.routes.search_results')
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/config/importmap.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | pin_all_from File.expand_path('../app/assets/javascript', __dir__)
4 |
--------------------------------------------------------------------------------
/config/repositories.yml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/projectblacklight/arclight/2fcfe481afcec80a5fe78b6ce45a415c70b123e9/config/repositories.yml
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Arclight::Engine.routes.draw do
4 | get 'collections' => 'catalog#index', defaults: { f: { level: ['Collection'] } }, as: :collections
5 | resources :repositories, only: %i[index show], controller: 'arclight/repositories'
6 | end
7 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.7"
2 |
3 | services:
4 | solr:
5 | image: solr:9.7.0
6 | volumes:
7 | - $PWD/solr/conf:/opt/solr/conf
8 | ports:
9 | - 8983:8983
10 | entrypoint:
11 | - docker-entrypoint.sh
12 | - solr-precreate
13 | - blacklight-core
14 | - /opt/solr/conf
15 | - "-Xms256m"
16 | - "-Xmx512m"
17 |
--------------------------------------------------------------------------------
/lib/arclight.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'arclight/version'
4 | require 'arclight/engine'
5 | require 'arclight/repository'
6 | require 'arclight/year_range'
7 |
8 | # :nodoc:
9 | module Arclight
10 | autoload :Routes, 'arclight/routes'
11 |
12 | def self.deprecation
13 | @deprecation ||= ActiveSupport::Deprecation.new('3.0', 'Arclight')
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/arclight/digital_object.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | ##
5 | # Plain ruby class to model serializing/deserializing digital object data
6 | class DigitalObject
7 | attr_reader :label, :href
8 |
9 | def initialize(label:, href:)
10 | @label = label.presence || href
11 | @href = href
12 | end
13 |
14 | def to_json(*)
15 | { label: label, href: href }.to_json
16 | end
17 |
18 | def self.from_json(json)
19 | object_data = JSON.parse(json)
20 | new(label: object_data['label'], href: object_data['href'])
21 | end
22 |
23 | def ==(other)
24 | href == other.href && label == other.label
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/arclight/engine.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'blacklight'
4 | require 'traject'
5 | require 'active_model'
6 | require 'arclight/exceptions'
7 | require 'arclight/normalized_date'
8 | require 'arclight/normalized_id'
9 | require 'arclight/normalized_title'
10 | require 'arclight/digital_object'
11 | require 'gretel'
12 |
13 | module Arclight
14 | ##
15 | # This is the defining class for the Arclight Rails Engine
16 | class Engine < ::Rails::Engine
17 | config.oembed_resource_exclude_patterns = [/\.pdf$/, /\.ppt$/]
18 |
19 | Arclight::Engine.config.catalog_controller_group_query_params = {
20 | group: true,
21 | 'group.field': '_root_',
22 | 'group.ngroups': true,
23 | 'group.limit': 3,
24 | fl: '*,collection:[subquery]',
25 | 'collection.q': '{!terms f=id v=$row._root_}',
26 | 'collection.defType': 'lucene',
27 | 'collection.fl': '*',
28 | 'collection.rows': 1
29 | }
30 |
31 | initializer 'arclight.helpers' do
32 | config.after_initialize do
33 | ActiveSupport.on_load(:action_view) { include ArclightHelper }
34 | end
35 | end
36 |
37 | initializer 'arclight.assets', before: 'assets' do |app|
38 | app.config.assets.precompile << 'arclight/arclight.js'
39 | app.config.assets.precompile << 'arclight/oembed_controller.js'
40 | app.config.assets.precompile << 'arclight/truncate_controller.js'
41 | end
42 |
43 | initializer 'arclight.importmap', before: 'importmap' do |app|
44 | app.config.importmap.paths << Engine.root.join('config/importmap.rb') if app.config.respond_to?(:importmap)
45 | end
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/lib/arclight/exceptions.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | module Exceptions
5 | # Id's must be present on all documents and components
6 | class IDNotFound < StandardError
7 | def message
8 | 'id must be present for all documents and components'
9 | end
10 | end
11 |
12 | # Unittitle or unitdate must be present on all documents and components
13 | class TitleNotFound < StandardError
14 | def message
15 | ' or must be present for all documents and components'
16 | end
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/arclight/hash_absolute_xpath.rb:
--------------------------------------------------------------------------------
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}$/
21 | attr_reader :node
22 |
23 | def initialize(node)
24 | @node = node
25 | end
26 |
27 | def to_hexdigest
28 | self.class.hash_algorithm.hexdigest(absolute_xpath).prepend('al_')
29 | end
30 |
31 | def absolute_xpath
32 | ancestor_tree = node.ancestors.map do |ancestor|
33 | ancestor_name_and_index(ancestor)
34 | end
35 |
36 | "#{[ancestor_tree.reverse, node.name].flatten.join('/')}#{current_index}"
37 | end
38 |
39 | private
40 |
41 | def current_index
42 | siblings.index(node)
43 | end
44 |
45 | def component_siblings_for_node(xml_node)
46 | xml_node.parent.children.select { |n| n.name =~ COMPONENT_NODE_NAME_REGEX }
47 | end
48 |
49 | def siblings
50 | @siblings ||= component_siblings_for_node(node)
51 | end
52 |
53 | def ancestor_name_and_index(ancestor)
54 | if ancestor.name =~ COMPONENT_NODE_NAME_REGEX
55 | index = component_siblings_for_node(ancestor).index(ancestor)
56 | "#{ancestor.name}#{index}"
57 | else
58 | ancestor.name
59 | end
60 | end
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/lib/arclight/level_label.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | ##
5 | # A utility class to return a human-readable label for an EAD @level code.
6 | # Can use the value from @otherlevel if provided.
7 | # Examples from @level: recordgrp = "Record Group"
8 | # collection = "Collection"
9 | # subseries = "Subseries"
10 | # otherlevel = (text provided in @otherlevel)
11 | class LevelLabel
12 | # @param [String] `level` from the collection or component @level
13 | # @param [String] `other_level` from the collection or component @otherlevel
14 | def initialize(level, other_level = nil)
15 | @level = level
16 | @other_level = other_level if other_level.present?
17 | end
18 |
19 | # @return [String] the human-readable label
20 | def to_s
21 | human_readable_level
22 | end
23 |
24 | private
25 |
26 | attr_reader :level, :other_level
27 |
28 | CUSTOM_LEVEL_LABELS = {
29 | recordgrp: 'Record Group',
30 | subgrp: 'Subgroup'
31 | }.freeze
32 |
33 | def human_readable_level
34 | if level == 'otherlevel'
35 | alternative_level
36 | elsif level.present?
37 | CUSTOM_LEVEL_LABELS.fetch(level.to_sym, level.capitalize).to_s
38 | end
39 | end
40 |
41 | def alternative_level
42 | alternative_level = other_level if other_level
43 | alternative_level.present? ? alternative_level.capitalize : 'Other'
44 | end
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/lib/arclight/missing_id_strategy.rb:
--------------------------------------------------------------------------------
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
22 |
--------------------------------------------------------------------------------
/lib/arclight/normalized_date.rb:
--------------------------------------------------------------------------------
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] `inclusive` from the `unitdate`
10 | # @param [Array] `bulk` from the `unitdate`
11 | # @param [Array] `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(', ')
25 | end
26 |
27 | # @return [String] the normalized title/date
28 | def to_s
29 | normalize
30 | end
31 |
32 | private
33 |
34 | attr_reader :inclusive, :bulk, :other
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 |
40 | # @see http://www2.archivists.org/standards/DACS/part_I/chapter_2/4_date for rules
41 | def normalize
42 | result = []
43 | result << inclusive if inclusive.present?
44 | result << other if other.present?
45 | result << "bulk #{bulk}" if bulk.present?
46 | result.compact.map(&:strip).join(', ').presence
47 | end
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/lib/arclight/normalized_id.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'arclight/exceptions'
4 |
5 | module Arclight
6 | ##
7 | # A simple utility class to normalize identifiers
8 | class NormalizedId
9 | # Accepts unused kwargs from the ead2_config.rb id to_field directive
10 | # (:title and :repository) so that applications can provide a custom
11 | # id_normalizer class to traject to form the collection id from these attributes.
12 | def initialize(id, **_kwargs)
13 | @id = id
14 | end
15 |
16 | def to_s
17 | normalize
18 | end
19 |
20 | private
21 |
22 | attr_reader :id
23 |
24 | def normalize
25 | raise Arclight::Exceptions::IDNotFound if id.blank?
26 |
27 | id.strip.tr('.', '-')
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/lib/arclight/normalized_title.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'arclight/exceptions'
4 |
5 | module Arclight
6 | ##
7 | # A utility class to normalize titles, typically by joining
8 | # the title and date, e.g., "My Title, 1990-2000"
9 | class NormalizedTitle
10 | # @param [String] `title` from the `unittitle`
11 | # @param [String] `date` from the `unitdate`
12 | def initialize(title, date = nil)
13 | @title = title.gsub(/\s*,\s*$/, '').strip if title.present?
14 | @date = date.strip if date.present?
15 | end
16 |
17 | # @return [String] the normalized title/date
18 | def to_s
19 | normalize
20 | end
21 |
22 | private
23 |
24 | attr_reader :title, :date, :default
25 |
26 | def normalize
27 | result = [title, date].compact.join(', ')
28 | raise Arclight::Exceptions::TitleNotFound if result.blank?
29 |
30 | result
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/lib/arclight/routes.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | # Custom routes for Arclight
5 | module Routes
6 | require 'arclight/routes/hierarchy'
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/lib/arclight/routes/hierarchy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | module Routes
5 | # Inject a hierarchy route for displaying the
6 | # components in the collection context
7 | class Hierarchy
8 | def initialize(defaults = {})
9 | @defaults = defaults
10 | end
11 |
12 | def call(mapper, _options = {})
13 | mapper.member do
14 | mapper.get 'hierarchy'
15 | end
16 | end
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/arclight/traject/nokogiri_namespaceless_reader.rb:
--------------------------------------------------------------------------------
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
23 |
--------------------------------------------------------------------------------
/lib/arclight/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Arclight
4 | VERSION = '2.0.0.alpha'
5 | end
6 |
--------------------------------------------------------------------------------
/lib/generators/arclight/templates/arclight.scss:
--------------------------------------------------------------------------------
1 | :root {
2 | --bs-breadcrumb-divider: "»";
3 | }
4 |
5 | // Arclight, imported from arclight frontend package
6 | @import 'arclight/app/assets/stylesheets/arclight/application';
7 |
--------------------------------------------------------------------------------
/lib/generators/arclight/templates/config/downloads.yml:
--------------------------------------------------------------------------------
1 | #
2 | # downloads.yml - Use the EAD's as the primary key and
3 | # provide the PDF and/or EAD (.xml) links. The
4 | # size value should be a String (shown as-is) or
5 | # the number of bytes in the download.
6 | # - Pass a template key to use a formatted string
7 | # which interpolates document accessors into the
8 | # url using the %{accessor} syntax.
9 | # - Pass a size_accessor key to pull the size of
10 | # the file from an accessor in the solr document
11 | #
12 | sample_unitid:
13 | pdf:
14 | href: 'http://example.com/sample.pdf'
15 | size: '1.23MB'
16 | ead:
17 | href: 'http://example.com/sample.xml'
18 | size: 123456
19 | # size_accessor: 'level'
20 | default:
21 | disabled: true
22 | pdf:
23 | template: 'http://example.com/%{unitid}.pdf'
24 | ead:
25 | template: 'http://example.com/%{unitid}.xml'
26 |
--------------------------------------------------------------------------------
/lib/generators/arclight/templates/config/locales/arclight.en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | blacklight:
3 | search:
4 | fields:
5 | abstract: Abstract
6 | creators: Creator
7 | extent: Extent
8 | language: Language
9 | prefercite: Preferred citation
10 |
11 | scopecontent: Scope and content
12 | bioghist: Biographical / historical
13 | acqinfo: Acquisition information
14 | appraisal: Appraisal information
15 | custodhist: Custodial history
16 | processinfo: Processing information
17 | arrangement: Arrangement
18 | accruals: Accruals
19 | phystech: Physical / technical requirements
20 | physloc: Physical location
21 | physdesc: Physical description
22 | physfacet: Physical facet
23 | dimensions: Dimensions
24 | indexes: Indexes
25 | descrules: Rules or conventions
26 |
27 | relatedmaterial: Related material
28 | separatedmaterial: Separated material
29 | otherfindaid: Other finding aids
30 | altformavail: Alternative form available
31 | originalsloc: Location of originals
32 | materialspec: Material specific details
33 | fileplan: File plan
34 | odd: Other descriptive data
35 | note: Note
36 |
37 | access_subjects: Subjects
38 | names_coll: Names
39 | places: Places
40 |
41 | containers: Containers
42 | names: Names
43 |
44 | restrictions: Restrictions
45 | terms: Terms of access
46 | parent_restrictions: Parent restrictions
47 | parent_terms: Parent terms of access
48 | repository_location: Location of this collection
49 | before_you_visit: Before you visit
50 | repository_contact: Contact
51 |
52 | facet:
53 | collection: Collection
54 | creator: Creator
55 | date_range: Date range
56 | level: Level
57 | names: Names
58 | repository: Repository
59 | place: Place
60 | subject: Subject
61 | access: Access
62 |
--------------------------------------------------------------------------------
/lib/generators/arclight/update_generator.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'generators/arclight/install_generator'
4 |
5 | module Arclight
6 | ##
7 | # Arclight Update generator. This subclasses the Install generator, so this is
8 | # intended to override behavior in the install generator that can allow the
9 | # downstream application to choose if they want to take our changes or not and
10 | # can choose to see a diff of our changes to help them decide.
11 | class Update < Arclight::Install
12 | source_root File.expand_path('templates', __dir__)
13 |
14 | def create_blacklight_catalog
15 | copy_file 'catalog_controller.rb', 'app/controllers/catalog_controller.rb'
16 | end
17 |
18 | def solr_config
19 | directory '../../../../solr', 'solr'
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "arclight",
3 | "version": "2.0.0-alpha",
4 | "description": "The frontend for arclight",
5 | "main": "app/assets/javascript/arclight/arclight.js",
6 | "files": ["app/assets"],
7 | "devDependencies": {
8 | "eslint": "^8.26.0",
9 | "eslint-config-airbnb-base": "^15.0.0",
10 | "eslint-plugin-import": "^2.2.0",
11 | "sass": "^1.59.3",
12 | "bootstrap": "^5.2.0"
13 | },
14 | "scripts": {
15 | "build": "sass ./app/assets/stylesheets/arclight/build.scss:./app/assets/builds/arclight.css --no-source-map --load-path=node_modules",
16 | "lint": "eslint './app/assets/javascripts/**/*.{js,es6}'",
17 | "lint:fix": "eslint --fix './app/assets/javascripts/**/*.{js,es6}'",
18 | "test": "echo \"Error: no test specified\" && exit 1"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "https://github.com/projectblacklight/arclight.git"
23 | },
24 | "author": "",
25 | "license": "Apache-2.0",
26 | "bugs": {
27 | "url": "https://github.com/projectblacklight/arclight/issues"
28 | },
29 | "homepage": "https://library.stanford.edu/projects/arclight"
30 | }
31 |
--------------------------------------------------------------------------------
/solr/conf/_rest_managed.json:
--------------------------------------------------------------------------------
1 | {
2 | "initArgs":{},
3 | "managedList":[]}
--------------------------------------------------------------------------------
/solr/conf/admin-extra.html:
--------------------------------------------------------------------------------
1 |
17 |
18 |
32 |
--------------------------------------------------------------------------------
/solr/conf/elevate.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/solr/conf/protwords.txt:
--------------------------------------------------------------------------------
1 | # The ASF licenses this file to You under the Apache License, Version 2.0
2 | # (the "License"); you may not use this file except in compliance with
3 | # the License. You may obtain a copy of the License at
4 | #
5 | # http://www.apache.org/licenses/LICENSE-2.0
6 | #
7 | # Unless required by applicable law or agreed to in writing, software
8 | # distributed under the License is distributed on an "AS IS" BASIS,
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | # See the License for the specific language governing permissions and
11 | # limitations under the License.
12 |
13 | #-----------------------------------------------------------------------
14 | # Use a protected word file to protect against the stemmer reducing two
15 | # unrelated words to the same base word.
16 |
17 | # Some non-words that normally won't be encountered,
18 | # just to test that they won't be stemmed.
19 | dontstems
20 | zwhacky
21 |
22 |
--------------------------------------------------------------------------------
/solr/conf/spellings.txt:
--------------------------------------------------------------------------------
1 | pizza
2 | history
3 |
--------------------------------------------------------------------------------
/solr/conf/stopwords.txt:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | #-----------------------------------------------------------------------
17 | # a couple of test stopwords to test that the words are really being
18 | # configured from this file:
19 | stopworda
20 | stopwordb
21 |
22 | #Standard english stop words taken from Lucene's StopAnalyzer
23 | a
24 | an
25 | and
26 | are
27 | as
28 | at
29 | be
30 | but
31 | by
32 | for
33 | if
34 | in
35 | into
36 | is
37 | it
38 | no
39 | not
40 | of
41 | on
42 | or
43 | s
44 | such
45 | t
46 | that
47 | the
48 | their
49 | then
50 | there
51 | these
52 | they
53 | this
54 | to
55 | was
56 | will
57 | with
58 |
59 |
--------------------------------------------------------------------------------
/solr/conf/stopwords_en.txt:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | #-----------------------------------------------------------------------
17 | # a couple of test stopwords to test that the words are really being
18 | # configured from this file:
19 | stopworda
20 | stopwordb
21 |
22 | #Standard english stop words taken from Lucene's StopAnalyzer
23 | a
24 | an
25 | and
26 | are
27 | as
28 | at
29 | be
30 | but
31 | by
32 | for
33 | if
34 | in
35 | into
36 | is
37 | it
38 | no
39 | not
40 | of
41 | on
42 | or
43 | s
44 | such
45 | t
46 | that
47 | the
48 | their
49 | then
50 | there
51 | these
52 | they
53 | this
54 | to
55 | was
56 | will
57 | with
58 |
59 |
--------------------------------------------------------------------------------
/solr/conf/stopwords_punctuation.txt:
--------------------------------------------------------------------------------
1 | # Punctuation characters we want to ignore as terms (i.e., when surrounded
2 | # by whitespace in a query, like 'fred : the puppy') in queries
3 | # ONLY FOR SINGLE TOKEN ANALYZED FIELDS
4 | # see https://issues.apache.org/jira/browse/SOLR-3085
5 | # Note that plusses and double hyphens are not treated as terms
6 | # per debugQuery
7 | :
8 | ;
9 | &
10 | /
11 | =
12 | >
13 | <
14 | ,
15 | .
16 | (
17 | )
18 | …
19 | »
20 | §
21 | •
22 | ·
23 | -
24 |
--------------------------------------------------------------------------------
/solr/conf/synonyms.txt:
--------------------------------------------------------------------------------
1 | # The ASF licenses this file to You under the Apache License, Version 2.0
2 | # (the "License"); you may not use this file except in compliance with
3 | # the License. You may obtain a copy of the License at
4 | #
5 | # http://www.apache.org/licenses/LICENSE-2.0
6 | #
7 | # Unless required by applicable law or agreed to in writing, software
8 | # distributed under the License is distributed on an "AS IS" BASIS,
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | # See the License for the specific language governing permissions and
11 | # limitations under the License.
12 |
13 | #-----------------------------------------------------------------------
14 | #some test synonym mappings unlikely to appear in real input text
15 | aaa => aaaa
16 | bbb => bbbb1 bbbb2
17 | ccc => cccc1,cccc2
18 | a\=>a => b\=>b
19 | a\,a => b\,b
20 | fooaaa,baraaa,bazaaa
21 |
22 | # Some synonym groups specific to this example
23 | GB,gib,gigabyte,gigabytes
24 | MB,mib,megabyte,megabytes
25 | Television, Televisions, TV, TVs
26 | #notice we use "gib" instead of "GiB" so any WordDelimiterFilter coming
27 | #after us won't split it into two words.
28 |
29 | # Synonym mappings can be used for spelling correction too
30 | pixima => pixma
31 |
32 |
--------------------------------------------------------------------------------
/solr/conf/xslt/example_rss.xsl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
20 |
23 |
24 |
26 |
27 |
32 |
33 |
34 |
35 | Example Solr RSS 2.0 Feed
36 | http://localhost:8983/solr
37 |
38 | This has been formatted by the sample "example_rss.xsl" transform -
39 | use your own XSLT to get a nicer RSS feed.
40 |
41 | en-us
42 | http://localhost:8983/solr
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | -
53 |
54 |
55 | http://localhost:8983/solr/select?q=id:
56 |
57 |
58 |
59 |
60 |
61 |
62 | http://localhost:8983/solr/select?q=id:
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/spec/arclight_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight do
6 | it 'has a version number' do
7 | expect(Arclight::VERSION).not_to be_nil
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/components/arclight/breadcrumb_component_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight::BreadcrumbComponent, type: :component do
6 | let(:document) do
7 | SolrDocument.new(
8 | parent_ids_ssim: %w[abc123 abc123_def abc123_ghi],
9 | parent_unittitles_ssm: %w[ABC123 DEF GHI],
10 | ead_ssi: 'abc123',
11 | repository_ssm: 'my repository',
12 | _root_: 'abc123'
13 | )
14 | end
15 |
16 | let(:render) do
17 | component.render_in(vc_test_controller.view_context)
18 | end
19 |
20 | let(:rendered) do
21 | Capybara::Node::Simple.new(render)
22 | end
23 |
24 | let(:attr) { {} }
25 |
26 | let(:component) { described_class.new(document: document, **attr) }
27 |
28 | context 'with a count' do
29 | let(:attr) { { count: 2 } }
30 |
31 | it 'renders only that many breadcrumb links' do
32 | expect(rendered).to have_css 'li', text: 'my repository'
33 | expect(rendered).to have_link 'ABC123', href: '/catalog/abc123'
34 | expect(rendered).to have_no_link 'DEF', href: '/catalog/abc123_def'
35 | expect(rendered).to have_no_link 'GHI', href: '/catalog/abc123_ghi'
36 | end
37 |
38 | it 'renders an ellipsis if there are more links than the count' do
39 | expect(render).to end_with '>…'
40 | end
41 | end
42 |
43 | context 'with an offset' do
44 | let(:attr) { { offset: 2 } }
45 |
46 | it 'skips some breadcrumb links' do
47 | expect(rendered).to have_no_selector 'li', text: 'my repository'
48 | expect(rendered).to have_no_link 'ABC123', href: '/catalog/abc123'
49 | expect(rendered).to have_link 'DEF', href: '/catalog/abc123_def'
50 | expect(rendered).to have_link 'GHI', href: '/catalog/abc123_ghi'
51 | end
52 | end
53 |
54 | it 'renders breadcrumb links' do
55 | expect(rendered).to have_css 'li', text: 'my repository'
56 | expect(rendered).to have_link 'DEF', href: '/catalog/abc123_def'
57 | expect(rendered).to have_link 'GHI', href: '/catalog/abc123_ghi'
58 | end
59 | end
60 |
--------------------------------------------------------------------------------
/spec/components/arclight/collection_sidebar_component_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight::CollectionSidebarComponent, type: :component do
6 | subject(:component) do
7 | described_class.new(document: document,
8 | partials: CatalogController.blacklight_config.show.metadata_partials,
9 | collection_presenter: collection_presenter)
10 | end
11 |
12 | before do
13 | render_inline(component)
14 | end
15 |
16 | let(:document) { instance_double(SolrDocument, root: 'foo') }
17 | let(:collection_presenter) { instance_double(Arclight::ShowPresenter, with_field_group: group_presenter) }
18 | let(:group_presenter) { instance_double(Arclight::ShowPresenter, fields_to_render: [double]) }
19 |
20 | it 'has navigation links' do
21 | expect(page).to have_link 'Summary'
22 | expect(page).to have_link 'Background'
23 | expect(page).to have_link 'Related'
24 | expect(page).to have_link 'Indexed terms'
25 | expect(page).to have_link 'Access and use'
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/spec/components/arclight/document_download_component_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight::DocumentDownloadComponent, type: :component do
6 | subject(:component) { described_class.new(downloads: downloads) }
7 |
8 | before do
9 | render_inline(component)
10 | end
11 |
12 | let(:downloads) { instance_double(Arclight::DocumentDownloads, files: files) }
13 | let(:pdf_data) { { 'href' => 'http://example.com/documents/abc123.pdf' } }
14 | let(:pdf) { Arclight::DocumentDownloads::File.new(type: 'pdf', data: pdf_data, document: document) }
15 | let(:document) { SolrDocument.new(id: 'abc123') }
16 |
17 | context 'with no files' do
18 | let(:files) { [] }
19 |
20 | it 'renders nothing' do
21 | expect(page).to have_no_css('*')
22 | end
23 | end
24 |
25 | context 'with one file' do
26 | let(:files) { [pdf] }
27 |
28 | it 'renders a download link' do
29 | expect(page).to have_link 'Download finding aid', href: 'http://example.com/documents/abc123.pdf'
30 | end
31 | end
32 |
33 | context 'with multiple files' do
34 | let(:files) { [pdf, ead] }
35 | let(:ead_data) { { 'href' => 'http://example.com/documents/abc123.xml' } }
36 | let(:ead) { Arclight::DocumentDownloads::File.new(type: 'ead', data: ead_data, document: document) }
37 |
38 | it 'renders download links' do
39 | expect(page).to have_button 'Download'
40 | expect(page).to have_link 'Finding aid', href: 'http://example.com/documents/abc123.pdf'
41 | expect(page).to have_link 'EAD', href: 'http://example.com/documents/abc123.xml'
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/spec/components/arclight/expand_hierarchy_button_component_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight::ExpandHierarchyButtonComponent, type: :component do
6 | subject(:component) { described_class.new(path: '/some/path') }
7 |
8 | before do
9 | render_inline(component)
10 | end
11 |
12 | it 'renders the button' do
13 | expect(page).to have_text('Expand')
14 | expect(page).to have_css('.btn.btn-secondary.btn-sm')
15 | end
16 |
17 | context 'with a custom class' do
18 | subject(:component) { described_class.new(path: '/path/to_file', classes: 'btn btn-primary') }
19 |
20 | it 'renders the button with the custom classes' do
21 | expect(page).to have_text('Expand')
22 | expect(page).to have_css('.btn.btn-primary')
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spec/components/arclight/hierarchy_toggle_component_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight::HierarchyToggleComponent, type: :component do
6 | let(:document) do
7 | instance_double(SolrDocument, id: 'abc123', children?: has_children)
8 | end
9 |
10 | let(:rendered) do
11 | render_inline(described_class.new(document: document, expanded: expanded))
12 | end
13 |
14 | context 'when the document has children' do
15 | let(:has_children) { true }
16 |
17 | context 'when expanded is true' do
18 | let(:expanded) { true }
19 |
20 | it 'does not include the .collapsed class' do
21 | expect(rendered.to_html).to include('class="al-toggle-view-children"')
22 | expect(rendered.to_html).not_to include('collapsed')
23 | end
24 |
25 | it 'renders the link with aria-expanded=true' do
26 | expect(rendered.to_html).to include('aria-expanded="true"')
27 | end
28 | end
29 |
30 | context 'when expanded is false' do
31 | let(:expanded) { false }
32 |
33 | it 'includes the .collapsed class' do
34 | expect(rendered.to_html).to include('class="al-toggle-view-children collapsed"')
35 | end
36 |
37 | it 'renders the link with aria-expanded=false' do
38 | expect(rendered.to_html).to include('aria-expanded="false"')
39 | end
40 | end
41 | end
42 |
43 | context 'when the document has no children' do
44 | let(:has_children) { false }
45 | let(:expanded) { false }
46 |
47 | it 'does not render the component' do
48 | expect(rendered.to_html).to be_empty
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/spec/components/arclight/repository_location_component_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight::RepositoryLocationComponent, type: :component do
6 | let(:field) do
7 | # rubocop:disable Rails/RedundantActiveRecordAllMethod
8 | instance_double(Blacklight::FieldPresenter, key: 'blah', document: nil, label: 'blah', values: [Arclight::Repository.all.first], render_field?: true)
9 | # rubocop:enable Rails/RedundantActiveRecordAllMethod
10 | end
11 | let(:render) do
12 | component.render_in(vc_test_controller.view_context)
13 | end
14 |
15 | let(:rendered) do
16 | Capybara::Node::Simple.new(render)
17 | end
18 |
19 | let(:component) { described_class.new(field: field) }
20 |
21 | it 'renders the repository location information' do
22 | expect(rendered).to have_css('.al-in-person-repository-name', text: 'My Repository')
23 | expect(rendered).to have_css('address .al-repository-street-address-building', text: 'My Building')
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spec/components/arclight/search_bar_component_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight::SearchBarComponent, type: :component do
6 | let(:render) do
7 | component.render_in(view_context)
8 | end
9 | let(:view_context) { vc_test_controller.view_context }
10 |
11 | let(:rendered) do
12 | Capybara::Node::Simple.new(render)
13 | end
14 |
15 | let(:params) { {} }
16 | let(:component) { described_class.new(url: '/', params: params) }
17 |
18 | describe 'within collection dropdown' do
19 | context 'when in a collection context on the search results page' do
20 | let(:params) { { f: { collection: ['some collection'] } } }
21 |
22 | it 'renders a name attribute on the select (so it will be sent through the form)' do
23 | expect(rendered).to have_select('f[collection][]')
24 | end
25 |
26 | it 'has the "this collection" option selected' do
27 | expect(rendered).to have_css('select option[selected]', text: 'this collection')
28 | end
29 | end
30 |
31 | context 'when in a collection context, e.g. on show page for a collection' do
32 | let(:document) { SolrDocument.new(id: 'abc123', collection: { docs: [{ normalized_title_ssm: ['some collection'] }] }) }
33 |
34 | before do
35 | allow(view_context).to receive(:current_context_document).and_return(document)
36 | end
37 |
38 | it 'renders a name attribute on the select (so it will be sent through the form)' do
39 | expect(rendered).to have_select('f[collection][]')
40 | end
41 |
42 | it 'has the "this collection" option selected' do
43 | expect(rendered).to have_css('select option[selected]', text: 'this collection')
44 | end
45 | end
46 |
47 | context 'when not in a collection context' do
48 | it 'does not render a name attribute on the select (because it does not need to be sent through the form)' do
49 | expect(rendered).to have_no_select 'name'
50 | end
51 |
52 | it 'has the "this collection" option disabled' do
53 | expect(rendered).to have_css('select option[disabled]', text: 'this collection')
54 | end
55 | end
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/spec/controllers/arclight/repositories_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight::RepositoriesController do
6 | routes { Arclight::Engine.routes }
7 | before do
8 | ENV['REPOSITORY_FILE'] = 'spec/fixtures/config/repositories.yml'
9 | end
10 |
11 | describe '#index' do
12 | it 'displays the repositories' do
13 | get :index
14 | expect(response).to be_successful
15 | end
16 |
17 | it 'assigns the view variable' do
18 | get :index
19 | repos = controller.instance_variable_get(:@repositories)
20 | expect(repos).to be_an(Array)
21 | expect(repos.first).to be_an(Arclight::Repository)
22 | expect(repos.size).to eq 5
23 | end
24 | end
25 |
26 | describe '#show' do
27 | it 'looks up the repository detail page' do
28 | get :show, params: { id: 'nlm' }
29 | repo = controller.instance_variable_get(:@repository)
30 | expect(repo).to be_an(Arclight::Repository)
31 | expect(repo.slug).to eq 'nlm'
32 | collections = controller.instance_variable_get(:@collections)
33 | expect(collections.first).to be_an(SolrDocument)
34 | expect(collections.find { |c| c.id == 'aoa271' }.unitid).to eq 'MS C 271'
35 | end
36 |
37 | it 'raises RecordNotFound if non-registered slug' do
38 | expect { get :show, params: { id: 'not-registered' } }.to raise_error(
39 | ActiveRecord::RecordNotFound
40 | )
41 | end
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/spec/controllers/catalog_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe CatalogController do
6 | describe 'index action customizations' do
7 | context 'any other view' do
8 | it 'starts a search_session' do
9 | allow(controller).to receive(:search_results)
10 | session[:history] = []
11 | get :index, params: { q: 'foo', view: 'list' }
12 | expect(session[:history]).not_to be_empty
13 | end
14 |
15 | it 'stores a preferred_view' do
16 | allow(controller).to receive(:search_results)
17 | session[:preferred_view] = 'list'
18 | get :index, params: { q: 'foo', view: 'gallery' }
19 | expect(session[:preferred_view]).to eq 'gallery'
20 | end
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/features/aeon_web_ead_request_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | describe 'Aeon Web EAD Request', :js do
6 | context 'when EAD URL template is provided' do
7 | it 'creates a request link' do
8 | visit solr_document_path 'm0198-xml'
9 |
10 | within '#m0198-xml_aspace_ref11_d0s-hierarchy-item' do
11 | click_on 'Pages 1-78'
12 | end
13 | expect(page).to have_css(
14 | 'a[href*="https://sample.request.com?Action=10&Form=31&Value=http%3A%2F%2Fexample.com%2FM0198.xml',
15 | text: 'Request'
16 | )
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/features/arclight_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'Arclight' do
6 | it 'an index view is present with search results' do
7 | visit search_catalog_path q: '', search_field: 'all_fields'
8 | expect(page).to have_css '.document', count: 10
9 | end
10 |
11 | describe 'eadid with a period' do
12 | it 'is visitable with a hyphen' do
13 | visit solr_document_path('m0198-xml_aspace_ref11_d0s')
14 | expect(page).to have_css 'h1', text: 'Pages 1-78'
15 | end
16 | end
17 |
18 | describe 'show page' do
19 | it 'renders metadata to meet minumum DACS requirements for a component'
20 | end
21 |
22 | describe 'Search history' do
23 | it 'successfully navigates' do
24 | visit blacklight.search_history_path
25 | expect(page).to have_css 'h2', text: 'You have no search history'
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/features/autocomplete_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'Autocomplete', :js do
6 | context 'site-wide search form' do
7 | it 'is configured properly to allow non-prefix autocomplete' do
8 | visit '/catalog'
9 | find_by_id('q').send_keys 'by-laws'
10 | expect(page).to have_content 'amendments to articles'
11 | send_keys(:arrow_down)
12 | send_keys(:tab)
13 | expect(page).to have_field('search for', with: 'amendments to articles of incorporation and revised constitution and by-laws, 1960')
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/features/bookmarks_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'Bookmarks' do
6 | it 'shows bookmarks as checkboxes', :js do
7 | visit solr_document_path('aoa271_aspace_a951375d104030369a993ff943f61a77')
8 | check 'Bookmark'
9 | click_on 'Bookmarks'
10 |
11 | visit solr_document_path('aoa271_aspace_a951375d104030369a993ff943f61a77')
12 | expect(page).to have_css('input[type="checkbox"][checked]')
13 | uncheck 'In Bookmarks'
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/features/collection_context_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'Collection context', :js do
6 | let(:doc_id) { 'aoa271_aspace_6ea193f778e553ca9ea0d00a3e5a1891' }
7 |
8 | before do
9 | visit solr_document_path(id: doc_id)
10 | end
11 |
12 | describe 'highly nested item' do
13 | it 'highlights the correct context item' do
14 | expect(page).to have_css '.al-hierarchy-highlight', text: 'Initial Phase'
15 | end
16 |
17 | it 'siblings are not expanded' do
18 | expect(page).to have_css '.al-toggle-view-children.collapsed[href="#collapsible-hierarchy-aoa271_aspace_b70574c7229e6f237f780579cc04595d"]'
19 | end
20 |
21 | it 'direct ancestors are expanded' do
22 | expect(page).to have_css '#collapsible-hierarchy-aoa271_aspace_f934f1add34289f28bd0feb478e68275.show', visible: :visible
23 | expect(page).to have_css '#collapsible-hierarchy-aoa271_aspace_238a0567431f36f49acea49ef576d408.show', visible: :visible
24 | expect(page).to have_css '#collapsible-hierarchy-aoa271_aspace_563a320bb37d24a9e1e6f7bf95b52671.show', visible: :visible
25 | end
26 |
27 | it 'siblings above are hidden' do
28 | expect(page).to have_no_css '#aoa271_aspace_843e8f9f22bac69872d0802d6fffbb04'
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/spec/features/compact_search_results_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'Compact Search Results' do
6 | it 'As a user I should be able to view results in a compact display' do
7 | visit root_path
8 | click_on 'Search'
9 |
10 | expect(page).to have_css('.documents-list')
11 | expect(page).to have_css('h3.index_title', text: 'Alpha Omega Alpha Archives, 1894-1992')
12 |
13 | click_on 'Compact'
14 |
15 | expect(page).to have_no_css('.documents-list')
16 | expect(page).to have_css('.documents-compact')
17 | expect(page).to have_css('article.document', count: 10)
18 | within '.document-position-3' do
19 | # Has breadcrumbs
20 | expect(page).to have_css '.breadcrumb-links a', text: /National Library of/
21 | # Has Containers
22 | expect(page).to have_css '.al-document-container', text: 'Box 1, Folder 1'
23 | # Has Online Content Indicator
24 | expect(page).to have_css '.al-online-content-icon'
25 | # Has Bookmark Control
26 | expect(page).to have_css 'form.bookmark-toggle'
27 | end
28 | end
29 |
30 | it 'Shows highlights in compact view' do
31 | visit search_catalog_path q: 'william root', search_field: 'name'
32 | click_on 'Compact'
33 | within '.document-position-1' do
34 | within '.al-document-highlight' do
35 | expect(page).to have_css 'em', text: 'William'
36 | expect(page).to have_css 'em', text: 'Root'
37 | end
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/spec/features/document_tools_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | describe 'Document Tools' do
6 | context 'in search results' do
7 | it 'renders the bookmark option for all documents' do
8 | visit search_catalog_path q: '', search_field: 'all_fields'
9 |
10 | expect(page).to have_css('article form.bookmark-toggle', count: 10)
11 | end
12 | end
13 |
14 | context 'on the record view' do
15 | before { visit solr_document_path(doc_id) }
16 |
17 | context 'for collections' do
18 | let(:doc_id) { 'aoa271' }
19 |
20 | it 'does not render the bookmark option' do
21 | expect(page).to have_no_css('.al-document-title-bar form.bookmark-toggle')
22 | end
23 | end
24 |
25 | context 'for components' do
26 | let(:doc_id) { 'm0198-xml_aspace_ref11_d0s' }
27 |
28 | pending 'renders the bookmark option' do
29 | expect(page).to have_css('.al-document-title-bar form.bookmark-toggle')
30 | end
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/spec/features/google_form_request_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | xdescribe 'Google Form Request', :js do
6 | context 'when container is present' do
7 | context 'repository is requestable' do
8 | it 'form is present with filled out values' do
9 | visit solr_document_path 'aoa271_aspace_843e8f9f22bac69872d0802d6fffbb04'
10 |
11 | within 'form' do
12 | expect(page).to have_css(
13 | 'input[name="entry.1980510262"][value$="catalog/aoa271_aspace_843e8f9f22bac69872d0802d6fffbb04"]',
14 | visible: :hidden
15 | )
16 | expect(page).to have_css('input[name="entry.619150170"][value="Alpha Omega Alpha Archives, 1894-1992"]',
17 | visible: :hidden)
18 | expect(page).to have_css 'input[name="entry.14428541"][value="Alpha Omega Alpha"]', visible: :hidden
19 | expect(page).to have_css 'input[name="entry.996397105"][value="aoa271"]', visible: :hidden
20 | expect(page).to have_css 'input[name="entry.1125277048"][value="Box 1 Folder 1"]', visible: :hidden
21 | expect(page).to have_css 'input[name="entry.862815208"][value$="William W. Root, n.d."]', visible: :hidden
22 | expect(page).to have_button 'Request'
23 | end
24 | end
25 |
26 | context 'repository is not requestable' do
27 | it 'form is absent' do
28 | visit solr_document_path 'm0198-xml_aspace_ref14_di4'
29 | expect(page).to have_no_css 'form'
30 | end
31 | end
32 | end
33 | end
34 |
35 | context 'when container is absent' do
36 | it 'form is absent' do
37 | visit solr_document_path 'aoa271_aspace_238a0567431f36f49acea49ef576d408'
38 | expect(page).to have_no_css 'form'
39 | end
40 | end
41 |
42 | context 'in search results' do
43 | it 'shows up when item is requestable' do
44 | visit search_catalog_path q: 'alpha', search_field: 'all_fields'
45 | expect(page).to have_css 'form[action*="https://docs.google.com"]', count: 4
46 | end
47 | end
48 |
49 | context 'in collection hierarchy' do
50 | it 'shows up in hierarchy' do
51 | visit solr_document_path 'aoa271'
52 | click_on 'Contents'
53 | first('.al-toggle-view-all').click
54 | within '#collection-context' do
55 | expect(page).to have_css 'form[action*="https://docs.google.com"]', count: 22
56 | end
57 | end
58 |
59 | it 'shows up in context' do
60 | visit solr_document_path 'aoa271_aspace_843e8f9f22bac69872d0802d6fffbb04'
61 | within '#collection-context' do
62 | expect(page).to have_css 'form[action*="https://docs.google.com"]', count: 3
63 | end
64 | end
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/spec/features/highlighted_search_results_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'Highlighted search results' do
6 | describe 'when querying' do
7 | describe '#all_fields' do
8 | it 'highlights the snippets' do
9 | visit search_catalog_path q: 'student life', search_field: 'all_fields'
10 | within '.document-position-1' do
11 | within '.al-document-highlight' do
12 | expect(page).to have_css 'em', text: /^student$/, count: 2
13 | expect(page).to have_css 'em', text: 'students', count: 1
14 | expect(page).to have_css 'em', text: 'life', count: 1
15 | end
16 | end
17 | end
18 |
19 | it 'does not highlight the snippets on empty query' do
20 | visit search_catalog_path q: '', search_field: 'all_fields'
21 | within '.document-position-1' do
22 | expect(page).to have_no_css '.al-document-highlight'
23 | end
24 | end
25 | end
26 |
27 | describe '#name' do
28 | it 'highlights the snippets' do
29 | visit search_catalog_path q: 'william root', search_field: 'name'
30 | within '.document-position-1' do
31 | within '.al-document-highlight' do
32 | expect(page).to have_css 'em', text: 'William', count: 1
33 | expect(page).to have_css 'em', text: 'Root', count: 2
34 | end
35 | end
36 | end
37 | end
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/spec/features/item_breadcrumbs_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'Item breadcrumb' do
6 | it 'results page shows navigable breadcrumbs' do
7 | visit search_catalog_path q: 'expansion', search_field: 'all_fields'
8 | document = page.all('article').find do |doc|
9 | doc.all('h3 a', text: 'Phase II: Expansion').present?
10 | end
11 |
12 | within document do
13 | within '.breadcrumb-links' do
14 | expect(page).to have_link 'National Library of Medicine. History of Medicine Division'
15 | expect(page).to have_link 'Alpha Omega Alpha Archives'
16 | expect(page).to have_link 'Series I: Administrative Records'
17 | expect(page).to have_link 'Reports'
18 | expect(page).to have_link 'Expansion Plan'
19 | click_on 'Expansion Plan'
20 | end
21 | end
22 | expect(page).to have_css 'h1', text: 'Expansion Plan'
23 | end
24 |
25 | it 'show page breadcrumbs' do
26 | visit solr_document_path id: 'aoa271_aspace_e8755922a9336970292ca817983e7139'
27 | expect(page).to have_css 'li.breadcrumb-item a', count: 5
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/spec/features/many_component_ead_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'Many component EAD' do
6 | describe 'hierarchy', :js do
7 | before { visit solr_document_path 'lc0100' }
8 |
9 | it 'includes all components' do
10 | within '#collection-context' do
11 | expect(page).to have_css 'li.al-collection-context', count: 202
12 | end
13 | end
14 |
15 | it 'includes all children' do
16 | within '#collection-context' do
17 | click_on 'View'
18 | click_on 'Expand'
19 | within '#collapsible-hierarchy-lc0100_aspace_327a75c226d44aa1a769edb4d2f13c6e' do
20 | expect(page).to have_css 'li.al-collection-context', count: 202
21 | end
22 | end
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spec/features/masthead_links_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'Masthead links' do
6 | describe 'collection link' do
7 | it 'is not active when collection search is not activated' do
8 | visit search_catalog_path q: 'a brief', search_field: 'all_fields'
9 | within '.al-masthead' do
10 | expect(page).to have_no_css 'li.nav-item.active', text: 'Collections'
11 | end
12 | end
13 |
14 | it 'is active when collection search is activated' do
15 | visit search_catalog_path f: { level: ['Collection'] }, search_field: 'all_fields'
16 | within '.al-masthead' do
17 | expect(page).to have_css 'li.nav-item.active', text: 'Collections'
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/features/online_content_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'Online Content' do
6 | let(:doc_id) { 'aoa271_aspace_843e8f9f22bac69872d0802d6fffbb04' }
7 |
8 | before { visit solr_document_path(id: doc_id) }
9 |
10 | describe 'Viewer' do
11 | context 'embedded content' do
12 | let(:doc_id) { 'a0011-xml_aspace_ref6_lx4' }
13 |
14 | it 'renders digital object viewer initialization markup', :js do
15 | expect(page).to have_css(
16 | '.al-oembed-viewer[data-arclight-oembed-url-value="http://purl.stanford.edu/kc844kt2526"]',
17 | visible: :all
18 | )
19 | end
20 | end
21 |
22 | context 'non-embeddable content' do
23 | let(:doc_id) { 'aoa271_aspace_843e8f9f22bac69872d0802d6fffbb04' }
24 |
25 | it 'renders a list of links', :js do
26 | expect(page).to have_link 'Folder of digitized stuff'
27 | expect(page).to have_link 'Letter from Christian B. Anfinsen'
28 | end
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/spec/features/repositories_page_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'Repositores Page' do
6 | it 'is navigabe from the home page' do
7 | visit '/'
8 |
9 | within '.al-repositories' do
10 | expect(page).to have_css('.al-repository h2 a', text: 'My Repository')
11 | end
12 |
13 | expect(page.body).to include('Repositories - Arclight ')
14 | end
15 |
16 | describe 'Repostory Show Page' do
17 | it 'is navigable form the Repositories page' do
18 | visit '/repositories'
19 |
20 | click_on 'Stanford University Libraries. Special Collections and University Archives'
21 |
22 | expect(page).to have_css('h2', text: 'Our Collections')
23 | end
24 |
25 | it 'links to all the repositories collections' do
26 | visit '/repositories'
27 |
28 | click_on 'Stanford University Libraries. Special Collections and University Archives'
29 |
30 | click_on 'View all of our collections'
31 |
32 | expect(page).to have_css('h2', text: 'Search Results')
33 | end
34 |
35 | it 'does not link the same page in the repository card header' do
36 | visit '/repositories'
37 |
38 | click_on 'Stanford University Libraries. Special Collections and University Archives'
39 |
40 | within '.al-repository' do
41 | expect(page).to have_no_css(
42 | 'h2 a',
43 | text: 'Stanford University Libraries. Special Collections and University Archives'
44 | )
45 | end
46 | end
47 |
48 | it 'has a title title starting with the repository name' do
49 | visit '/repositories'
50 |
51 | click_on 'Stanford University Libraries. Special Collections and University Archives'
52 |
53 | expect(page.body).to include('Stanford University Libraries. Special Collections and University Archives - Arclight ')
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/spec/features/search_breadcrumb_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'Search Breadcrumb' do
6 | context 'on regular search results' do
7 | it do
8 | visit search_catalog_path q: 'a brief', search_field: 'all_fields'
9 | within '.al-search-breadcrumb' do
10 | expect(page).to have_link 'Home'
11 | expect(page).to have_content 'Search results'
12 | end
13 | end
14 |
15 | it do
16 | visit search_catalog_path f: { level: ['Collection'] }, search_field: 'all_fields'
17 | within '.al-search-breadcrumb' do
18 | expect(page).to have_link 'Home'
19 | expect(page).to have_content 'Collections'
20 | end
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/fixtures/config/downloads.yml:
--------------------------------------------------------------------------------
1 | #
2 | # downloads.yml - Use the EAD's as the primary key and
3 | # provide the PDF and/or EAD (.xml) links. The
4 | # size value should be a String (shown as-is) or
5 | # the number of bytes in the download.
6 | #
7 | #
8 | MS C 271:
9 | pdf:
10 | href: 'http://example.com/MS+C+271.pdf'
11 | size: '1.23MB'
12 | ead:
13 | href: 'http://example.com/MS+C+271.xml'
14 | size: 123456
15 | default:
16 | # disabled: true
17 | pdf:
18 | template: 'http://example.com/%{unitid}.pdf'
19 | # size_accessor: finding_aid_size
20 | ead:
21 | template: 'http://example.com/%{unitid}.xml'
22 |
--------------------------------------------------------------------------------
/spec/i18n_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'i18n/tasks'
4 |
5 | RSpec.describe 'I18n', type: :feature do
6 | let(:i18n) { I18n::Tasks::BaseTask.new }
7 | let(:missing_keys) { i18n.missing_keys }
8 | let(:unused_keys) { i18n.unused_keys }
9 |
10 | it 'does not have missing keys' do
11 | expect(missing_keys).to be_empty,
12 | "Missing #{missing_keys.leaves.count} i18n keys, run `i18n-tasks missing' to show them"
13 | end
14 |
15 | it 'does not have unused keys' do
16 | expect(unused_keys).to be_empty,
17 | "#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them"
18 | end
19 |
20 | it 'files are normalized' do
21 | non_normalized = i18n.non_normalized_paths
22 | error_message = "The following files need to be normalized:\n" \
23 | "#{non_normalized.map { |path| " #{path}" }.join("\n")}\n" \
24 | 'Please run `i18n-tasks normalize` to fix'
25 | expect(non_normalized).to be_empty, error_message
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/spec/lib/arclight/digital_object_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight::DigitalObject do
6 | subject(:instance) do
7 | described_class.new(label: 'An object label', href: 'https://example.com/an-object-href')
8 | end
9 |
10 | describe 'label' do
11 | let(:empty_label) do
12 | described_class.new(label: '', href: 'https://example.com/an-object-href')
13 | end
14 |
15 | it 'uses href if label is blank' do
16 | expect(empty_label.href).to eq 'https://example.com/an-object-href'
17 | end
18 | end
19 |
20 | describe '#to_json' do
21 | it 'returns a json serialization of the object' do
22 | json = JSON.parse(instance.to_json)
23 | expect(json).to be_a Hash
24 | expect(json['label']).to eq 'An object label'
25 | end
26 | end
27 |
28 | describe "#{described_class}.from_json" do
29 | it 'returns an instance of the class given the parsed json' do
30 | deserialized = described_class.from_json(instance.to_json)
31 | expect(deserialized).to be_a described_class
32 | expect(deserialized.label).to eq 'An object label'
33 | end
34 | end
35 |
36 | describe '==' do
37 | let(:dissimilar) do
38 | described_class.new(label: 'A different label', href: 'https://example.com/an-object-href')
39 | end
40 |
41 | let(:similar) do
42 | described_class.new(label: 'An object label', href: 'https://example.com/an-object-href')
43 | end
44 |
45 | it 'is true when href and label are similar' do
46 | expect(instance).not_to eq dissimilar
47 | end
48 |
49 | it 'is false when objects have dissimilar labels' do
50 | expect(instance).to eq similar
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/spec/lib/arclight/level_label_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 | require 'arclight/level_label'
5 |
6 | RSpec.describe Arclight::LevelLabel do
7 | subject(:level_label) { described_class.new(level, other_level).to_s }
8 |
9 | context 'when level is collection' do
10 | let(:level) { 'collection' }
11 | let(:other_level) { nil }
12 |
13 | it 'capitalizes it' do
14 | expect(level_label).to eq 'Collection'
15 | end
16 | end
17 |
18 | context 'when level has a custom human-readable value defined' do
19 | let(:level) { 'recordgrp' }
20 | let(:other_level) { nil }
21 |
22 | it 'uses the human-readable form' do
23 | expect(level_label).to eq 'Record Group'
24 | end
25 | end
26 |
27 | context 'when level is otherlevel & one is specified' do
28 | let(:level) { 'otherlevel' }
29 | let(:other_level) { 'binder' }
30 |
31 | it 'capitalizes specified value' do
32 | expect(level_label).to eq 'Binder'
33 | end
34 | end
35 |
36 | context 'when level is otherlevel without one specified' do
37 | let(:level) { 'otherlevel' }
38 | let(:other_level) { nil }
39 |
40 | it 'uses Other' do
41 | expect(level_label).to eq 'Other'
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/spec/lib/arclight/missing_id_strategy_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 | require 'arclight/missing_id_strategy'
5 |
6 | RSpec.describe Arclight::MissingIdStrategy do
7 | subject(:strategy) { described_class.selected }
8 |
9 | it 'defaults to Arclight::HashAbsoluteXpath' do
10 | expect(strategy).to eq Arclight::HashAbsoluteXpath
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/spec/lib/arclight/normalized_date_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight::NormalizedDate do
6 | subject(:normalized_date) { described_class.new(date_inclusive, date_bulk, date_other).to_s }
7 |
8 | let(:date_inclusive) { ['1990-2000'] }
9 | let(:date_bulk) { '1999-2005' }
10 | let(:date_other) { 'Undated' }
11 |
12 | context 'under normal conditions' do
13 | it 'joins dates' do
14 | expect(normalized_date).to eq '1990-2000, Undated, bulk 1999-2005'
15 | end
16 |
17 | context 'multiple normalized dates' do
18 | let(:date_inclusive) { %w[1990 1992] }
19 |
20 | it 'are joined w/ a comma' do
21 | expect(normalized_date).to eq '1990, 1992, Undated, bulk 1999-2005'
22 | end
23 | end
24 | end
25 |
26 | context 'with special case dates' do
27 | # NOTE: This test is the only place where the code that exercises this is routable
28 | # This has to be a multidimensional array, and the resulting XML nodes sent in are always flat
29 | context 'multiples' do
30 | let(:date_inclusive) { [%w[1990-2000 2001-2002 2004]] }
31 | let(:date_bulk) { '1990-2004' }
32 |
33 | it 'uses compressed joined years' do
34 | expect(normalized_date).to eq '1990-2002, 2004, Undated, bulk 1990-2004'
35 | end
36 | end
37 |
38 | context 'undated' do
39 | let(:date_bulk) { 'n.d.' }
40 |
41 | it 'do not normalized term "undated"' do
42 | expect(normalized_date).to eq '1990-2000, Undated, bulk n.d.'
43 | end
44 | end
45 |
46 | context 'circa' do
47 | let(:date_bulk) { 'c.1995' }
48 |
49 | it 'do not normalized term "circa"' do
50 | expect(normalized_date).to eq '1990-2000, Undated, bulk c.1995'
51 | end
52 | end
53 |
54 | context 'no bulk' do
55 | let(:date_bulk) { nil }
56 | let(:date_other) { nil }
57 |
58 | it 'uses inclusive date only' do
59 | expect(normalized_date).to eq '1990-2000'
60 | end
61 | end
62 |
63 | context 'no inclusive or bulk but other' do
64 | let(:date_inclusive) { nil }
65 | let(:date_bulk) { nil }
66 | let(:date_other) { 'n.d.' }
67 |
68 | it 'uses other' do
69 | expect(normalized_date).to eq 'n.d.'
70 | end
71 | end
72 |
73 | context 'no inclusive but bulk' do
74 | let(:date_inclusive) { nil }
75 |
76 | it 'uses other and bulk' do
77 | expect(normalized_date).to eq 'Undated, bulk 1999-2005'
78 | end
79 | end
80 |
81 | context 'no information' do
82 | let(:date_inclusive) { nil }
83 | let(:date_bulk) { nil }
84 | let(:date_other) { nil }
85 |
86 | it 'does not know what to do' do
87 | expect(normalized_date).to be_nil
88 | end
89 | end
90 | end
91 | end
92 |
--------------------------------------------------------------------------------
/spec/lib/arclight/normalized_id_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight::NormalizedId do
6 | subject(:normalized_id) { described_class.new(id).to_s }
7 |
8 | context 'when the id as a period in it' do
9 | let(:id) { 'abc123.xml' }
10 |
11 | it 'replaces it with a hyphen' do
12 | expect(normalized_id).to eq 'abc123-xml'
13 | end
14 | end
15 |
16 | context 'when the id has extra space in it' do
17 | let(:id) { ' abc123 ' }
18 |
19 | it 'is stripped' do
20 | expect(normalized_id).to eq 'abc123'
21 | end
22 | end
23 |
24 | context 'when the id is nil' do
25 | let(:id) { nil }
26 |
27 | it do
28 | expect { normalized_id }.to raise_error(
29 | Arclight::Exceptions::IDNotFound,
30 | 'id must be present for all documents and components'
31 | )
32 | end
33 | end
34 |
35 | context 'when additional keyword arguments are supplied' do
36 | subject(:normalized_id) do
37 | described_class.new('abc123.xml', unitid: 'abc-123', title: 'a title', repository: 'repo').to_s
38 | end
39 |
40 | it 'accepts the additional arguments without changing the output' do
41 | expect(normalized_id).to eq 'abc123-xml'
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/spec/models/arclight/requests/aeon_external_request_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight::Requests::AeonExternalRequest do
6 | subject(:valid_object) { described_class.new(document, {}) }
7 |
8 | let(:config_hash) do
9 | {
10 | 'request_url' => 'https://example.com/aeon/aeon.dll',
11 | 'request_mappings' => {
12 | 'url_params' => {
13 | 'Action' => 11,
14 | 'Type' => 200
15 | },
16 | 'static' => {
17 | 'SystemId' => 'ArcLight',
18 | 'ItemInfo1' => 'manuscript'
19 | },
20 | 'accessor' => {
21 | 'ItemTitle' => 'collection_name'
22 | }
23 | }
24 | }
25 | end
26 |
27 | let(:config) do
28 | instance_double Arclight::Repository,
29 | request_config_for_type: config_hash
30 | end
31 | let(:document) do
32 | instance_double SolrDocument,
33 | repository_config: config,
34 | collection_name: 'Cool Document'
35 | end
36 |
37 | describe '#url' do
38 | it 'constructs from the repository config' do
39 | expect(valid_object.url).to eq 'https://example.com/aeon/aeon.dll?Action=11&Type=200'
40 | end
41 | end
42 |
43 | describe '#form_mapping' do
44 | it 'compiles from the repository config' do
45 | expect(valid_object.form_mapping).to eq('SystemId' => 'ArcLight',
46 | 'ItemInfo1' => 'manuscript',
47 | 'ItemTitle' => 'Cool Document')
48 | end
49 | end
50 |
51 | describe '#static_mappings' do
52 | it 'pulls from the repository config' do
53 | expect(valid_object.static_mappings).to eq('SystemId' => 'ArcLight',
54 | 'ItemInfo1' => 'manuscript')
55 | end
56 | end
57 |
58 | describe '#dynamic_mappings' do
59 | it 'pulls from the repository config' do
60 | expect(valid_object.dynamic_mappings).to eq('ItemTitle' => 'Cool Document')
61 | end
62 | end
63 |
64 | describe '#url_params' do
65 | it 'constructs from the repository config' do
66 | expect(valid_object.url_params).to eq('Action=11&Type=200')
67 | end
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/spec/models/arclight/requests/aeon_web_ead_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight::Requests::AeonWebEad do
6 | subject(:valid_object) { described_class.new(document, 'http://example.com/sample.xml') }
7 |
8 | let(:config) do
9 | instance_double Arclight::Repository,
10 | request_config_for_type: {
11 | request_url: 'https://sample.request.com',
12 | request_mappings: 'Action=10&Form=31&Value=ead_url'
13 | }.with_indifferent_access
14 | end
15 | let(:document) { instance_double SolrDocument, repository_config: config }
16 |
17 | describe '#request_url' do
18 | it 'returns from the repository config' do
19 | expect(valid_object.request_url).to eq 'https://sample.request.com'
20 | end
21 | end
22 |
23 | describe '#url' do
24 | it 'constructs a url with params' do
25 | expect(valid_object.url).to eq 'https://sample.request.com?Action=10&Form=31&Value=http%3A%2F%2Fexample.com%2Fsample.xml'
26 | end
27 | end
28 |
29 | describe '#form_mapping' do
30 | subject(:form_mapping) { valid_object.form_mapping }
31 |
32 | it 'converts string from config to hash' do
33 | expect(form_mapping).to be_an Hash
34 | end
35 |
36 | it 'has valid key/value pairs' do
37 | expect(form_mapping).to include('Action' => '10', 'Form' => '31', 'Value' => 'http://example.com/sample.xml')
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/spec/models/arclight/requests/google_form_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe Arclight::Requests::GoogleForm do
6 | subject(:valid_object) { described_class.new(document, presenter, '') }
7 |
8 | let(:config) do
9 | instance_double Arclight::Repository,
10 | request_config_for_type: { request_url: 'https://docs.google.com/abc123',
11 | request_mappings: 'collection_name=abc&eadid=123' }.with_indifferent_access
12 | end
13 | let(:document) { instance_double SolrDocument, repository_config: config }
14 | let(:presenter) { instance_double Arclight::ShowPresenter, heading: 'Indiana Jones and the Last Crusade' }
15 |
16 | describe 'API' do
17 | it 'responds to needed methods for mapping' do
18 | %i[collection_name eadid containers title].each do |method|
19 | expect(valid_object).to respond_to(method)
20 | end
21 | end
22 | end
23 |
24 | describe '#url' do
25 | it 'returns from the repository config' do
26 | expect(valid_object.url).to eq 'https://docs.google.com/abc123'
27 | end
28 | end
29 |
30 | describe '#form_mapping' do
31 | subject(:form_mapping) { valid_object.form_mapping }
32 |
33 | it 'converts string from config to hash' do
34 | expect(form_mapping).to be_an Hash
35 | end
36 |
37 | it 'has valid key/value pairs' do
38 | expect(form_mapping).to include('collection_name' => 'abc', 'eadid' => '123')
39 | end
40 | end
41 |
42 | describe '#title' do
43 | it 'gets the heading from the presenter' do
44 | expect(valid_object.title).to eq 'Indiana Jones and the Last Crusade'
45 | end
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/spec/models/concerns/arclight/search_behavior_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | describe Arclight::SearchBehavior do
6 | subject(:search_builder_instance) { search_builder.with(user_params) }
7 |
8 | let(:user_params) { {} }
9 | let(:solr_params) { {} }
10 | let(:context) { CatalogController.new }
11 | let(:search_builder_class) do
12 | Class.new(Blacklight::SearchBuilder).tap do |klass|
13 | include Blacklight::Solr::SearchBuilderBehavior
14 | klass.include(described_class)
15 | end
16 | end
17 | let(:search_builder) { search_builder_class.new(context) }
18 |
19 | describe '#add_highlighting' do
20 | it 'enables highlighting' do
21 | expect(search_builder_instance.add_highlighting(solr_params)).to include('hl' => true)
22 | end
23 | end
24 |
25 | describe '#add_grouping' do
26 | context 'when group is selected' do
27 | let(:user_params) { { group: 'true' } }
28 |
29 | it 'adds grouping params' do
30 | expect(search_builder_instance.add_grouping(solr_params)).to include(Arclight::Engine.config.catalog_controller_group_query_params)
31 | end
32 | end
33 |
34 | context 'when group is not selected' do
35 | it 'enables highlighting' do
36 | expect(search_builder_instance.add_grouping(solr_params)).not_to include(
37 | group: true
38 | )
39 | end
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/spec/presenters/arclight/index_presenter_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | describe Arclight::IndexPresenter, type: :presenter do
6 | subject { presenter }
7 |
8 | let(:view_context) { ActionView::Base.new(nil, {}, nil) }
9 |
10 | let(:config) { Blacklight::Configuration.new }
11 |
12 | let(:presenter) { described_class.new(document, view_context, config) }
13 |
14 | let(:document) do
15 | SolrDocument.new(id: 1,
16 | 'normalized_title_ssm' => 'My Title, 1900-2000')
17 | end
18 |
19 | before do
20 | config.index.title_field = :normalized_title_ssm
21 | end
22 |
23 | describe '#label' do
24 | it 'uses normalized title' do
25 | expect(presenter.label).to eq 'My Title, 1900-2000'
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/presenters/arclight/show_presenter_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | describe Arclight::ShowPresenter, type: :presenter do
6 | subject { presenter }
7 |
8 | let(:view_context) { ActionView::Base.new(nil, {}, nil) }
9 | let(:config) { Blacklight::Configuration.new }
10 |
11 | let(:presenter) { described_class.new(document, view_context, config) }
12 |
13 | let(:document) do
14 | SolrDocument.new(id: 1,
15 | 'normalized_title_ssm' => ['My Title, 1900-2000'])
16 | end
17 |
18 | before do
19 | config.show.title_field = :normalized_title_ssm
20 | allow(view_context).to receive(:action_name).and_return(:show)
21 | end
22 |
23 | describe '#heading' do
24 | it 'uses normalized title' do
25 | expect(presenter.heading).to eq 'My Title, 1900-2000'
26 | end
27 | end
28 |
29 | describe '#with_field_group' do
30 | it 'is nil when none is set' do
31 | expect(presenter.send(:field_group)).to be_nil
32 | end
33 |
34 | it 'sets the field group based on the given field accessor (and returns the presenter)' do
35 | returned_presenter = presenter.with_field_group('a_group')
36 | expect(returned_presenter).to be_a Arclight::ShowPresenter
37 | expect(returned_presenter.send(:field_group)).to eq 'a_group'
38 | end
39 | end
40 |
41 | describe '#field_config' do
42 | it 'returns a field configuration (NullField in this context)' do
43 | if Blacklight::VERSION > '8'
44 | expect(presenter.send(:field_config, 'some_field')).to be_a Blacklight::Configuration::NullDisplayField
45 | else
46 | expect(presenter.send(:field_config, 'some_field')).to be_a Blacklight::Configuration::NullField
47 | end
48 | end
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/spec/routing/arclight/repositories_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'Vanity repositories routes' do
6 | routes { Arclight::Engine.routes }
7 | context 'repositories' do
8 | it '#index' do
9 | expect(get: '/repositories').to route_to(
10 | controller: 'arclight/repositories',
11 | action: 'index'
12 | )
13 | end
14 |
15 | it '#show' do
16 | expect(get: '/repositories/my-slug').to route_to(
17 | controller: 'arclight/repositories',
18 | action: 'show',
19 | id: 'my-slug'
20 | )
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/routing/collections_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'Vanity collections route' do
6 | routes { Arclight::Engine.routes }
7 | it 'routes to collection search' do
8 | expect(get: '/collections').to route_to(
9 | 'f' => { 'level' => ['Collection'] },
10 | controller: 'catalog',
11 | action: 'index'
12 | )
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/support/controller_level_helpers.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ControllerLevelHelpers
4 | def search_state
5 | @search_state ||= Blacklight::SearchState.new(params, blacklight_config, controller)
6 | end
7 |
8 | def blacklight_configuration_context
9 | @blacklight_configuration_context ||= Blacklight::Configuration::Context.new(controller)
10 | end
11 |
12 | delegate :blacklight_config, to: :CatalogController
13 | end
14 |
--------------------------------------------------------------------------------
/spec/test_app_templates/Gemfile.extra:
--------------------------------------------------------------------------------
1 | if ENV['BLACKLIGHT_VERSION'] == 'github'
2 | gem 'blacklight', github: 'projectblacklight/blacklight'
3 | elsif ENV['BLACKLIGHT_VERSION'] && !ENV['BLACKLIGHT_VERSION'].empty?
4 | gem 'blacklight', ENV['BLACKLIGHT_VERSION']
5 | end
6 |
--------------------------------------------------------------------------------
/spec/test_app_templates/lib/generators/test_app_generator.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails/generators'
4 |
5 | # :nodoc:
6 | class TestAppGenerator < Rails::Generators::Base
7 | source_root './spec/test_app_templates'
8 |
9 | # if you need to generate any additional configuration
10 | # into the test app, this generator will be run immediately
11 | # after setting up the application
12 |
13 | def add_gems
14 | Bundler.with_clean_env do
15 | run 'bundle install'
16 | end
17 | end
18 |
19 | # This makes the assets available in the test app so that changes made in
20 | # local development can be picked up automatically
21 | def link_frontend
22 | run 'yarn link'
23 | end
24 |
25 | def run_blacklight_generator
26 | say_status('warning', 'GENERATING BL', :yellow)
27 |
28 | generate 'blacklight:install', '--devise'
29 | end
30 |
31 | def install_engine
32 | generate 'arclight:install --test'
33 | end
34 |
35 | def add_test_locales
36 | initializer 'test_locale_configuration.rb' do
37 | 'Blacklight::LocalePicker::Engine.config.available_locales = [:en, :es]'
38 | end
39 | end
40 |
41 | def add_custom_download
42 | config_download = <<~YML
43 | M0198:
44 | disabled: false
45 | ead:
46 | template: 'http://example.com/%{collection_unitid}.xml'
47 | YML
48 | inject_into_file 'config/downloads.yml', config_download, after: "template: 'http://example.com/%{unitid}.xml'\n"
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/spec/views/_requests.html.erb_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'arclight/_requests' do
6 | let(:document) { SolrDocument.new(id: 'abc123') }
7 | let(:config) { instance_double(Arclight::Repository) }
8 | let(:blacklight_config) { Blacklight::Configuration.new }
9 |
10 | before do
11 | allow(document).to receive_messages(repository_config: config, requestable?: true)
12 | allow(view).to receive_messages(blacklight_config: blacklight_config, action_name: 'show', document: document)
13 | end
14 |
15 | context 'with EAD documents which require Aeon requests' do
16 | let(:document_downloads) { instance_double(Arclight::DocumentDownloads::File) }
17 |
18 | before do
19 | allow(document_downloads).to receive(:href).and_return('https://sample.request.com')
20 | allow(document).to receive(:ead_file).and_return(document_downloads)
21 | allow(config).to receive_messages(available_request_types: [:aeon_web_ead],
22 | request_config_for_type: { 'request_url' => 'https://sample.request.com',
23 | 'request_mappings' => 'Action=10&Form=31&Value=ead_url' })
24 |
25 | render
26 | end
27 |
28 | it 'renders links to the Aeon request form' do
29 | expect(rendered).to have_css '.al-request'
30 | expect(rendered).to have_css '.al-request a[href^="https://sample.request.com"]'
31 | end
32 | end
33 |
34 | context 'with EAD documents which require external Aeon requests' do
35 | let(:config_hash) do
36 | {
37 | 'request_url' => 'https://example.com/aeon/aeon.dll',
38 | 'request_mappings' => {
39 | 'url_params' => {
40 | 'Action' => 11,
41 | 'Type' => 200
42 | },
43 | 'static' => {
44 | 'SystemId' => 'ArcLight',
45 | 'ItemInfo1' => 'manuscript'
46 | },
47 | 'accessor' => {
48 | 'ItemTitle' => 'collection_name'
49 | }
50 | }
51 | }
52 | end
53 |
54 | before do
55 | allow(config).to receive_messages(available_request_types: [:aeon_external_request_endpoint], request_config_for_type: config_hash)
56 | render
57 | end
58 |
59 | it 'renders links to the external Aeon request endpoint' do
60 | expect(rendered).to have_css '.al-request-form'
61 | expect(rendered).to have_css '.al-request-form[action^="https://example.com/aeon/aeon.dll"]'
62 | end
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/spec/views/repositories/show.html.erb_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'spec_helper'
4 |
5 | RSpec.describe 'arclight/repositories/show' do
6 | let(:test_data) { Arclight::Repository.find_by(slug: 'sample') }
7 |
8 | before do
9 | ENV['REPOSITORY_FILE'] = 'spec/fixtures/config/repositories.yml'
10 | assign(:repository, test_data)
11 | assign(:collections, [])
12 | allow(view).to receive(:search_action_url).and_return('/')
13 | end
14 |
15 | context 'renders a repository detail page' do
16 | before { render }
17 |
18 | it 'has the repository card' do
19 | expect(rendered).to have_css('.al-repository h2', text: /My Repository/)
20 | end
21 |
22 | it 'has breadcrumbs' do
23 | expect(rendered).to have_css('.al-search-breadcrumb', text: /My Repository/)
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/tasks/arclight.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'solr_wrapper'
4 | require 'engine_cart/rake_task'
5 | require 'rspec/core/rake_task'
6 | require 'arclight'
7 |
8 | class DependencyNotInstalled < StandardError; end
9 |
10 | # Build with our opinionated defaults if none are provided.
11 | rails_options = ENV.fetch('ENGINE_CART_RAILS_OPTIONS', '')
12 | rails_options = "#{rails_options} -a propshaft" unless rails_options.match?(/-a\s|--asset-pipeline/)
13 | rails_options = "#{rails_options} -j importmap" unless rails_options.match?(/-j\s|--javascript/)
14 | rails_options = "#{rails_options} --css bootstrap" unless rails_options.match?(/--css/)
15 | ENV['ENGINE_CART_RAILS_OPTIONS'] = rails_options
16 |
17 | desc 'Run test suite'
18 | task ci: %w[arclight:generate] do
19 | SolrWrapper.wrap do |solr|
20 | solr.with_collection do
21 | Rake::Task['arclight:seed'].invoke
22 | within_test_app do
23 | system 'bin/rake spec:prepare'
24 | end
25 | Rake::Task['spec'].invoke
26 | end
27 | end
28 | end
29 |
30 | desc 'Run Eslint'
31 | task :eslint do
32 | raise DependencyNotInstalled, 'ESLint not found. Please run yarn install.' unless File.exist?('./node_modules/.bin/eslint')
33 |
34 | exit 1 unless system './node_modules/.bin/eslint app/assets/javascripts'
35 | end
36 |
37 | namespace :arclight do
38 | desc 'Generate a test application'
39 | task generate: %w[engine_cart:generate]
40 |
41 | desc 'Run Solr and Blacklight for interactive development'
42 | task :server, %i[rails_server_args] do |_t, args|
43 | if File.exist? EngineCart.destination
44 | within_test_app do
45 | system 'bundle update'
46 | end
47 | else
48 | Rake::Task['engine_cart:generate'].invoke
49 | end
50 |
51 | print 'Starting Solr...'
52 | SolrWrapper.wrap do |solr|
53 | puts 'done.'
54 | solr.with_collection do
55 | Rake::Task['arclight:seed'].invoke
56 | within_test_app do
57 | system "bin/dev #{args[:rails_server_args]}"
58 | end
59 | end
60 | end
61 | end
62 |
63 | desc 'Seed fixture data to Solr'
64 | task :seed do
65 | puts 'Seeding index with data from spec/fixtures/ead...'
66 | Dir.glob('spec/fixtures/ead/*').each do |dir|
67 | next unless File.directory?(dir)
68 |
69 | within_test_app do
70 | # Sets the REPOSITORY_ID to the name of the file's containing directory
71 | system("REPOSITORY_ID=#{File.basename(dir)} " \
72 | "REPOSITORY_FILE=#{Arclight::Engine.root}/spec/fixtures/config/repositories.yml " \
73 | "DIR=#{Arclight::Engine.root}/#{dir} " \
74 | 'SOLR_URL=http://127.0.0.1:8983/solr/blacklight-core ' \
75 | 'rake arclight:index_dir')
76 | end
77 | end
78 | end
79 | end
80 |
--------------------------------------------------------------------------------
/template.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | gem 'arclight'
4 |
5 | after_bundle do
6 | generate 'blacklight:install', '--devise'
7 | generate 'arclight:install', '-f'
8 |
9 | rake 'db:migrate'
10 | end
11 |
--------------------------------------------------------------------------------