├── VERSION ├── user_documentation ├── user_doc.txt ├── after_copy.png ├── after_load.png ├── examples │ ├── images │ │ ├── readme.txt │ │ ├── load_popup.png │ │ ├── results_do.png │ │ ├── added_first_ss.png │ │ ├── first_ss_copied.png │ │ ├── first_ss_report.png │ │ ├── add_ss_load_report.png │ │ ├── tree_after_add_load.png │ │ ├── add_load_report_in_xls.png │ │ ├── empty_collection_edit.png │ │ ├── empty_collection_view.png │ │ ├── new_load_spreadsheet.png │ │ ├── select_archival_object.png │ │ ├── empty_collection_results.png │ │ ├── empty_test_file_selection.png │ │ └── empty_collection_finished_popup.png │ ├── empty_test_collection.xlsx │ ├── results │ │ ├── first_ss_report.xlsx │ │ ├── add_ss_load_report.xlsx │ │ └── readme.txt │ ├── add_to_hl_test_ingest_collection.xlsx │ └── example.md ├── EmptyResource.png ├── file_selected.png ├── load_report.png ├── copied_into_ss.png ├── load_completion.png ├── load_on_object.png ├── selecting_file.png ├── load_on_resource.png ├── OpenLoadSpreadsheet.png ├── descriptionLevelDropDown.png ├── digital_objects_instructions.md ├── USER_DOCUMENTATION.md └── archival_objects_instructions.md ├── Gemfile ├── templates ├── aspace_import_excel_template.xlsx ├── aspace_import_excel_DO_template.xlsx └── extended_aspace_import_excel_template.xlsx ├── frontend ├── views │ ├── layout_head.html.erb │ └── resources │ │ ├── _bulk_file_form.html.erb │ │ └── _bulk_response.html.erb ├── controllers │ ├── concerns │ │ ├── updates_utils.rb │ │ └── linked_objects.rb │ └── resources_updates_controller.rb ├── routes.rb ├── models │ ├── enum_list.rb │ ├── digital_object_handler.rb │ ├── ingest_report.rb │ ├── handler.rb │ ├── subject_handler.rb │ ├── container_instance_handler.rb │ └── agent_handler.rb ├── plugin_init.rb ├── locales │ └── en.yml └── assets │ ├── tree_extensions.js │ ├── clipboard.js │ └── javascripts │ └── utils.js ├── extras ├── README.md └── modified_initialize-plugin.bat ├── .gitignore ├── README.md └── LICENSE /VERSION: -------------------------------------------------------------------------------- 1 | v3.0.3 2 | -------------------------------------------------------------------------------- /user_documentation/user_doc.txt: -------------------------------------------------------------------------------- 1 | placeholder 2 | -------------------------------------------------------------------------------- /user_documentation/after_copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/after_copy.png -------------------------------------------------------------------------------- /user_documentation/after_load.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/after_load.png -------------------------------------------------------------------------------- /user_documentation/examples/images/readme.txt: -------------------------------------------------------------------------------- 1 | This files contains images that are used in the /user_documentation/examples/readme.md 2 | -------------------------------------------------------------------------------- /user_documentation/EmptyResource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/EmptyResource.png -------------------------------------------------------------------------------- /user_documentation/file_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/file_selected.png -------------------------------------------------------------------------------- /user_documentation/load_report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/load_report.png -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | ASpaceGems.setup if defined? ASpaceGems 2 | 3 | source 'http://rubygems.org' 4 | 5 | gem 'rubyXL', "3.3.29", :require => false 6 | 7 | -------------------------------------------------------------------------------- /user_documentation/copied_into_ss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/copied_into_ss.png -------------------------------------------------------------------------------- /user_documentation/load_completion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/load_completion.png -------------------------------------------------------------------------------- /user_documentation/load_on_object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/load_on_object.png -------------------------------------------------------------------------------- /user_documentation/selecting_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/selecting_file.png -------------------------------------------------------------------------------- /user_documentation/load_on_resource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/load_on_resource.png -------------------------------------------------------------------------------- /templates/aspace_import_excel_template.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/templates/aspace_import_excel_template.xlsx -------------------------------------------------------------------------------- /user_documentation/OpenLoadSpreadsheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/OpenLoadSpreadsheet.png -------------------------------------------------------------------------------- /templates/aspace_import_excel_DO_template.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/templates/aspace_import_excel_DO_template.xlsx -------------------------------------------------------------------------------- /user_documentation/descriptionLevelDropDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/descriptionLevelDropDown.png -------------------------------------------------------------------------------- /user_documentation/examples/images/load_popup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/load_popup.png -------------------------------------------------------------------------------- /user_documentation/examples/images/results_do.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/results_do.png -------------------------------------------------------------------------------- /templates/extended_aspace_import_excel_template.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/templates/extended_aspace_import_excel_template.xlsx -------------------------------------------------------------------------------- /user_documentation/examples/images/added_first_ss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/added_first_ss.png -------------------------------------------------------------------------------- /user_documentation/examples/empty_test_collection.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/empty_test_collection.xlsx -------------------------------------------------------------------------------- /user_documentation/examples/images/first_ss_copied.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/first_ss_copied.png -------------------------------------------------------------------------------- /user_documentation/examples/images/first_ss_report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/first_ss_report.png -------------------------------------------------------------------------------- /user_documentation/examples/results/first_ss_report.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/results/first_ss_report.xlsx -------------------------------------------------------------------------------- /user_documentation/examples/images/add_ss_load_report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/add_ss_load_report.png -------------------------------------------------------------------------------- /user_documentation/examples/images/tree_after_add_load.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/tree_after_add_load.png -------------------------------------------------------------------------------- /user_documentation/examples/images/add_load_report_in_xls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/add_load_report_in_xls.png -------------------------------------------------------------------------------- /user_documentation/examples/images/empty_collection_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/empty_collection_edit.png -------------------------------------------------------------------------------- /user_documentation/examples/images/empty_collection_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/empty_collection_view.png -------------------------------------------------------------------------------- /user_documentation/examples/images/new_load_spreadsheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/new_load_spreadsheet.png -------------------------------------------------------------------------------- /user_documentation/examples/images/select_archival_object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/select_archival_object.png -------------------------------------------------------------------------------- /user_documentation/examples/results/add_ss_load_report.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/results/add_ss_load_report.xlsx -------------------------------------------------------------------------------- /user_documentation/examples/results/readme.txt: -------------------------------------------------------------------------------- 1 | This directory contains Excel Spreadsheets that have been created by pasting the "copied to clipboard" results of example spreadsheet loads 2 | -------------------------------------------------------------------------------- /user_documentation/examples/images/empty_collection_results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/empty_collection_results.png -------------------------------------------------------------------------------- /user_documentation/examples/add_to_hl_test_ingest_collection.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/add_to_hl_test_ingest_collection.xlsx -------------------------------------------------------------------------------- /user_documentation/examples/images/empty_test_file_selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/empty_test_file_selection.png -------------------------------------------------------------------------------- /user_documentation/examples/images/empty_collection_finished_popup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-library/aspace-import-excel/HEAD/user_documentation/examples/images/empty_collection_finished_popup.png -------------------------------------------------------------------------------- /frontend/views/layout_head.html.erb: -------------------------------------------------------------------------------- 1 | <% if controller.controller_name == 'resources' && controller.action_name == 'edit' %> 2 | <%= javascript_include_tag "#{@base_url}/assets/clipboard.js" %> 3 | <%= javascript_include_tag "#{@base_url}/assets/tree_extensions.js" %> 4 | 5 | <% end %> 6 | -------------------------------------------------------------------------------- /extras/README.md: -------------------------------------------------------------------------------- 1 | # The extras/ Directory 2 | This directory contains files that may need to be copied to another directory upon installation. 3 | 4 | ## modified_initialize-plugin.bat 5 | 6 | This DOS batch file is used to overcome a problem with **\scripts\initialize-plugin.bat**, which is currently downloading into the **\plugins\aspace-import-excel** the latest version of [Bundler](https://bundler.io/) that is incompatible with the version of Bundler that the core ArchivesSpace uses. -------------------------------------------------------------------------------- /extras/modified_initialize-plugin.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | SETLOCAL ENABLEDELAYEDEXPANSION 4 | 5 | cd /d %~dp0..\plugins\%1 6 | 7 | for /d %%a in (..\..\gems\gems\bundler-*) do set bnm=%%a 8 | for /f "tokens=1* delims=-" %%a in ("%bnm%") do set vers=%%b 9 | echo %vers% 10 | 11 | set JRUBY= 12 | FOR /D %%c IN (..\..\gems\gems\jruby-*) DO ( 13 | set JRUBY=!JRUBY!;%%c\lib\* 14 | ) 15 | 16 | set GEM_HOME=gems 17 | java %JAVA_OPTS% -cp "..\..\lib\*!JRUBY!" org.jruby.Main -S gem install bundler -v "%vers%" 18 | java %JAVA_OPTS% -cp "..\..\lib\*!JRUBY!" org.jruby.Main -S ..\..\gems\bin\bundle install --gemfile=Gemfile 19 | -------------------------------------------------------------------------------- /frontend/controllers/concerns/updates_utils.rb: -------------------------------------------------------------------------------- 1 | module UpdatesUtils 2 | extend ActiveSupport::Concern 3 | 4 | # contains methods needed to support validation and other processing of various classes 5 | 6 | 7 | # returns true if the input object validates, otherwise raises an erro 8 | def self.test_exceptions(obj, what = '') 9 | # Pry::ColorPrinter.pp "TESTING #{what}: #{obj.jsonmodel_type}" 10 | ret_val = false 11 | begin 12 | obj._exceptions 13 | true 14 | rescue Exception => e 15 | # Pry::ColorPrinter.pp e.message 16 | # Pry::ColorPrinter.pp ASUtils.jsonmodels_to_hashes(obj) 17 | # Pry::ColorPrinter.pp e.backtrace[1..2] 18 | raise ExcelImportException.new("editable?") if e.message.include?("editable?") 19 | raise e 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /frontend/routes.rb: -------------------------------------------------------------------------------- 1 | ArchivesSpace::Application.routes.draw do 2 | scope AppConfig[:frontend_proxy_prefix] do 3 | match 'resources/:rid/getfile' => 'resources_updates#get_file', :via => [:post] 4 | match 'resources/:rid/getfile' => 'resources_updates#get_file', :via => [:get] 5 | # match 'resources/ssload' => 'resources_updates#load_ss', :via => [:post] 6 | match 'resources/:id/ssload' => 'resources_updates#load_ss', :via => [:post] 7 | match 'resources/:id/ssload' => 'resources_updates#load_ss', :via => [:get] 8 | match 'resources/:id/getdofile' => 'resources_updates#get_do_file', :via => [:get] 9 | match 'resources/:id/getdofile' => 'resources_updates#get_do_file', :via => [:post] 10 | match 'resources/:id/digital_load' => 'resources_updates#load_dos', :via => [:get] 11 | match 'resources/:id/digital_load' => 'resources_updates#load_dos', :via => [:post] 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | /coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /spec/examples.txt 9 | /test/tmp/ 10 | /test/version_tmp/ 11 | /tmp/ 12 | 13 | # Used by dotenv library to load environment variables. 14 | # .env 15 | 16 | ## Specific to RubyMotion: 17 | .dat* 18 | .repl_history 19 | build/ 20 | *.bridgesupport 21 | build-iPhoneOS/ 22 | build-iPhoneSimulator/ 23 | 24 | ## Specific to RubyMotion (use of CocoaPods): 25 | # 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 29 | # 30 | # vendor/Pods/ 31 | 32 | ## Documentation cache and generated files: 33 | /.yardoc/ 34 | /_yardoc/ 35 | /doc/ 36 | /rdoc/ 37 | 38 | ## Environment normalization: 39 | /.bundle/ 40 | /vendor/bundle 41 | /lib/bundler/man/ 42 | 43 | # for a library or gem, you might want to ignore these files since the code is 44 | # intended to run in multiple environments; otherwise, check them in: 45 | # Gemfile.lock 46 | # .ruby-version 47 | # .ruby-gemset 48 | 49 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 50 | .rvmrc 51 | -------------------------------------------------------------------------------- /frontend/models/enum_list.rb: -------------------------------------------------------------------------------- 1 | class EnumList 2 | require 'pp' 3 | @list = [] 4 | @list_hash = {} 5 | @which = '' 6 | 7 | def initialize(which) 8 | @which = which 9 | renew 10 | end 11 | 12 | def value(label) 13 | if @list_hash[label] 14 | v = @list_hash[label] 15 | elsif @list.index(label) 16 | v = label 17 | end 18 | raise Exception.new(I18n.t('plugins.aspace-import-excel.error.enum',:label =>label,:which => @which)) if !v 19 | v 20 | end 21 | 22 | def length 23 | @list.length 24 | end 25 | 26 | def renew 27 | @list = [] 28 | list_hash = {} 29 | enums = JSONModel(:enumeration).all 30 | enums_list = ASUtils.jsonmodels_to_hashes(enums) 31 | enums_list.each do |enum| 32 | if enum['name'] == @which 33 | enum['values'].each do |v| 34 | if v 35 | trans = I18n.t("enumerations.#{@which}.#{v}", default: v) 36 | if !list_hash[trans] 37 | list_hash[trans] = v 38 | @list.push v 39 | else 40 | Rails.logger.warn(I18n.t('plugins.aspace-import-excel.warn.dup', :which => @which, :trans => trans, :used => list_hash[trans])) 41 | end 42 | end 43 | end 44 | break 45 | end 46 | end 47 | @list_hash = list_hash 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /frontend/plugin_init.rb: -------------------------------------------------------------------------------- 1 | # set this to true if you don't want to allow the digital object loading functionality 2 | AppConfig[:hide_do_load] = false 3 | 4 | # handle the spreadsheet load 5 | my_routes = File.join(File.dirname(__FILE__), "routes.rb") 6 | # ArchivesSpace::Application.config.paths['config/routes'].concat(my_routes) 7 | if ArchivesSpace::Application.respond_to?(:extend_aspace_routes) 8 | ArchivesSpace::Application.extend_aspace_routes(my_routes) 9 | else 10 | ArchivesSpace::Application.config.paths['config/routes'].concat([my_routes]) 11 | end 12 | # create a special exception for this import 13 | 14 | class ExcelImportException < Exception 15 | end 16 | 17 | # create a "stop everything" exception 18 | 19 | class StopExcelImportException < Exception 20 | end 21 | 22 | # override the editable? method so errors end up rescued as ValidationExceptions 23 | Rails.application.config.after_initialize do 24 | class ClientEnumSource 25 | def editable?(name) 26 | begin 27 | MemoryLeak::Resources.get(:enumerations).fetch(name).editable? 28 | rescue Exception => e 29 | Rails.logger.error("Blowup for #{name}! #{e.message}") 30 | end 31 | end 32 | end 33 | end 34 | 35 | 36 | # Work around small difference in rubyzip API (from https://github.com/hudmol/nla_staff_spreadsheet_importer/blob/2a28e6379a6748877ab433735153bba96be09b12/backend/plugin_init.rb) 37 | module Zip 38 | if !defined?(Error) 39 | class Error < StandardError 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /frontend/controllers/concerns/linked_objects.rb: -------------------------------------------------------------------------------- 1 | module LinkedObjects 2 | extend ActiveSupport::Concern 3 | 4 | 5 | # This module originally incorporated all the classes needed to handle objects that must be linked to 6 | # Archival Objects, such as Subjects, Top Containers, etc. These classes have be refactored out, and 7 | # can be found in aspace-import-excel/frontend/models 8 | # a lot of this is adapted from Hudson Mlonglo's Arrearage plugin: 9 | # https://github.com/hudmol/nla_staff_spreadsheet_importer/blob/master/backend/converters/arrearage_converter.rb 10 | 11 | # ParentTracker, used to keep track of hierarchy, remains in this module 12 | 13 | 14 | #shamelessly stolen (and adapted from HM's nla_staff_spreadsheet plugin :-) 15 | class ParentTracker 16 | require 'pp' 17 | def set_uri(hier, uri) 18 | @current_hierarchy ||= {} 19 | @current_hierarchy = Hash[@current_hierarchy.map {|k, v| 20 | if k < hier 21 | [k, v] 22 | end 23 | }.compact] 24 | 25 | # Record the URI of the current record 26 | @current_hierarchy[hier] = uri 27 | end 28 | def parent_for(hier) 29 | # Level 1 parent may be a resource record and therefore nil, 30 | if hier > 0 31 | parent_level = hier - 1 32 | @current_hierarchy.fetch(parent_level) 33 | else 34 | nil 35 | end 36 | end 37 | end #of ParentTracker 38 | 39 | end 40 | -------------------------------------------------------------------------------- /frontend/models/digital_object_handler.rb: -------------------------------------------------------------------------------- 1 | class DigitalObjectHandler < Handler 2 | @@digital_object_types ||= EnumList.new('digital_object_digital_object_type') 3 | 4 | def self.create(row, archival_object, report) 5 | dig_o = nil 6 | dig_instance = nil 7 | thumb = row['thumbnail'] || row['Thumbnail'] 8 | unless !thumb && !row['digital_object_link'] 9 | files = [] 10 | if !row['digital_object_link'].blank? && row['digital_object_link'].start_with?('http') 11 | fv = JSONModel(:file_version).new._always_valid! 12 | fv.file_uri = row['digital_object_link'] 13 | fv.publish = row['publish'] 14 | fv.xlink_actuate_attribute = 'onRequest' 15 | fv.xlink_show_attribute = 'new' 16 | files.push fv 17 | end 18 | if !thumb.blank? && thumb.start_with?('http') 19 | fv = JSONModel(:file_version).new._always_valid! 20 | fv.file_uri = thumb 21 | fv.publish = row['publish'] 22 | fv.xlink_actuate_attribute = 'onLoad' 23 | fv.xlink_show_attribute = 'embed' 24 | fv.is_representative = true 25 | files.push fv 26 | end 27 | osn = row['digital_object_id'].blank? ? (archival_object.ref_id + 'd') : row['digital_object_id'] 28 | dig_o = JSONModel(:digital_object).new._always_valid! 29 | dig_o.title = row['digital_object_title'].blank? ? archival_object.display_string : row['digital_object_title'] 30 | dig_o.digital_object_id = osn 31 | dig_o.file_versions = files 32 | dig_o.publish = row['publish'] 33 | begin 34 | dig_o.save 35 | rescue ValidationException => ve 36 | report.add_errors(I18n.t('plugins.aspace-import-excel.error.dig_validation', :err => ve.errors)) 37 | return nil 38 | rescue Exception => e 39 | raise e 40 | end 41 | report.add_info(I18n.t('plugins.aspace-import-excel.created', :what =>I18n.t('plugins.aspace-import-excel.dig'), :id => "'#{dig_o.title}' #{dig_o.uri} [#{dig_o.digital_object_id}]")) 42 | dig_instance = JSONModel(:instance).new._always_valid! 43 | dig_instance.instance_type = 'digital_object' 44 | dig_instance.digital_object = {"ref" => dig_o.uri} 45 | end 46 | dig_instance 47 | end 48 | 49 | def self.renew 50 | clear(@@digital_object_types) 51 | end 52 | end # DigitalObjectHandler 53 | 54 | -------------------------------------------------------------------------------- /frontend/views/resources/_bulk_file_form.html.erb: -------------------------------------------------------------------------------- 1 |
<%= report.terminal_error %>
29 |
30 | 3. Click on the button. You will see a Load Spreadsheet modal window, with the rest of the page "greyed out".
31 | If you are at the Resource level, the modal window will look like this:
32 | If you are at the Archival Object level, the modal window will look like this:
33 |
34 | 4. Click on "Select File" to browse and locate a file on your system. Select the Excel File.
35 | 5. Click on either the **Import Archival Objects** or **Add Digital Objects to Archival Objects** button, if you are given the choice.
36 | 5. Click on **"Import from SpreadSheet"**. The Ingester will start; the rest of the page will continue to be "greyed out".
37 | 6. When the ingest is finished, there will be an alert pop-up.
38 | 7. Click to close the popup, and you will be presented with a report of the processing.
39 | 8. You can click on "Copy to clipboard" to get a tabbed version of the report to examine and/or save.
40 |
41 | *back to Workflow*
42 |
--------------------------------------------------------------------------------
/frontend/models/container_instance_handler.rb:
--------------------------------------------------------------------------------
1 | # Supporting multiple containers in the row
2 |
3 | class ContainerInstanceHandler < Handler
4 | @@top_containers = {}
5 | @@container_types ||= EnumList.new('container_type')
6 | @@instance_types ||= EnumList.new('instance_instance_type') # for when we move instances over here
7 |
8 | def self.renew
9 | clear( @@container_types)
10 | clear(@@instance_types)
11 | end
12 |
13 | def self.key_for(top_container, resource)
14 | key = "'#{resource}' #{top_container[:type]}: #{top_container[:indicator]}"
15 | key += " #{top_container[:barcode]}" if top_container[:barcode]
16 | key
17 | end
18 |
19 | def self.build(row,substr)
20 | {
21 | :type => @@container_types.value(row.fetch("type_1#{substr}", 'Box') || 'Box'),
22 | :indicator => row.fetch("indicator_1#{substr}", 'Unknown') || 'Unknown',
23 | :barcode => row.fetch("barcode#{substr}",nil)
24 | }
25 | end
26 |
27 | # returns a top container JSONModel
28 | def self.get_or_create(row, substr, resource, report)
29 | begin
30 | top_container = build(row, substr)
31 | tc_key = key_for(top_container, resource)
32 | # check to see if we already have fetched one from the db, or created one.
33 | existing_tc = @@top_containers.fetch(tc_key, false) || get_db_tc(top_container, resource)
34 | if !existing_tc
35 | tc = JSONModel(:top_container).new._always_valid!
36 | tc.type = top_container[:type]
37 | tc.indicator = top_container[:indicator]
38 | tc.barcode = top_container[:barcode] if top_container[:barcode]
39 | tc.repository = {'ref' => resource.split('/')[0..2].join('/')}
40 | # UpdateUtils.test_exceptions(tc,'top_container')
41 | tc.save
42 | report.add_info(I18n.t('plugins.aspace-import-excel.created', :what =>"#{I18n.t('plugins.aspace-import-excel.tc')} [#{tc.type} #{tc.indicator}]", :id=> tc.uri))
43 | existing_tc = tc
44 | end
45 | rescue Exception => e
46 | report.add_errors(I18n.t('plugins.aspace-import-excel.error.no_tc', :why => e.message + " in linked_objects"))
47 | existing_tc = nil
48 | end
49 | @@top_containers[tc_key] = existing_tc if existing_tc
50 | existing_tc
51 | end
52 |
53 | def self.get_db_tc(top_container, resource_uri)
54 | repo_id = resource_uri.split('/')[2]
55 | if !(ret_tc = get_db_tc_by_barcode(top_container[:barcode], repo_id))
56 | tc_str = "#{top_container[:type]} #{top_container[:indicator]}"
57 | tc_str += ": [#{top_container[:barcode]}]" if top_container[:barcode]
58 | tc_params = {}
59 | tc_params["type[]"] = 'top_container'
60 | tc_params["q"] = "display_string:\"#{tc_str}\" AND collection_uri_u_sstr:\"#{resource_uri}\""
61 | ret_tc = search(repo_id,tc_params, :top_container,'', "display_string:#{tc_str}")
62 | end
63 | ret_tc
64 | end
65 |
66 | def self.get_db_tc_by_barcode(barcode, repo_id)
67 | ret_tc = nil
68 | if barcode
69 | tc_params = {}
70 | tc_params["type[]"] = 'top_container'
71 | tc_params["q"] = "barcode_u_sstr:\"#{barcode}\""
72 | ret_tc = search(repo_id,tc_params, :top_container)
73 | end
74 | ret_tc
75 | end
76 |
77 | def self.create_container_instance(row, substr, resource_uri,report)
78 | instance = nil
79 | raise ExcelImportException.new(I18n.t('plugins.aspace-import-excel.error.missing_instance_type')) if row["cont_instance_type#{substr}"].blank?
80 | begin
81 | tc = get_or_create(row, substr, resource_uri, report)
82 | sc = {'top_container' => {'ref' => tc.uri},
83 | 'jsonmodeltype' => 'sub_container'}
84 | %w(2 3).each do |num|
85 | if row["type_#{num}#{substr}"]
86 | sc["type_#{num}"] = @@container_types.value(row["type_#{num}#{substr}"])
87 | sc["indicator_#{num}"] = row["indicator_#{num}#{substr}"] || 'Unknown'
88 | end
89 | end
90 | instance = JSONModel(:instance).new._always_valid!
91 | instance.instance_type = @@instance_types.value(row["cont_instance_type#{substr}"])
92 | instance.sub_container = JSONModel(:sub_container).from_hash(sc)
93 | rescue ExcelImportException => ee
94 | raise ee
95 | rescue Exception => e
96 | msg = e.message #+ "\n" + e.backtrace()[0]
97 | raise ExcelImportException.new(msg)
98 | end
99 | instance
100 | end
101 |
102 | end # of container handler
103 |
--------------------------------------------------------------------------------
/user_documentation/examples/example.md:
--------------------------------------------------------------------------------
1 | # An Example of Using aspace-import-excel
2 |
3 | Included in this directory are two spreadsheets which you can use to follow the step-by-step description below, where you create an empty Resource, populate it with the first spreadsheet, then add to it with the second.
4 |
5 | ## Create a new Resource
6 |
7 | Create a new Resource of type **Collection**. You make it as minimal as you like, but you must assign an EAD ID of **hl_test_ingest**. Don't create any archival objects for it.
8 |
9 |
10 | ## Loading the First Spreadsheet
11 |
12 |
13 |
14 |
15 | ### Select the Spreadsheet
16 |
17 | With your new Resource in *edit* mode, click on the "Load via Spreadsheet" button.
18 |
19 |
20 |
21 |
22 | Click on the **Add File** button, and select the **empty_test_collection.xlsx** file, that you've downloade from here . This spreadsheet creates two top level "Series" Archival Objects; the second Archival Object will also have a child "Item" object. There are a few errors in the spreadsheet, so that you can see the error reporting mechanism.
23 |
24 | This spreadsheet has specified an Agent by the Agent ID of 3760. If you don't have an Agent with that ID, an agent will be created, with the header of "PLACEHOLDER FOR person agent ID 3760 NOT FOUND", and reported as such in the results.
25 | It also specifies a subject with the ID of 837, but also specifies the term, type, and source. If you don't have a subject with that ID, a new subject will be created based on that information; again, it will be reported in the results.
26 |
27 |
28 |
29 | Here's what it looks like from an MS Windows view:
30 |
31 |
32 |
33 | ### Click "Import From Spreadsheet"
34 |
35 | The importer will "gray out" that button, and begin processing. When it is completed, you will see a confirmation pop-up:
36 |
37 |
38 | Click "OK", and you will be presented with the report of the results:
39 |
40 |
41 | ### Copying the results
42 |
43 | If you click on the "Copy to Clipboard" button, you will get a "Copied" confirmation popup. You will now have
44 | get a tabbed copy of the results in your clipboard, which you can then paste into a text file, Word document, Excel spreadsheet, etc. We've pasted it into an Excel spreadsheet, which we've also uploaded to GitHub:
45 |
46 |
47 | ### Addressing errors
48 |
49 | Reading the results, you will see that the processing of each Archival Object did not go smoothly. For example, for the object **"The Early Years,1990 - 1995"**, a Container instance was not created because there was a problem with the container *child type*.
50 |
51 | You can interactively create a Container instance for that object. We suggest you use the Top Container ("Box 1") that was created, because it's referenced in the next spreadsheet. Otherwise, a second free-standing "Box 1" Top Container will be created when the second spreadsheet is run.
52 |
53 | Similarly, you can edit the other two objects, if you like.
54 |
55 |
56 | ## Adding Children and Siblings to the new Resource
57 |
58 | ### Select Your Upload Point
59 |
60 | With your Resource in **edit** mode, select the "**The Early Years, 1990 - 1995**" archival object.
61 |
62 |
63 |
64 | ### Load Spreadsheet
65 |
66 | As above, click on "Load via Spreadsheet", add the **add_to_hl_test_ingest_collection.xlsx** file that you've downloaded from here, then click on "Import from SpreadSheet".
67 |
68 | This spreadsheet also specifies Agent ID 3760. If you already had an agent with the ID, that is what will be assigned to it; otherwise, the agent created by the ingest of the previous spreadsheet will be used. Similarly, the subject ID 837 is referenced as well, and treated the same.
69 |
70 | ### Results
71 |
72 | These are the expected results:
73 |
74 |
75 | If you had not edited the "**The Early Years, 1990 - 1995**" archival object to add the Container instance, as described above, you will also see a "Top Container [box 1] created..." message.
76 |
77 | These results also were copied and pasted into an Excel spreadsheet:
78 |
79 |
80 |
81 | We have also uploaded the actual spreadsheet here.
82 |
83 | And here's a view of the Resource's "tree" after the two spreadsheets have been loaded:
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/frontend/locales/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | plugins:
3 | aspace-import-excel:
4 | import: Import from SpreadSheet
5 | add_file: Select File
6 | drag_drop: Drag and drop file here
7 | clip_btn: Copy to Clipboard
8 | add_archival_objects: Import Archival Objects
9 | add_digital_objects: Add Digital Objects to Archival Objects
10 | row: "Row %{row}"
11 | processing_row: "Processing row %{row}"
12 | dig_assoc: Digital Object added to Archival Object
13 | row_error: "Row %{row} will not be processed due to errors: %{errs}"
14 | no_ao: No Archival Object created
15 | created: "%{what} created: %{id}"
16 | updated: "%{what}: %{id}"
17 | clip_created: "\t%{what} created: \t%{nm}\t%{id}\t%{ref_id}"
18 | clip_what: "\t%{what} : \t%{nm}\t%{id}\t%{ref_id}"
19 | clip_info: "\t\t\t\t\t%{what}\n"
20 | clip_err: "\t\t\t\t\tERROR: %{err}\n"
21 | clip_header: "Row\tStatus\tTitle\tURI\tRef ID\tInfo"
22 | ao: Archival Object
23 | tc: Top Container
24 | subj: Subject
25 | agent: Agent
26 | dig: Digital Object
27 | unfound_id: "PLACEHOLDER FOR %{type} ID %{id} NOT FOUND"
28 | ref_id_notfound: "Ref Id %{refid} not found"
29 | warn:
30 | dup: "Managed Controlled Value List %{which} has multiple instances for the Translation '%{trans}'. '%{used}' will be used as the value."
31 | disam: "Multiple match(es) found. Creating %{name} for disabiguation."
32 | single_date_end: "Single date %{date_str} has end date that will be ignored."
33 | error:
34 | date_type: "Date type [%{what}] invalid for %{date_str}. Defaulting to 'inclusive'"
35 | date_label: "Date label [%{what}] invalid for %{date_str}. The date will not be processed."
36 | certainty: "Invalid 'date certainty' ignored for %{date_str}: (%{what})"
37 | below_bad_ao: Cannot process because it's a child of the bad archival object
38 | enum: "NOT FOUND: '%{label}' not found in list %{which}"
39 | invalid_date: "Invalid date definition (%{what}) for %{date_str}. The date will not be processed."
40 | invalid_date_label: "Invalid date label definition in first date (%{what})"
41 | too_many: More than one match found in the database
42 | type_undef: Unable to determine type
43 | file_name: File name cannot be determined
44 | system: "Some system error has occurred [%{msg}]."
45 | initialize: "Processing is terminated [%{msg}]"
46 | stopped: "Processing stopped at row %{row} [%{msg}]"
47 | duplicates: "This spreadsheet has duplicate Archive Space Field codes: %{codes}"
48 | res_ead: This form's Resource is missing an EAD ID
49 | row_ead: This row is missing an EAD ID
50 | ead_mismatch: "Form's EAD ID [%{res_ead}] does not match row's EAD ID [%{row_ead}]"
51 | title: Missing title
52 | title_and_date: Missing Title AND Valid Date definition
53 | hier_miss: Missing hierachy -- must be a number greater than 0
54 | hier_zero: Hierarchy must be greater than 0
55 | hier_wrong: Hierarchy cannot not be more than one level deeper than the previous row
56 | hier_wrong_resource: did you mean to start processing with an archival object selected?
57 | hier_below_error_level: The parent archival object was not created
58 | level: Missing valid Description level
59 | date: "Date must have at least one of: Date begin; Date end; or Date expression"
60 | number: Missing Extent number
61 | extent_type: Missing Extent type
62 | extent_validation: "Unable to validate extent (%{ext}): %{msg}"
63 | no_header: No header (field codes) row found; are you using the correct template?
64 | no_data: No processible data rows found!
65 | excel: "Error(s) parsing Excel File %{errs}"
66 | no_agent: "Unable to create Agent %{num}: [%{why}]"
67 | no_tc: "Unable to create Top Container %{num}: [%{why}]"
68 | missing_instance_type: Missing container instance type
69 | no_container_instance: "Unable to create Container Instance: [%{why}]"
70 | no_subject: "Unable to create Subject %{num}: [%{why}]"
71 | no_move: "Unable to move the archival objects from the end of the list (response code %{code})"
72 | bad_note: "%{type} note is not wellformed: %{msg}"
73 | bad_relator: "Unable to create agent link: '%{label}' is not a valid relator"
74 | relator_invalid: "Unable to create agent link due to problem with relator '%{label}': %{why}"
75 | bad_role: "Unable to create agent link: '%{label}' is not a valid role"
76 | role_invalid: "Unable to create agent link due to problem with role '%{label}': %{why}"
77 |
78 | has_dig_obj: "Archival object already has an associated digital object"
79 | dig_unassoc: "Unable to save archival object with associated digital object: %{msg}"
80 | ref_id_miss: No Ref Id specified
81 | dig_info_miss: Neither the Digital Object URN or the Thumbnail URN is specified
82 | dig_validation: "Cannot create the Digital Object %{err}"
83 | initial_save_error: "Problem with initial save of %{title} -- %{msg}"
84 | second_save_error: "Error on attempt to re-save archival object with 'instances' %{title} position: %{pos}.This means that the archival object has been created, but possibly not linked to its associated instances (digital object, top container, subject, etc.) [%{what}]"
85 | ao_validation: "Validation error when attempting to save Archival Object: %{err}"
86 |
--------------------------------------------------------------------------------
/frontend/models/agent_handler.rb:
--------------------------------------------------------------------------------
1 | class AgentHandler < Handler
2 | @@agents = {}
3 | @@agent_role ||= EnumList.new('linked_agent_role')
4 | @@agent_relators ||= EnumList.new('linked_agent_archival_record_relators')
5 | AGENT_TYPES = { 'families' => 'family', 'corporate_entities' => 'corporate_entity', 'people' => 'person'}
6 | def self.renew
7 | clear(@@agent_relators)
8 | clear(@@agent_role)
9 | @@agents = {}
10 | end
11 | def self.key_for(agent)
12 | key = "#{agent[:type]} #{agent[:name]}"
13 | key
14 | end
15 |
16 | def self.build(row, type, num)
17 | id = row.fetch("#{type}_agent_record_id_#{num}", nil)
18 | input_name = row.fetch("#{type}_agent_header_#{num}",nil)
19 | role = row.fetch("#{type}_agent_role_#{num}", nil)
20 | role ='creator' if role.blank?
21 | {
22 | :type => AGENT_TYPES[type],
23 | :id => id,
24 | :name => input_name || (id ? I18n.t('plugins.aspace-import-excel.unfound_id', :id => id, :type => 'Agent') : nil),
25 | :role => role,
26 | :relator => row.fetch("#{type}_agent_relator_#{num}", nil) ,
27 | :id_but_no_name => id && !input_name
28 | }
29 | end
30 |
31 | def self.get_or_create(row, type, num, resource_uri, report)
32 | agent = build(row, type, num)
33 | agent_key = key_for(agent)
34 | if !(agent_obj = stored(@@agents, agent[:id], agent_key))
35 | unless agent[:id].blank?
36 | begin
37 | agent_obj = JSONModel("agent_#{agent[:type]}".to_sym).find(agent[:id])
38 | rescue Exception => e
39 | if e.message != 'RecordNotFound'
40 | raise ExcelImportException.new( I18n.t('plugins.aspace-import-excel.error.no_agent', :num => num, :why => e.message))
41 | end
42 | end
43 | end
44 | begin
45 | if !agent_obj
46 | begin
47 | agent_obj = get_db_agent(agent, resource_uri, num)
48 | rescue Exception => e
49 | if e.message == 'More than one match found in the database'
50 | agent[:name] = agent[:name] + DISAMB_STR
51 | report.add_info(I18n.t('plugins.aspace-import-excel.warn.disam', :name => agent[:name]))
52 | else
53 | raise e
54 | end
55 | end
56 | end
57 | if !agent_obj
58 | agent_obj = create_agent(agent, num)
59 | report.add_info(I18n.t('plugins.aspace-import-excel.created', :what =>"#{I18n.t('plugins.aspace-import-excel.agent')}[#{agent[:name]}]", :id => agent_obj.uri))
60 | end
61 | rescue Exception => e
62 | raise ExcelImportException.new( I18n.t('plugins.aspace-import-excel.error.no_agent', :num => num, :why => e.message))
63 | end
64 | end
65 | agent_link = nil
66 | if agent_obj
67 | if agent[:id_but_no_name]
68 | @@agents[agent[:id].to_s] = agent_obj
69 | else
70 | @@agents[agent_obj.id.to_s] = agent_obj
71 | end
72 | @@agents[agent_key] = agent_obj
73 | agent_link = {"ref" => agent_obj.uri}
74 | begin
75 | agent_link["role"] = @@agent_role.value(agent[:role])
76 | rescue Exception => e
77 | if e.message.start_with?("NOT FOUND")
78 | raise ExcelImportException.new(I18n.t('plugins.aspace-import-excel.error.bad_role', :label => agent[:role]))
79 | else
80 | raise ExcelImportException.new(I18n.t('plugins.aspace-import-excel.error.role_invalid', :label => agent[:role], :why => e.message))
81 | end
82 | end
83 | begin
84 | agent_link["relator"] = @@agent_relators.value(agent[:relator]) if !agent[:relator].blank?
85 | rescue Exception => e
86 | if e.message.start_with?("NOT FOUND")
87 | raise ExcelImportException.new(I18n.t('plugins.aspace-import-excel.error.bad_relator', :label => agent[:relator]))
88 | else
89 | raise ExcelImportException.new(I18n.t('plugins.aspace-import-excel.error.relator_invalid', :label => agent[:relator], :why => e.message))
90 | end
91 | end
92 | end
93 | agent_link
94 | end
95 |
96 | def self.create_agent(agent, num)
97 | begin
98 | ret_agent = JSONModel("agent_#{agent[:type]}".to_sym).new._always_valid!
99 | ret_agent.names = [name_obj(agent)]
100 | ret_agent.publish = !(agent[:id_but_no_name] || agent[:name].ends_with?(DISAMB_STR))
101 | ret_agent.save
102 | rescue Exception => e
103 | raise Exception.new(I18n.t('plugins.aspace-import-excel.error.no_agent', :num => num, :why => e.message))
104 | end
105 | ret_agent
106 | end
107 |
108 | def self.get_db_agent(agent, resource_uri, num)
109 | ret_ag = nil
110 | if agent[:id]
111 | begin
112 | ret_ag = JSONModel("agent_#{agent[:type]}".to_sym).find(agent[:id])
113 | rescue Exception => e
114 | if e.message != 'RecordNotFound'
115 | raise ExcelImportException.new( I18n.t('plugins.aspace-import-excel.error.no_agent', :num => num, :why => e.message))
116 | end
117 | end
118 | end
119 | if !ret_ag
120 | a_params = {"q" => "title:\"#{agent[:name]}\" AND primary_type:agent_#{agent[:type]}"}
121 | repo = resource_uri.split('/')[2]
122 | ret_ag = search(repo, a_params, "agent_#{agent[:type]}".to_sym,'', "title:#{agent[:name]}")
123 | end
124 | ret_ag
125 | end
126 |
127 | def self.name_obj(agent)
128 | obj = JSONModel("name_#{agent[:type]}".to_sym).new._always_valid!
129 | obj.source = 'ingest'
130 | obj.authorized = true
131 | obj.is_display_name = true
132 | if agent[:type] == 'family'
133 | obj.family_name = agent[:name]
134 | else
135 | obj.primary_name = agent[:name]
136 | obj.name_order = 'direct' if agent[:type] == 'person'
137 | end
138 | obj
139 | end
140 | end # agent
141 |
142 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # aspace-import-excel
2 | An [ArchivesSpace ](http://archivesspace.org/) [plugin](https://github.com/archivesspace/tech-docs/blob/master/customization/plugins.md) to support the bulk uploading via Excel SpreadSheet of Archival Objects and (optionally) their associated Creator Agents, Top Containers, Subjects, Digital Objects etc.
3 |
4 | Also supports the import of spreadsheets that will allow for the creation of Digital Objects to be associated with already-created Archival Objects for **Version 2.2.2 and higher** of ArchiveSpace.
5 |
6 | ## Current Version
7 |
8 | For versions of ArchivesSpace **before** v2.2.2: [v1.7.8](https://github.com/harvard-library/aspace-import-excel/releases/tag/v1.7.8)
9 |
10 | **NOTE**: v1.7.8 does *not* support the creation of Digital Objects to be associated with already-created Archival Objects.
11 |
12 | For ArchivesSpace **v2.2.2 and higher**: [v3.0.2](https://github.com/harvard-library/aspace-import-excel/releases/tag/v3.0.3)
13 |
14 | ## Development
15 |
16 | This plugin supports interactive selection of an archival object (or resource) as the starting point of the bulk upload.
17 |
18 | Version 3.0 incorporates new functionality for uploading archival objects (described in the [user documentation](user_documentation/archival_objects_instructions.md)), which supports the use of an [expansion](templates/extended_aspace_import_excel_template.xlsx) to the [original](templates/aspace_import_excel_template.xlsx) Excel template. Version 3.0 is, however, backward compatible, so that users whose workflow is satisfied with the original template can continue to use it.
19 |
20 | ### Bulk upload/creation of Archival Objects
21 |
22 | The Excel templates will be found in the templates/ folder as
23 | * *New in V3.0*: [**extended_aspace_import_excel_template**](templates/extended_aspace_import_excel_template.xlsx)
24 |
25 | * [**aspace_import_excel_template.xlsx**](templates/aspace_import_excel_template.xlsx).
26 |
27 | The intention is not to completely reproduce a Finding Aid as presented in an EAD XML, or to allow for every permutation of Archival Object creation within ArchivesSpace. We are aiming for the "80% rule"; that is, at least 80% of the work that would be done interactively can be replaced by an excel spreadsheet; additional refinements to individual archival objects (such as assignment of locations to top-level containers) would take place interactively.
28 |
29 | See the [user documentation](user_documentation/USER_DOCUMENTATION.md) for more information.
30 |
31 | ### Bulk upload/creation of Digital Objects associated with already-created Archival Objects
32 |
33 | **This functionality is turned on by default** See the Installation instructions for turning it off.
34 |
35 | The Excel template will be found in the templates/ folder as [**aspace_import_excel_DO_template.xlsx**](templates/aspace_import_excel_DO_template.xlsx).
36 |
37 | As with the original development, we are not completely reproducing all the functionality of ArchivesSpace: only one Digital Object, which can have either or both of one:
38 | + File with an *Xlink Actuate Attribute* of **onLoad** and an *Xlink Show Attribute* of **embed**
39 | + File with an Xlink Actuate Attribute of **onRequest** and an *Xlink Show Attribute* of **new**
40 |
41 | See the [user documentation](user_documentation/USER_DOCUMENTATION.md) for more information.
42 |
43 |
44 |
45 | ## Installation
46 |
47 | This is a regular [ArchivesSpace Plug-in](https://github.com/archivesspace/tech-docs/blob/master/customization/plugins.md).
48 |
49 | To install this plug-in:
50 | 1. Either clone this plugin, or download the latest version:
51 | - Clone the plug-in from this [GitHub repository](https://github.com/harvard-library/aspace-import-excel) into the ArchivesSpace **/plugins/** directory.
52 | - Download the zipfile of the appropriate version: see [Current Versions](#current_versions) for links to the appropriate release download. Unzip the download into the **/plugins/** directory. You will probably need to rename the top folder/directory to **aspace-import-excel**.
53 |
54 | 2. (Optional) To turn **off** the functionality for creating Digital Objects associated with already-created Archival objects, you must edit **/plugin/aspace-import-excel/frontend/plugin_init.rb**. Change the line
55 | ```bash
56 | AppConfig[:hide_do_load] = false
57 | ```
58 | to
59 | ```bash
60 | AppConfig[:hide_do_load] = true
61 | ```
62 | 3. **IF** you are running, on Windows, a version of ArchivesSpace that is *lower* than version **2.6.0**:
63 |
64 | There was a problem with Bundler versioning.
65 |
66 | Copy
67 | ```
68 | archivesspace\aspace-import-excel\extras\modified_initialize-plugin.bat
69 | ```
70 | to
71 | ```
72 | archivesspace\scripts
73 | ```
74 |
75 | **UPDATE**: You no longer need to use this modified .bat script **if** you are running ArchivesSpace 2.6.0 or higher.
76 |
77 |
78 | 4. Run the initializer script:
79 | * for Linux, that's
80 | ```bash
81 | scripts/initialize-plugin.sh aspace-import-excel
82 | ```
83 | * for Windows, running an ArchivesSpace version **lower than 2.6.0** ,that's
84 | ```
85 | scripts\modified_initialize-plugin.bat aspace-import-excel
86 | ```
87 | Otherwise, for Windows running ArchivesSpace version **2.6.0** and higher:
88 | ```
89 | scripts\initialize-plugin.bat aspace-import-excel
90 | ```
91 |
92 |
93 | 5. In the **common/config/config.rb** file, add 'aspace-import-excel' to the `AppConfig[:plugins]` array.
94 | 6. Stop and restart ArchivesSpace
95 |
96 | ### Why we don't include a Gemfile.lock in this repository
97 |
98 | We have found that when we include a `Gemfile.lock` file in our plugin, some sites have found that, after initializing the plugin and trying to restart ArchivesSpace, they get errors like this:
99 | ```bash
100 | [!] There was an error parsing Gemfile: You cannot specify the same gem twice with different version requirements.
101 | You specified: rubyzip (~> 1.2.2) and rubyzip (= 1.0.0). Bundler cannot continue.
102 | ```
103 |
104 | This problem does not seem to occur when the `Gemfile.lock` is created through the initialization instead.
105 |
106 |
107 |
108 | ## User Documentation
109 |
110 | User documentation is [available](user_documentation/USER_DOCUMENTATION.md)
111 |
112 | ## Contributors
113 |
114 | * Bobbi Fox: [@bobbi-SMR](https://github.com/bobbi-SMR) (maintainer)
115 | * Robin Wendler: [@rwendler](https://github.com/rwendler)
116 | * Julie Wetherill: [@juliewetherill](https://github.com/juliewetherill)
117 | * Adrienne Pruitt: [@adriennepruitt2](https://github.com/adriennepruitt2)
118 | * Dave Mayo: [@pobocks](https://github.com/pobocks)
119 | * h/t to Chintan Desai: [@cdesai-qi](https://github.com/cdesai-qi) for catching inconsistencies
120 |
--------------------------------------------------------------------------------
/frontend/assets/tree_extensions.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2017 Harvard Library
3 | License: MIT license (https://opensource.org/licenses/MIT )
4 | Author: Bobbi Fox
5 | Version: 1.04
6 |
7 | This script supports the ingest into ArchivesSpace of Excel Spreadsheet data. It currently supports both
8 | ArchivesSpace 1.* and ArchivesSpace 2.*
9 | */
10 |
11 |
12 | $(function () {
13 | var aspace_version = (typeof(TreeToolbarConfiguration) === 'undefined')? 1 : 2;
14 | var file_modal_html = '';
15 | var $file_form_modal;
16 |
17 | /* used in aspace v1.* */
18 | var bulk_btn_str = 'Load via Spreadsheet';
19 |
20 |
21 | /* returns a hash with information about the selected archival object or resource */
22 | var get_object_info = function() {
23 | var ret_obj = new Object;
24 | var $tree = $("#archives_tree");
25 | var $obj_form = $("#archival_object_form");
26 | if (typeof $obj_form.attr("action") !== 'undefined') {
27 | ret_obj.type = "archival_object";
28 | ret_obj.aoid = $obj_form.find("#id").val();
29 | ret_obj.ref_id = $obj_form.find("#archival_object_ref_id_").val();
30 | ret_obj.resource = $obj_form.find("#archival_object_resource_").val();
31 | ret_obj.rid = (aspace_version === 1)? $tree.attr("data-root-id") : ret_obj.resource.split('/').pop();
32 | ret_obj.position = $obj_form.find("#archival_object_position_").val();
33 | }
34 | else {
35 | $obj_form = $("#resource_form");
36 | if (typeof $obj_form.attr("action") !== 'undefined') {
37 | ret_obj.type = "resource";
38 | ret_obj.resource = $obj_form.attr("action");
39 | ret_obj.aoid = '';
40 | ret_obj.ref_id = '';
41 | ret_obj.position = '';
42 | ret_obj.rid = (aspace_version === 1)? $tree.attr("data-root-id"): $obj_form.find("#id").val();
43 | }
44 | }
45 | return ret_obj;
46 | }
47 |
48 | /* adds the spreadsheet load button in AS V1.* */
49 | var add_bulk_button = function() {
50 | var $tmpBtn = $("#bulk-ingest");
51 | if ($tmpBtn.length == 1) {
52 | // alert("we got it already!");
53 | }
54 | else {
55 | var $next = $('.btn.add-child');
56 | if ($next.length == 1) {
57 | $next.parent().append(bulk_btn_str);
58 | // alert("created!");
59 | }
60 | $("#bulk-ingest").on('click', function() {
61 | file_modal_html = '';
62 | fileSelection();
63 | });
64 | }
65 | }
66 |
67 | var initExcelFileUploadSection = function() {
68 | var handleExcelFileChange = function() {
69 | var $input = $(this);
70 | var filename = $input.val().split("\\").reverse()[0];
71 | $("#excel_filename").html(filename);
72 | };
73 | $("#excel_file").on("change", handleExcelFileChange);
74 |
75 | };
76 |
77 | /* submit the file for processing */
78 | var handleFileUpload = function($modal) {
79 | /* don't let the modal disappear on submission */
80 | $modal.on("hide.bs.modal", function (event){
81 | event.preventDefault();
82 | event.stopImmediatePropagation();
83 | });
84 | /* submit via ajax */
85 | $form = $("#bulk_ingest_form");
86 | rid = $form.find("#rid").val();
87 | /* I do this because ajaxSubmit doesn't like the URL property? */
88 | $form.attr("action", APP_PATH + "resources/" + rid + "/ssload");
89 | $form.ajaxSubmit({
90 | type: "POST",
91 | beforeSubmit: function(arr, $form, options) {
92 | var names = "";
93 | var hasFile = false;
94 | var missingFile = 'You have not added a file';
95 | for (var i=0; i < arr.length; i++) {
96 | if (arr[i].type === "file") {
97 | fileObj = arr[i].value;
98 | if (typeof(fileObj) === "object") {
99 | if (typeof(fileObj.type) !== "undefined" && (fileObj.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || fileObj.name.endsWith(".xlsx"))) {
100 | hasFile = true;
101 | }
102 | else {
103 | missingFile = 'The file you have chosen is not an Excel Spreadsheet';
104 | }
105 | }
106 | }
107 | }
108 | if (!hasFile) {
109 | alert(missingFile);
110 | $(".bulkbtn").removeClass("disabled");
111 | return false;
112 | }
113 | $(".bulkbtn").addClass('disabled');
114 | return true;
115 | },
116 | /* uploadProgress: function(event, position, total, percentComplete) {
117 | var percentVal = percentComplete + '%';
118 | console.log("Percent: " + percentVal);
119 | }, */
120 | success: function(data, status, xhr) {
121 | /*display? */
122 | alert("The file has been processed");
123 | $("#bulk_messages").html(data);
124 | modalSuccess($file_form_modal);
125 | },
126 | error: function(xhr, status, err) {
127 | alert("ERROR: " + status + "; Error detected");
128 | $("#bulk_messages").html(xhr.responseText);
129 | /* console.log(xhr);
130 | console.log(err); */
131 | /* display error */
132 | modalError($file_form_modal);
133 | }
134 | });
135 | $modal.on("hidden.bs.modal", function (event){
136 | /*console.log("hide hit"); */
137 | $modal.hide();
138 | $("body").css("overflow", "auto");
139 | });
140 | }
141 |
142 |
143 | /* link switching in the tree in AS v1.* means we have to do some initializing */
144 | $(document).on('treesingleselected.aspace', function() {
145 | add_bulk_button();
146 | file_modal_html = '';
147 | });
148 |
149 |
150 |
151 | var openFileModal = function() {
152 | $file_form_modal = AS.openCustomModal("bulkIngestFileModal", "Load Spreadsheet", file_modal_html, 'large', null, $("#bulkFileButton").get(0));
153 | initExcelFileUploadSection();
154 | $("#bulkFileButton").on("click", function(event) {
155 | event.stopPropagation();
156 | event.preventDefault();
157 | handleFileUpload($file_form_modal);
158 | });
159 | var clipboard = new Clipboard('.clip-btn');
160 | clipboard.on('success', function(e) {
161 | /* console.log('Action:', e.action);
162 | console.log('Text:', e.text);
163 | console.log('Trigger:', e.trigger); */
164 | alert('Copied!');
165 | });
166 |
167 | clipboard.on('error', function(e) {
168 | console.error('Action:', e.action);
169 | console.error('Trigger:', e.trigger);
170 | alert("Unable to copy");
171 | });
172 |
173 | $file_form_modal.show();
174 | }
175 | var modalError = function($modal) {
176 | $(".bulkbtn").removeClass("disabled");
177 | $(".bulkbtn.btn-cancel").text("Close").removeClass("disabled").addClass("close")
178 | $(".clip-btn").removeClass("disabled");
179 | $modal.find(".close").click(function(event) {
180 | $("input").each(function() {
181 | /*console.log($(this).val()); */
182 | $(this).val("");
183 | });
184 | $("#bulk_messages").html("");
185 | $("#excel_filename").html("");
186 | $modal.hide();
187 | $("body").css("overflow", "auto");
188 | });
189 | }
190 |
191 | var modalSuccess = function($modal) {
192 | $(".bulkbtn.btn-cancel").text("Close").removeClass("disabled").addClass("close")
193 | $(".clip-btn").removeClass("disabled");
194 | $modal.find(".close").click(function(event) {
195 | window.location.reload(true);
196 | });
197 | }
198 |
199 | var toggleTreeSpinner = function(){
200 | $(".archives-tree-container .spinner").toggle();
201 | }
202 |
203 |
204 | $(document).on('loadedrecordform.aspace', function () {
205 | /* adding the button to the tree on the resource page */
206 | add_bulk_button();
207 | });
208 |
209 |
210 | var fileSelection = function() {
211 | toggleTreeSpinner();
212 | obj = get_object_info();
213 | if ($.isEmptyObject(obj)) {
214 | toggleTreeSpinner();
215 | return;
216 | }
217 | file_modal_html = '';
218 | if (typeof($file_form_modal) !== 'undefined') {
219 | /* console.log("Remove"); */
220 | $file_form_modal.remove();
221 | }
222 | /*console.log("we got rid: " + obj.rid + " " + obj.aoid + " ref_id: " + obj.ref_id + " resource: " + obj.resource + " position: " + obj.position); */
223 | $.ajax({
224 | url: APP_PATH + "resources/" + obj.rid + "/getfile",
225 | type: "POST",
226 | data: {aoid: obj.aoid, type: obj.type, ref_id: obj.ref_id, resource: obj.resource, position: obj.position},
227 | dataType: "html",
228 | success: function(data) {
229 | file_modal_html = data;
230 | openFileModal();
231 | },
232 | error: function(xhr,status,err) {
233 | alert("ERROR: " + status + " " + err);
234 | }
235 | });
236 | toggleTreeSpinner();
237 | };
238 |
239 | var bulkbtnArr = {
240 | label: 'Load via Spreadsheet',
241 | cssClasses: 'btn-default',
242 | onClick: function(event, btn, node, tree, toolbarRenderer) {
243 | fileSelection();
244 | },
245 | isEnabled: function(node, tree, toolbarRenderer) {
246 | return true;
247 | },
248 | isVisible: function(node, tree, toolbarRenderer) {
249 | return !tree.large_tree.read_only;
250 | },
251 | onFormLoaded: function(btn, form, tree, toolbarRenderer) {
252 | $(btn).removeClass('disabled');
253 | },
254 | onToolbarRendered: function(btn, toolbarRenderer) {
255 | $(btn).addClass('disabled');
256 | },
257 | }
258 |
259 | if (aspace_version !== 1) {
260 | var res = TreeToolbarConfiguration["resource"];
261 | TreeToolbarConfiguration["resource"] = [].concat(res).concat([bulkbtnArr]);
262 | var arch = [];
263 | var new_val;
264 | $.each(TreeToolbarConfiguration["archival_object"], function(index,value) {
265 | if ($.type(value) !== "array") {
266 | new_val = value ;
267 | }
268 | else {
269 | new_val = value;
270 | $.each(value,function(i, v){
271 | if (typeof(v['label']) !== 'undefined' && v['label'] === 'Add Child') {
272 | new_val = [].concat(value).concat([bulkbtnArr]);
273 | }
274 | });
275 | }
276 | arch.push(new_val);
277 |
278 | });
279 | TreeToolbarConfiguration["archival_object"] = arch;
280 | }
281 | });
282 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2015 The President and Fellows of Harvard College
2 |
3 | Apache License
4 | Version 2.0, January 2004
5 | http://www.apache.org/licenses/
6 |
7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8 |
9 | 1. Definitions.
10 |
11 | "License" shall mean the terms and conditions for use, reproduction,
12 | and distribution as defined by Sections 1 through 9 of this document.
13 |
14 | "Licensor" shall mean the copyright owner or entity authorized by
15 | the copyright owner that is granting the License.
16 |
17 | "Legal Entity" shall mean the union of the acting entity and all
18 | other entities that control, are controlled by, or are under common
19 | control with that entity. For the purposes of this definition,
20 | "control" means (i) the power, direct or indirect, to cause the
21 | direction or management of such entity, whether by contract or
22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
23 | outstanding shares, or (iii) beneficial ownership of such entity.
24 |
25 | "You" (or "Your") shall mean an individual or Legal Entity
26 | exercising permissions granted by this License.
27 |
28 | "Source" form shall mean the preferred form for making modifications,
29 | including but not limited to software source code, documentation
30 | source, and configuration files.
31 |
32 | "Object" form shall mean any form resulting from mechanical
33 | transformation or translation of a Source form, including but
34 | not limited to compiled object code, generated documentation,
35 | and conversions to other media types.
36 |
37 | "Work" shall mean the work of authorship, whether in Source or
38 | Object form, made available under the License, as indicated by a
39 | copyright notice that is included in or attached to the work
40 | (an example is provided in the Appendix below).
41 |
42 | "Derivative Works" shall mean any work, whether in Source or Object
43 | form, that is based on (or derived from) the Work and for which the
44 | editorial revisions, annotations, elaborations, or other modifications
45 | represent, as a whole, an original work of authorship. For the purposes
46 | of this License, Derivative Works shall not include works that remain
47 | separable from, or merely link (or bind by name) to the interfaces of,
48 | the Work and Derivative Works thereof.
49 |
50 | "Contribution" shall mean any work of authorship, including
51 | the original version of the Work and any modifications or additions
52 | to that Work or Derivative Works thereof, that is intentionally
53 | submitted to Licensor for inclusion in the Work by the copyright owner
54 | or by an individual or Legal Entity authorized to submit on behalf of
55 | the copyright owner. For the purposes of this definition, "submitted"
56 | means any form of electronic, verbal, or written communication sent
57 | to the Licensor or its representatives, including but not limited to
58 | communication on electronic mailing lists, source code control systems,
59 | and issue tracking systems that are managed by, or on behalf of, the
60 | Licensor for the purpose of discussing and improving the Work, but
61 | excluding communication that is conspicuously marked or otherwise
62 | designated in writing by the copyright owner as "Not a Contribution."
63 |
64 | "Contributor" shall mean Licensor and any individual or Legal Entity
65 | on behalf of whom a Contribution has been received by Licensor and
66 | subsequently incorporated within the Work.
67 |
68 | 2. Grant of Copyright License. Subject to the terms and conditions of
69 | this License, each Contributor hereby grants to You a perpetual,
70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71 | copyright license to reproduce, prepare Derivative Works of,
72 | publicly display, publicly perform, sublicense, and distribute the
73 | Work and such Derivative Works in Source or Object form.
74 |
75 | 3. Grant of Patent License. Subject to the terms and conditions of
76 | this License, each Contributor hereby grants to You a perpetual,
77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78 | (except as stated in this section) patent license to make, have made,
79 | use, offer to sell, sell, import, and otherwise transfer the Work,
80 | where such license applies only to those patent claims licensable
81 | by such Contributor that are necessarily infringed by their
82 | Contribution(s) alone or by combination of their Contribution(s)
83 | with the Work to which such Contribution(s) was submitted. If You
84 | institute patent litigation against any entity (including a
85 | cross-claim or counterclaim in a lawsuit) alleging that the Work
86 | or a Contribution incorporated within the Work constitutes direct
87 | or contributory patent infringement, then any patent licenses
88 | granted to You under this License for that Work shall terminate
89 | as of the date such litigation is filed.
90 |
91 | 4. Redistribution. You may reproduce and distribute copies of the
92 | Work or Derivative Works thereof in any medium, with or without
93 | modifications, and in Source or Object form, provided that You
94 | meet the following conditions:
95 |
96 | (a) You must give any other recipients of the Work or
97 | Derivative Works a copy of this License; and
98 |
99 | (b) You must cause any modified files to carry prominent notices
100 | stating that You changed the files; and
101 |
102 | (c) You must retain, in the Source form of any Derivative Works
103 | that You distribute, all copyright, patent, trademark, and
104 | attribution notices from the Source form of the Work,
105 | excluding those notices that do not pertain to any part of
106 | the Derivative Works; and
107 |
108 | (d) If the Work includes a "NOTICE" text file as part of its
109 | distribution, then any Derivative Works that You distribute must
110 | include a readable copy of the attribution notices contained
111 | within such NOTICE file, excluding those notices that do not
112 | pertain to any part of the Derivative Works, in at least one
113 | of the following places: within a NOTICE text file distributed
114 | as part of the Derivative Works; within the Source form or
115 | documentation, if provided along with the Derivative Works; or,
116 | within a display generated by the Derivative Works, if and
117 | wherever such third-party notices normally appear. The contents
118 | of the NOTICE file are for informational purposes only and
119 | do not modify the License. You may add Your own attribution
120 | notices within Derivative Works that You distribute, alongside
121 | or as an addendum to the NOTICE text from the Work, provided
122 | that such additional attribution notices cannot be construed
123 | as modifying the License.
124 |
125 | You may add Your own copyright statement to Your modifications and
126 | may provide additional or different license terms and conditions
127 | for use, reproduction, or distribution of Your modifications, or
128 | for any such Derivative Works as a whole, provided Your use,
129 | reproduction, and distribution of the Work otherwise complies with
130 | the conditions stated in this License.
131 |
132 | 5. Submission of Contributions. Unless You explicitly state otherwise,
133 | any Contribution intentionally submitted for inclusion in the Work
134 | by You to the Licensor shall be under the terms and conditions of
135 | this License, without any additional terms or conditions.
136 | Notwithstanding the above, nothing herein shall supersede or modify
137 | the terms of any separate license agreement you may have executed
138 | with Licensor regarding such Contributions.
139 |
140 | 6. Trademarks. This License does not grant permission to use the trade
141 | names, trademarks, service marks, or product names of the Licensor,
142 | except as required for reasonable and customary use in describing the
143 | origin of the Work and reproducing the content of the NOTICE file.
144 |
145 | 7. Disclaimer of Warranty. Unless required by applicable law or
146 | agreed to in writing, Licensor provides the Work (and each
147 | Contributor provides its Contributions) on an "AS IS" BASIS,
148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149 | implied, including, without limitation, any warranties or conditions
150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151 | PARTICULAR PURPOSE. You are solely responsible for determining the
152 | appropriateness of using or redistributing the Work and assume any
153 | risks associated with Your exercise of permissions under this License.
154 |
155 | 8. Limitation of Liability. In no event and under no legal theory,
156 | whether in tort (including negligence), contract, or otherwise,
157 | unless required by applicable law (such as deliberate and grossly
158 | negligent acts) or agreed to in writing, shall any Contributor be
159 | liable to You for damages, including any direct, indirect, special,
160 | incidental, or consequential damages of any character arising as a
161 | result of this License or out of the use or inability to use the
162 | Work (including but not limited to damages for loss of goodwill,
163 | work stoppage, computer failure or malfunction, or any and all
164 | other commercial damages or losses), even if such Contributor
165 | has been advised of the possibility of such damages.
166 |
167 | 9. Accepting Warranty or Additional Liability. While redistributing
168 | the Work or Derivative Works thereof, You may choose to offer,
169 | and charge a fee for, acceptance of support, warranty, indemnity,
170 | or other liability obligations and/or rights consistent with this
171 | License. However, in accepting such obligations, You may act only
172 | on Your own behalf and on Your sole responsibility, not on behalf
173 | of any other Contributor, and only if You agree to indemnify,
174 | defend, and hold each Contributor harmless for any liability
175 | incurred by, or claims asserted against, such Contributor by reason
176 | of your accepting any such warranty or additional liability.
177 |
178 | END OF TERMS AND CONDITIONS
179 |
180 | APPENDIX: How to apply the Apache License to your work.
181 |
182 | To apply the Apache License to your work, attach the following
183 | boilerplate notice, with the fields enclosed by brackets "{}"
184 | replaced with your own identifying information. (Don't include
185 | the brackets!) The text should be enclosed in the appropriate
186 | comment syntax for the file format. We also recommend that a
187 | file or class name and description of purpose be included on the
188 | same "printed page" as the copyright notice for easier
189 | identification within third-party archives.
190 |
191 | Copyright {yyyy} {name of copyright owner}
192 |
193 | Licensed under the Apache License, Version 2.0 (the "License");
194 | you may not use this file except in compliance with the License.
195 | You may obtain a copy of the License at
196 |
197 | http://www.apache.org/licenses/LICENSE-2.0
198 |
199 | Unless required by applicable law or agreed to in writing, software
200 | distributed under the License is distributed on an "AS IS" BASIS,
201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
202 | See the License for the specific language governing permissions and
203 | limitations under the License.
204 |
205 |
--------------------------------------------------------------------------------
/user_documentation/archival_objects_instructions.md:
--------------------------------------------------------------------------------
1 | # Import Archival Objects
2 |
3 | ## Using the Template to Create a Spreadsheet
4 |
5 | **aspace-import-excel v3.0** introduces an [expanded Excel Spreadsheet template](../templates/extended_aspace_import_excel_template.xlsx) with new functionality for importing Archival Objects.
6 |
7 | The new functionality consists of support for:
8 |
9 | * Individually setting the publish/unpublish flags for Notes.
10 | * Ability to add Agents as Source and Subject, not just Creator.
11 | * Expanded the number of Agents for each type, including directions for adding even more agents.
12 |
13 | * Support for more than one Extent, with the ability to add more extents.
14 | * Support for more than one Container Instance, with the ability to add more container instances.
15 |
16 | The code is backward-compatible with the the original [Excel Spreadsheet template](../templates/aspace_import_excel_template.xlsx) so you may continue using the original if it meets your needs.
17 |
18 | Once you've opened your chosen template, use **Save as** *(your new filename}*.xlsx to begin filling in your spreadsheet.
19 |
20 |
21 | The template is designed to be flexible enough to accommodate different workflows. The *first row* is the place where you can put identifying information, such as "Foo Collection".
22 |
23 | As long as you **don't edit** the **row** marked *"ArchivesSpace field code"*, you may hide, delete, or rearrange **columns** to suit your workflow. Indeed, you will see that there are a few already-hidden columns; these are not currently used, but may be used in future enhancements. **_DO NOT_** hide required columns.
24 |
25 | **Note** that some columns already have in-column drop down data validation defined. You may of course add more of these, or edit the ones that are already defined. See [The Excel help page](https://support.office.com/en-us/article/Apply-data-validation-to-cells-29FECBCC-D1B9-42C1-9D76-EFF3CE5F7249) to learn how to create these.
26 |
27 | Column Definitions \| Dates \| Extent \| Container \| Digital Objects \| Agents \| Subjects \| Notes
28 |
29 | ### Required Columns
30 |
31 | There are very few columns that _must_ be filled in:
32 |
33 | * **EAD ID** - of the resource to which you're adding Archival Objects. This will be used to confirm that you are trying to add your spreadsheet information to the correct resource.
34 | * The **Hierarchical Relationship** of the new Archival Object to the selected resource or selected Archival Object: If you've selected a Resource, **1** indicates that this is the first level of Archival Objects. If you have selected an Archival Object, use **1** if you're adding a sibling to a selected Archival Object, **2** if a child, etc. You can therefore describe several levels of Archival Objects in a single spreadsheet.
35 | * **The Description Level** This is an in-column drop-down.
36 | * EITHER the **Title** OR a **valid Date** having at least a begin date or a date expression.
37 |
38 | ## Column Definitions
39 |
40 | Below is a discussion of each used column in the spreadsheet.
41 |
42 | For columns where the value is from a Controlled Value List, you can fill in either the controlled list's **Value** *or* the **Translation**. It must be entered **exactly** as it is written (lower case, title case, etc.). As an example (for English), in the *Extent Extent Type* controlled list, "cubic feet" is represented as the **value** `cubic_feet` or the **translation** `Cubic Feet`. Entering `cubic feet` would result in an error message.
43 |
44 | **Notes:**
45 | 1. The application compares the input first against the **Translation**, then, failing that, against the **Value**.
46 | 2. In the case that your list has more than one entry with the same **Translation**, the **Value** for the first (lowest position) entry is used. A **WARN** message will appear in the frontend log file when this application encounters this situation.
47 |
48 |
49 | Column | Value | Default | Comment
50 | -------|-------|---------|---------
51 | EAD ID | String | | **REQUIRED**
52 | Title | String| |Title of the Archival Object; required if no Creation Date information
53 | Component Unit Identifier| String | |
54 | Hierarchical Relationship| Number | | **REQUIRED**
55 | Description Level| in column drop-down || **REQUIRED** *from the Archival Record Level controlled value list*
56 | Other Level| String | *unspecified*| This is used if *Other Level* was specified in the **Description Level**
57 | Publish?| in column drop-down | **False** | This is applied to any information (such as subject, note) created with this Archival Object
58 | Restrictions Apply? | in column drop-down | **False** |
59 | Processing Note | String | | No markup allowed
60 |
61 | Column Definitions \| Dates \| Extent \| Container \| Digital Objects \| Agents \| Subjects \| Notes
62 |
63 | ### Dates
64 |
65 | New in version 3.0: Support for more than one Date. The spreadsheet provides for two dates; you can add more by following the instructions for adding additional dates.
66 |
67 | A Date must have **a valid label** and **at least** either a *begin date* or a *date expression.*
68 |
69 | **NOTE:** The cell format for cells containing values for *Date Begin* and *Date End* **MUST** be **Text**, not some date format like `yyyy-mm-dd`, if you don't want the hours, minutes, seconds appended (e.g.: *1969-17-17T00:00:00+00.00*). Some versions of Excel will "helpfully" convert the cell to a date format if you are not watching.
70 |
71 | Column | Value | Default | Comment
72 | -------|-------|---------|---------
73 | Dates Label | String | *creation* | from the *Date Label* controlled value list. **Note**: If the value given is *not* on the controlled value list, this date will not be processed.
74 | Date Begin | a Date string || in one of the following: **YYYY, YYYY-MM, or YYYY-MM-DD**
75 | Date End | a Date string || in one of the following: **YYYY, YYYY-MM, or YYYY-MM-DD** **Note**: If you choose a Date Type of *'single'*, any value in this column will be ignored.
76 | Date Type | String| *inclusive*| from the *Date Type* controlled value list. **Note**: If the given value is *not* on the controlled value list, it will be overridden with the value 'inclusive'.
77 | Date Expression |String||
78 | Date Certainty |String | | from the *Date Certainty* controlled value list
79 |
80 | ### Adding more dates to the spreadsheet
81 |
82 | New in version 3.0:
83 | The plugin supports your adding more than the two dates supplied on the spreadsheet. To do this, you may edit, locally, the [extended_aspace_import_excel_template.xlsx](../templates/extended_aspace_import_excel_template.xlsx) by copying the set of columns for the second date, inserting them into the template, and editing the labels in Rows 4 and 5 to reflect the next integer number:
84 | * insert 6 columns to the RIGHT of second date block
85 | * copy the six columns of the second date, then paste them into the blank columns
86 | * edit the labels in Row 4 to increment the number. For example, for the first added date, you'd edit **dates_label_2** to **dates_label_3** . **NOTE**: it is *extremely important* that you ensure that the labels in Row 4 are edited; otherwise, you may not get the results you're expecting.
87 | * While not necessary for proper processing, it's recommended that you also update the numbers in the copied columns in Row 5 to avoid confusion. For example, edit **Date (2) Label** to **Date (3) Label**.
88 |
89 | Column Definitions \| Dates \| Extent \| Container \| Digital Objects \| Agents \| Subjects \| Notes
90 |
91 | ### Extent Information
92 |
93 | New in version 3.0: Support for more than one extent. The spreadsheet provides for two extents; you can add more by following the instructions for adding additional extents.
94 |
95 | Extent information is not required, but if you are defining an extent, please note the required fields.
96 |
97 | Column | Value | Default | Comment
98 | -------|-------|---------|---------
99 | Extent portion | String| whole| from the *Extent Portion* controlled value list
100 | Extent number | Number||**REQUIRED**
101 | Extent type | String| |**REQUIRED** from the *Extent Extent Type* controlled value list
102 | Container Summary|String||
103 | Physical details |String||
104 | Dimensions| String ||
105 |
106 | ### Adding more extents to the spreadsheet
107 |
108 | New in version 3.0:
109 | The plugin supports your adding more than the two extents supplied on the spreadsheet. To do this, you may edit, locally, the [extended_aspace_import_excel_template.xlsx](../templates/extended_aspace_import_excel_template.xlsx) by copying the set of columns for the second extent, inserting them into the template, and editing the labels in Rows 4 and 5 to reflect the next integer number:
110 | * insert 6 columns to the RIGHT of second extent block
111 | * copy the six columns of the second extent, then paste them into the blank columns
112 | * edit the labels in Row 4 to increment the number. For example, for the first added extent, you'd edit **portion_2** to **portion_3** . **NOTE**: it is *extremely important* that you ensure that the labels in Row 4 are edited; otherwise, you may not get the results you're expecting.
113 | * While not necessary for proper processing, it's recommended that you also update the numbers in the copied columns in Row 5 to avoid confusion. For example, edit **Extent Portion(2)** to **Extent Portion(3)**. *
114 |
115 | Column Definitions \| Dates \| Extent \| Container \| Digital Objects \| Agents \| Subjects \| Notes
116 |
117 | ### Container Information - Creating a Container Instance
118 |
119 | New in version 3.0: Support for more than one container instance. The spreadsheet provides for two container instances; you can add more by following the instructions for adding additional instances.
120 |
121 | A Container instance associates the Archival Object with a Top Container, with additional information on Child and Grandchild sub-containers if present.
122 |
123 | The ingester will try to find an already-created Top Container in the database.
124 | + If you have defined a barcode:
125 | + If there's a match for the barcode for that resource, that Top Container will be used without further checking.
126 | + Otherwise, a new Top Container will be created.
127 | + If you have not defined a barcode:
128 | + The type and indicator will be used to search the database for a Top Container that is already associated with the resource;
129 | + Otherwise, a new Top Container will be created.
130 |
131 |
132 | **NOTE:** if you want the object in this spreadsheet to be in a Top Container shared with *another Resource*, you must either specify the container by *barcode* or else make sure that at least one archival object in the spreadsheet's Resource with that container has already been created via the usual ArchivesSpace interface.
133 |
134 | If you are specifying container information, note that both **type** and **indicator** are required for each level (top, child, and grandchild) you want to specify.
135 |
136 |
137 | Column | Value | Default | Comment
138 | -------|-------|---------|---------
139 | Container Instance type| String | | **REQUIRED** if you are defining a Container Instance. Value from the *Instance Instance Type* controlled value list
140 | Top Container indicator|String | Unknown || **REQUIRED**
141 | Barcode|String|||
142 | Child type | String||from the *Container Type* controlled value list
143 | Child indicator|String |Unknown || *only used if a Child type is specified*
144 | Grandchild type | String||from the *Container Type* controlled value list
145 | Grandchild indicator|String | Unknown || *only used if a Grandchild type is specified*
146 |
147 | ### Adding more container instances to the spreadsheet
148 |
149 | New in version 3.0:
150 | The plugin supports your adding more than the two container instances supplied on the spreadsheet. To do this, you may edit, locally, the [extended_aspace_import_excel_template.xlsx](../templates/extended_aspace_import_excel_template.xlsx) by copying the set of columns for the second extent, inserting them into the template, and editing the labels in Rows 4 and 5 to reflect the next integer number:
151 | * insert 8 columns to the LEFT of second container block
152 | * copy the 8 columns of the second container block, then paste them into the blank columns
153 | * edit the labels in Row 4 to increment the number. For example, for the first added container instance, you'd edit **cont_instance_type_2** to **cont_instance_type_3** .
154 | For container instances, there are some Row 4 values with double numbers, such as **type_2_2**, which would be edited to **type_2_3**. Sorry for the confusion!
155 | **NOTE**: it is *extremely important* that you ensure that the labels in Row 4 are edited; otherwise, you may not get the results you're expecting.
156 | * While not necessary for proper processing, it's recommended that you also edit the numbers in the copied columns in Row 5 to avoid confusion. For example, edit **Container Instance Type(2)** to **Container Instance Type(3)**.
157 |
158 | Column Definitions \| Dates \| Extent \| Container \| Digital Objects \| Agents \| Subjects \| Notes
159 |
160 | ### Digital Objects
161 |
162 | Ingest allows you to create a Digital Object, and associate it with the Archival Object. The "publish" state will be whatever the "publish" state of the Archival Object has been defined to be.
163 |
164 | Column | Value | Default | Comment
165 | -------|-------|---------|---------
166 | Digital Object Title| String || If no Digital Object Title is provided, the display header string of the parent Archival Object will be used.
167 | URL of Linked-out digital object| URL String || this becomes the File Version with the **actuate_attribute** set to "onRequest" and the **show_attribute** set to "new"
168 | URL of thumbnail| URL String || if defined, this becomes the File version with the **actuate_attribute** set to "onLoad", the **show_attribute** set to "embed", and the "is representative" flag is set to TRUE.
169 |
170 | Column Definitions \| Dates \| Extent \| Container \| Digital Objects \| Agents \| Subjects \| Notes
171 |
172 |
173 | ### Agent Objects
174 |
175 | The ingester allows you to link Agents to Archival objects. The [extended_aspace_import_excel_template.xlsx](../templates/extended_aspace_import_excel_template.xlsx), as provided, allows for up to **5** Person Agents, up to **2** Family Agents, and up to **3** Corporate Agents per Archival object. If you need more of any of these types, you can follow the directions for adding more agents.
176 |
177 | If you have previously defined the Agent(s) you are using, you may use the Record ID number (e.g.: for the Agent URI /agents */agent_person/1249*, you would use **1249**) OR the full header string, with all capitalization and punctuation.
178 |
179 | Either the Record ID *or* the header string is **required**.
180 |
181 | If you include both, or only the header, and the record isn't found, a new Agent record will be created. The header string will be used as the **family_name** if it's a Family Agent, and the **primary_name**
182 | otherwise.
183 |
184 | If you enter the header string *without* the ID, the ingester will try to do an **exact match** against the header; if it finds more than one match (for example, if the database contains two agents with identical headers, but different sources):
185 |
186 | * The ingester will create a **new** agent (with publish=false) containing the header with ' DISAMBIGUATE ME!' appended to it. For example, given a person agent with a header of 'George Washington', a new person agent would be created with a primary name of 'George Washington DISAMBIGUATE ME!'.
187 | * After ingest, you can use the *merge* functionality to resolve the ambiguities.
188 |
189 | If you enter a Record ID and **not** the header string, and that ID is not found, a new Agent record will be created with the name "PLACEHOLDER FOR *{agent type}* ID *{ id number}* NOT FOUND", so that you may easily find that record later and edit/merge it. In this case, the new Agent would be marked publish=false. When you correct the record, change publish to true if appropriate.
190 |
191 |
192 |
193 | If you **only** enter the header string, and a record isn't found in the database, a new Agent will be created, with its Linked Agent Role of **Creator**.
194 |
195 |
196 |
197 |
198 | #### Person agents:
199 |
200 | Column | Value | Default | Comment
201 | -------|-------|---------|---------
202 | Agent (1) Record ID | Number||
203 | Agent (1) header string |String|| must be the entire header, including punctuation & capitalization
204 | Agent Role(1)|String|Creator|New in v3.0: from the *Linked Agent Role* controlled value list.
205 | Agent (1) Relator|String|| If supplying relator, term must be from the *Linked Agent Archival Record Relators* controlled value list. The default list provided by ArchivesSpace maps to the [MARC Relator Code and Term List](http://www.loc.gov/marc/relators/relaterm.html).
206 | Agent (2) Record ID | Number||
207 | Agent (2) header string |String|| must be the entire header, including punctuation & capitalization
208 | Agent Role(2)|String|Creator|New in v3.0: from the *Linked Agent Role* controlled value list.
209 | Agent (2) Relator|String|| If supplying relator, term must be from the *Linked Agent Archival Record Relators* controlled value list.
210 | Agent (3) Record ID | Number||
211 | Agent (3) header string |String|| must be the entire header, including punctuation & capitalization
212 | Agent Role(3)|String|Creator|New in v3.0: from the *Linked Agent Role* controlled value list.
213 | Agent (3) Relator|String|| If supplying relator, term must be from the *Linked Agent Archival Record Relators* controlled value list.
214 |
215 | #### Family Agents:
216 | Column | Value | Default | Comment
217 | -------|-------|---------|---------
218 | Family Agent Record ID | Number||
219 | Family Agent header string |String|| must be the entire header, including punctuation & capitalization
220 | Family Agent Role|String|Creator|New in v3.0: from the *Linked Agent Role* controlled value list.
221 | Family Agent Relator|String|| If supplying relator, term must be from the *Linked Agent Archival Record Relators* controlled value list.
222 |
223 | #### Corporate Agents:
224 | Column | Value | Default | Comment
225 | -------|-------|---------|---------
226 | Corporate Agent Record ID | Number||
227 | Corporate Agent header string |String|| must be the entire header, including punctuation & capitalization
228 | Corporate Agent Role|String|Creator|New in v3.0: from the *Linked Agent Role* controlled value list.
229 | Corporate Agent Relator|string|| If supplying relator, term must be from the *Linked Agent Archival Record Relators* controlled value list.
230 | Corporate Agent Record ID (2) | Number||
231 | Corporate Agent header string (2) |String|| must be the entire header, including punctuation & capitalization
232 | Corporate Agent Role(2)|String|Creator|New in v3.0: from the *Linked Agent Role* controlled value list.
233 | Corporate Agent Relator (2)|String|| If supplying relator, term must be from the *Linked Agent Archival Record Relators* controlled value list.
234 |
235 | ### Adding more agents to the spreadsheet
236 |
237 | The plugin supports your associating with an Archival Object even more agents of each type. To do this, you may edit, locally, the [extended_aspace_import_excel_template.xlsx](../templates/extended_aspace_import_excel_template.xlsx) by copying the last set of columns of the particular type, inserting them into the template, and editing the labels in Rows 4 and 5 to reflect the next integer number.
238 |
239 | For example, if you were to want *3* Family Agents, you would:
240 | * insert four blank columns next to the second Family Agent columns
241 | * copy the four columns of the second Family Agent, and paste them into the blank columns
242 | * edit the labels in Row 4, incrementing the number. For example, you would edit the label **families_agent_record_id_2** in the _copied_ column to **families_agent_record_id_3**. **NOTE**: it is *extremely important* that you ensure that the labels in Row 4 are edited; otherwise, you may not get the results you're expecting.
243 | * While not necessary for proper processing, it's recommended that you also update the numbers in Row 5, to avoid confusion. For example, you would edit the label **Family Agent(2) header string** to **Family Agent(3) header string**
244 |
245 |
246 | **Note:** The plugin stops at the first set of columns that are blank. This means that, if you've filled in the columns for Person Agent 1, and Person Agent 3, leaving Person Agent 2 blank, the plugin *will not*
247 | process Person Agent 3.
248 |
249 | Column Definitions \| Dates \| Extent \| Container \| Digital Objects \| Agents \| Subjects \| Notes
250 |
251 | ### Subjects
252 |
253 | As with Agents, you may associate Subjects with the Archival Object. You may associate up to two Subject records. If you know the Record ID, you may use that instead of the **term**, **type**, and **source** in a manner similar to the way that Agent specifications are made, with the same database lookup and handling done there. Again, if you want the ingest to look up the **term** in the database, you must use the entire Subject header, including any punctuation or capitalization.
254 |
255 | If you enter the subject header string *without* the ID, the ingester will try to do an **exact match** against the header; if it finds more than one match (for example, if the database contains two subjects with identical headers, but different sources):
256 |
257 | * The ingester will create a **new** subject (with publish=false) containing the header with ' DISAMBIGUATE ME!' appended to it. For example, given a subject with a header of 'Black Lives Matter', a new subject would be created with the header 'Black Lives Matter DISAMBIGUATE ME!'.
258 | * After ingest, you can use the *merge* functionality to resolve the ambiguities.
259 |
260 | Column | Value | Default | Comment
261 | -------|-------|---------|---------
262 | Subject (1) Record ID|Number||
263 | Subject (1) Term |String ||
264 | Subject (1) Type | String| topical| from the *Subject Term Type* controlled value list
265 | Subject (1) Source | String| ingest| from the *Subject Source* controlled value list
266 | Subject (2) Record ID|Number||
267 | Subject (2) Term |String ||
268 | Subject (2) Type | String| topical| from the *Subject Term Type* controlled value list
269 | Subject (2) Source | String| ingest| from the *Subject Source* controlled value list
270 |
271 | Column Definitions \| Dates \| Extent \| Container \| Digital Objects \| Agents \| Subjects \| Notes
272 |
273 |
274 | ### Notes fields
275 |
276 | You may specify a variety of notes fields.
277 |
278 | If the note type allows for subfields, what you specify will be put in the first subfield.
279 |
280 | New in version 3.0:
281 | Each Note column is accompanied by a "Publish" column, which has in-column drop down data validation (TRUE/FALSE). The publish flag will be set for that note (and any associated subnote) as follows:
282 | * if the field is left blank, use the value of the Publish field for that Archival Object
283 | * Otherwise, set to True or False as specified.
284 |
285 |
286 | As does ArchivesSpace, you may used Mixed Content (EAD/XML markup). The Ingester will check to make sure that the entry is "well formed" -- that is, that the opening and closing elements match -- but will **not** validate the text to make sure you're using the proper markup.
287 |
288 | The following Notes fields are supported:
289 |
290 | + Abstract
291 | + Access Restrictions
292 | + Acquisition Information
293 | + Arrangement
294 | + Biography/History
295 | + Custodial History
296 | + Dimensions
297 | + General
298 | + Language of Materials
299 | + Physical Description
300 | + Physical Facet
301 | + Physical Location
302 | + Preferred Citation
303 | + Processing Information
304 | + Related Materials
305 | + Scope and Contents
306 | + Separated Materials
307 | + Use Restrictions
308 |
309 | Column Definitions \| Dates \| Extent \| Container \| Digital Objects \| Agents \| Subjects \| Notes
310 |
--------------------------------------------------------------------------------
/frontend/assets/clipboard.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * clipboard.js v1.6.1
3 | * https://zenorocha.github.io/clipboard.js
4 | *
5 | * Licensed MIT © Zeno Rocha
6 | */
7 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Clipboard = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o