hyrax-preservation 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +5 -0
  4. data/Rakefile +15 -0
  5. data/app/assets/config/preservation_manifest.js +2 -0
  6. data/app/assets/javascripts/hyrax/preservation/application.js +13 -0
  7. data/app/assets/stylesheets/hyrax/preservation/application.css +15 -0
  8. data/app/controllers/hyrax/preservation/events_controller.rb +133 -0
  9. data/app/helpers/hyrax/preservation/application_helper.rb +6 -0
  10. data/app/indexers/hyrax/preservation/event_indexer.rb +73 -0
  11. data/app/jobs/hyrax/preservation/application_job.rb +6 -0
  12. data/app/mailers/hyrax/preservation/application_mailer.rb +8 -0
  13. data/app/models/hyrax/preservation/event.rb +41 -0
  14. data/app/models/hyrax/preservation/premis_event_type.rb +64 -0
  15. data/app/presenters/hyrax/preservation/event_index_presenter.rb +12 -0
  16. data/app/presenters/hyrax/preservation/event_show_presenter.rb +11 -0
  17. data/app/search_builders/hyrax/preservation/events_search_builder.rb +105 -0
  18. data/app/views/hyrax/preservation/events/_event.html.erb +1 -0
  19. data/app/views/hyrax/preservation/events/_filter_search.html.erb +6 -0
  20. data/app/views/hyrax/preservation/events/_index_header_list_default.html.erb +20 -0
  21. data/app/views/hyrax/preservation/events/_previous_next_doc.html.erb +29 -0
  22. data/app/views/hyrax/preservation/events/filter_search/_date_range.html.erb +18 -0
  23. data/app/views/hyrax/preservation/events/filter_search/_premis_agent.html.erb +15 -0
  24. data/app/views/hyrax/preservation/events/filter_search/_premis_event_type.html.erb +18 -0
  25. data/app/views/hyrax/preservation/events/index.html.erb +10 -0
  26. data/app/views/layouts/hyrax/preservation/application.html.erb +14 -0
  27. data/config/routes.rb +3 -0
  28. data/lib/generators/hyrax/preservation/install_generator.rb +21 -0
  29. data/lib/hyrax/preservation.rb +12 -0
  30. data/lib/hyrax/preservation/demo.rb +80 -0
  31. data/lib/hyrax/preservation/engine.rb +13 -0
  32. data/lib/hyrax/preservation/event_logger.rb +18 -0
  33. data/lib/hyrax/preservation/search_state.rb +9 -0
  34. data/lib/hyrax/preservation/service_environment.rb +65 -0
  35. data/lib/hyrax/preservation/version.rb +5 -0
  36. metadata +307 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8b640e48b5f2c1c7671aafe3cedbfa3c9eddd13e47a0b42aa710e814845bcbf6
4
+ data.tar.gz: a0046c6f6ad64f5b2ec63feb340c74699073e4d1bc47802d0ede9d59c572bbe9
5
+ SHA512:
6
+ metadata.gz: d19413cfa69f75cb3e03b8c7bd83615c55e2c6859ec6f6911099a7c9851f6b2b7f094235bd11d2a71cc28e09c232f807638730485b170e51be14cdd2fb3f0abd
7
+ data.tar.gz: 4453203e60f1de3df9360fad1dbbdabba447e0c82197cd256b5bb40f35c5ad08cde371fd124d093d8b701bd9b6b30eeb6a531621682c410a33798e6163e279ae
@@ -0,0 +1,20 @@
1
+ Copyright 2016 Andrew Myers
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,5 @@
1
+ ![Travis Build Status](https://travis-ci.org/WGBH/hydradam-preservation.svg?branch=master)
2
+
3
+ # Hydradam Preservation
4
+
5
+ Gem for adding preservation functionality to an existing Hydra head.
@@ -0,0 +1,15 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'bundler/gem_tasks'
8
+ load 'rails/tasks/statistics.rake'
9
+ require 'engine_cart/rake_task'
10
+
11
+ # Load tasks for gem development
12
+ Dir.glob('gem_development_rake_tasks/*.rake').each { |r| import r }
13
+
14
+ # Set the default rake task when developing this gem.
15
+ task :default => ['ci']
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/preservation .js
2
+ //= link_directory ../stylesheets/preservation .css
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,133 @@
1
+ require 'hyrax/preservation/search_state'
2
+
3
+ module Hyrax
4
+ module Preservation
5
+ class EventsController < ActionController::Base
6
+ include Blacklight::Controller
7
+ include Hydra::Controller::ControllerBehavior
8
+ include Hydra::Catalog
9
+ include Hyrax::Controller
10
+
11
+ # Include Blacklight helpers that are needed to avoid breakage when using
12
+ # Blacklight views.
13
+ helper CatalogHelper
14
+ helper ComponentHelper
15
+ helper LayoutHelper
16
+
17
+ # Include Hyrax helpers that are needed to avoid breakage when rendering
18
+ # Hyrax views.
19
+ helper HyraxHelper
20
+
21
+ # Override Hyrax::UrlHelper#url_for_document
22
+ # helper_method :url_for_document
23
+ helper_method :display_premis_agent
24
+ helper_method :display_premis_event_date_time
25
+ helper_method :display_related_file
26
+
27
+ # TODO: We used to include CurationConcerns::ApplicationControllerBehavior
28
+ # here, but that module was merged into Sufia::Controller, which was later
29
+ # converted to Hyrax::Controller. But we're not sure if it's still needed.
30
+ # TODO: Determine whether or not we need to include Hyrax::Controller, and
31
+ # upadate (or delete) these commets accordingly.
32
+ # include Hyrax::Controller
33
+ include Hyrax::ThemedLayoutController
34
+ with_themed_layout '1_column'
35
+
36
+ # Prevent CSRF attacks by raising an exception.
37
+ # For APIs, you may want to use :null_session instead.
38
+ protect_from_forgery with: :exception
39
+
40
+ # Override rails path for the views by appending 'catalog' as a
41
+ # place to look for views. This allows using default blacklight
42
+ # views if you don't want to override each one.
43
+ def _prefixes
44
+ @_prefixes ||= super + ['catalog']
45
+ end
46
+
47
+ configure_blacklight do |config|
48
+
49
+ config.search_builder_class = EventsSearchBuilder
50
+
51
+ # Index view config
52
+ config.index.document_presenter_class = EventIndexPresenter
53
+ # NOTE: setting `config.index.title_field` here has no effect, because
54
+ # we're currently overriding the presenter class, and the partial that
55
+ # would normally be looking for it. Instead, see the overridden partial
56
+ # in app/views/preservation/events/_index_header_list_default.html.erb
57
+ # and the EventIndexPresenter#search_result_title method.
58
+ config.add_index_field solr_name(:hasEventRelatedObject, :symbol), label: "File", helper_method: :display_related_file
59
+ config.add_index_field solr_name(:premis_event_date_time, :stored_searchable, type: :date), label: "Date", helper_method: :display_premis_event_date_time
60
+ config.add_index_field solr_name(:premis_agent, :symbol), label: "Agent", helper_method: :display_premis_agent
61
+
62
+ # Show view config
63
+ config.show.document_presenter_class = EventShowPresenter
64
+ config.add_show_field solr_name(:premis_agent, :symbol), label: "PREMIS Agent"
65
+ config.add_show_field solr_name(:premis_event_date_time, :stored_searchable, type: :date), label: "Date"
66
+ config.add_show_field solr_name(:hasEventRelatedObject, :symbol), label: "File", helper_method: :display_related_file
67
+
68
+ # Remove unused actions from the show view. Enabling these breaks the
69
+ # show view because Blacklight generates urls that don't exist. Figuring
70
+ # out how to make them work will take more digging.
71
+ config.show.document_actions.delete(:bookmark)
72
+ config.show.document_actions.delete(:email)
73
+ config.show.document_actions.delete(:sms)
74
+ config.show.document_actions.delete(:citation)
75
+
76
+ # Facet config
77
+ # config.add_facet_fields_to_solr_request!
78
+ # config.add_facet_field :premis_event_date_time_ltsi, label: 'Date', range: { segments: false }
79
+ # config.add_facet_field solr_name(:premis_event_type, :symbol), label: 'Type'
80
+ end
81
+
82
+ # Overrides CatalogController::UrlHelper#url_for_document. It would be
83
+ # nice to put this method in our own Preservation::UrlHelper module but I
84
+ # couldn't get the helper to load after Hyrax::UrlHelper in
85
+ # order to overwrite the #url_for_document method. NOTE: In any event,
86
+ # this method needs to behave roughly the same way as
87
+ # CurationCocerns::UrlHelper#url_for_document, so if that method changes
88
+ # change this one accordingly.
89
+ # def url_for_document(doc, _options = {})
90
+ # polymorphic_path([preservation, doc])
91
+ # end
92
+
93
+ def search_state
94
+ @search_state ||= Hyrax::Preservation::SearchState.new(params, blacklight_config, self)
95
+ end
96
+
97
+ def display_premis_agent(opts={})
98
+ solr_doc = opts[:document]
99
+ premis_agent_mailto_uri = solr_doc[opts[:field]]
100
+ premis_agent_mailto_uri.first.sub(/^mailto\:/, '')
101
+ end
102
+
103
+ def display_premis_event_date_time(opts={})
104
+ solr_doc = opts[:document]
105
+ premis_event_date_time = solr_doc[opts[:field]]
106
+ Date.parse(premis_event_date_time.to_s).strftime('%Y-%m-%d')
107
+ end
108
+
109
+ def display_related_file(opts={})
110
+ # TODO: Is there a better way than having the controller send back HTML?
111
+ # TODO: Is there a better way to fetch the FileSet ID and Title? This way is confusing.
112
+ solr_doc = opts[:document]
113
+ file_set_id = solr_doc[:hasEventRelatedObject_ssim].first
114
+ file_set_url = Rails.application.routes.url_helpers.hyrax_file_set_path(id: file_set_id)
115
+
116
+ # TODO: this is totally klugey. We're pulling back the FileSet object from Fedora in order to get
117
+ # a good name to display it with. Originally, we were getting this value from the Solr document of
118
+ # the PreservationEvent object, but we ran into an issue where the PreservationEventIndexer does
119
+ # not know which FileSet property has been populated with a good, representative name for the FileSet
120
+ # at the time of indexing, because that is determined by configuration within the host app in any one of:
121
+ # 1. the FileSet model
122
+ # 1. the ingest configuration of the FileSet if using hyrax-ingest.
123
+ # 1. the FileSet indexer
124
+ # So either this value needs to be configurable within the host app,
125
+ # or the PreservationEventIndexer (in the hyrax-preservation gem) needs to have similar fallback logic
126
+ # when indexing a representative name for the FileSet.
127
+ file_set = ::FileSet.find(file_set_id)
128
+ file_set_title = file_set&.title&.first || file_set&.label || file_set&.filename&.first
129
+ "<a href='#{file_set_url}'>#{file_set_title}</a>".html_safe
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,6 @@
1
+ module Hyrax
2
+ module Preservation
3
+ module ApplicationHelper
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,73 @@
1
+ module Hyrax
2
+ module Preservation
3
+ class EventIndexer < ActiveFedora::IndexingService
4
+ def generate_solr_document
5
+ super.tap do |solr_doc|
6
+ # TODO: Some of the conditionals checking for existence of properties
7
+ # may be removed after require those fields with validations on the
8
+ # model.
9
+
10
+ if object.premis_event_related_object
11
+ Solrizer.set_field(solr_doc,
12
+ 'related_file_title',
13
+ object.premis_event_related_object.label,
14
+ :stored_searchable)
15
+ end
16
+
17
+ unless object.premis_event_type.empty?
18
+ # Index the PREMIS event type.
19
+ # NOTE: the value we index is only the last URI segment, which is a
20
+ # 3-letter abbreviation.
21
+ Solrizer.set_field(solr_doc,
22
+ 'premis_event_type',
23
+ shortened_field_val = URI(object.premis_event_type.first.id).path.split('/').last,
24
+ :symbol)
25
+ end
26
+
27
+ unless object.premis_agent.empty?
28
+ # Index the PREMIS agent, under the assumption that is a mailto: URI.
29
+ # NOTE: The value we index is only the amil address, without the "mailto:" part.
30
+ Solrizer.set_field(solr_doc,
31
+ 'premis_agent',
32
+ object.premis_agent.first.id.sub(/^mailto:/, ''),
33
+ :symbol)
34
+ end
35
+
36
+
37
+ # If the PreservationEvent's agent is found in the db,
38
+ # then index that user's full name as well.
39
+ # TODO: Not sure if this is a good idea.
40
+ # How much metadata about the agent should we index for PreservationEvents?
41
+ # if object.premis_agent_db_user
42
+ # Solrizer.set_field(solr_doc,
43
+ # 'premis_agent_full_name',
44
+ # object.premis_agent_db_user.first.id,
45
+ # :stored_searchable)
46
+ # end
47
+
48
+ unless object.premis_event_date_time.empty?
49
+ # Index the PREMIS event date time as a date.
50
+ Solrizer.set_field(solr_doc,
51
+ 'premis_event_date_time',
52
+ object.premis_event_date_time.first,
53
+ :stored_searchable)
54
+
55
+ # Index the PREMIS event date time as an integer, for range queries.
56
+ Solrizer.set_field(solr_doc,
57
+ 'premis_event_date_time_integer',
58
+ object.premis_event_date_time.first.strftime('%Y%m%d').to_i,
59
+ Solrizer::Descriptor.new(:long, :stored, :indexed))
60
+ end
61
+
62
+ unless object.premis_event_outcome.empty?
63
+ # Index the PREMIS event outcome.
64
+ Solrizer.set_field(solr_doc,
65
+ 'premis_event_outcome',
66
+ object.premis_event_outcome.first,
67
+ :stored_searchable)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,6 @@
1
+ module Hyrax
2
+ module Preservation
3
+ class ApplicationJob < ActiveJob::Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ module Hyrax
2
+ module Preservation
3
+ class ApplicationMailer < ActionMailer::Base
4
+ default from: 'from@example.com'
5
+ layout 'mailer'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,41 @@
1
+ module Hyrax
2
+ module Preservation
3
+ class Event < ActiveFedora::Base
4
+ type ::RDF::Vocab::PREMIS.Event
5
+
6
+ belongs_to :premis_event_related_object, predicate: ::RDF::Vocab::PREMIS.hasEventRelatedObject, class_name: ::FileSet
7
+
8
+ property :premis_event_type, predicate: ::RDF::Vocab::PREMIS.hasEventType
9
+ property :premis_agent, predicate: ::RDF::Vocab::PREMIS.hasAgent
10
+ property :premis_event_date_time, predicate: ::RDF::Vocab::PREMIS.hasEventDateTime
11
+ property :premis_event_outcome, predicate: ::RDF::Vocab::PREMIS.hasEventOutcome
12
+ property :premis_event_detail, predicate: ::RDF::Vocab::PREMIS.hasEventDetail
13
+
14
+ def user_from_db
15
+ @user_from_db ||= User.where(email: premis_agent)
16
+ end
17
+
18
+ # @return [String] the label of the PREMIS event type that corresponds
19
+ # with the URI from the first value of the #premis_event_type property.
20
+ def premis_event_type_label
21
+ premis_event_type_object.label
22
+ end
23
+
24
+ def premis_event_type_abbr
25
+ premis_event_type_object.abbr
26
+ end
27
+
28
+ def self.indexer
29
+ EventIndexer
30
+ end
31
+
32
+ private
33
+ # @return [Preservation::PremisEventType] first PremisEventType instance
34
+ # found where Preservation::PremisEventType#uri matches first value
35
+ # found in #premis_event_type property.
36
+ def premis_event_type_object
37
+ @premis_event_type_object ||= Preservation::PremisEventType.find_by_uri(premis_event_type.first.id)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,64 @@
1
+ # A basic PORO model for PREMIS event types.
2
+ #
3
+ # The data for this model is not persisted to any data store. It is just
4
+ # hardcoded (see Preservation::PremisEventyType.all).
5
+ #
6
+ # While only the URI of a PREMIS event type is stored on the
7
+ # Preservation::Event#premis_event_type property, this PORO is used to pair
8
+ # the URI with an abbreviation and a label.
9
+ module Hyrax
10
+ module Preservation
11
+ class PremisEventType
12
+ attr_reader :abbr, :label
13
+
14
+ def initialize(abbr, label='')
15
+ @abbr = abbr
16
+ @label = label
17
+ end
18
+
19
+ def uri
20
+ ::RDF::Vocab::PremisEventType.send(abbr)
21
+ end
22
+
23
+ # @return [Array] list of all available PremisEventType instances.
24
+ def self.all
25
+ @all ||= [
26
+ new('cap', 'PREMIS Capture'),
27
+ new('com', 'PREMIS Compression'),
28
+ new('cre', 'PREMIS Creation'),
29
+ new('dea', 'PREMIS Deaccession'),
30
+ new('dec', 'PREMIS Decryption'),
31
+ new('del', 'PREMIS Deletion'),
32
+ new('dig', 'PREMIS Digital Signature Validation'),
33
+ new('fix', 'PREMIS Fixity Check'),
34
+ new('ing', 'PREMIS Ingestion'),
35
+ new('mes', 'PREMIS Message Digest Calculation'),
36
+ new('mig', 'PREMIS Migration'),
37
+ new('nor', 'PREMIS Normalization'),
38
+ new('rep', 'PREMIS Replication'),
39
+ new('val', 'PREMIS Validation'),
40
+ new('vir', 'PREMIS Virus Check')
41
+ ]
42
+ end
43
+
44
+ # @param [String] URI to use for comparison. Note #to_s is called on the
45
+ # param before comparing.
46
+ # @return [Preservation::PremisEventType] first found instance with URI
47
+ # that matches parameter.
48
+ def self.find_by_uri(uri)
49
+ result = all.find { |record| record.uri.to_s == uri.to_s }
50
+ raise NotFound.new("PremisEventType with URI \"#{uri.to_s}\" was not found") unless result
51
+ result
52
+ end
53
+
54
+ def self.find_by_abbr(abbr)
55
+ result = all.find { |record| record.abbr == abbr }
56
+ raise NotFound.new("PremisEventType with abbreviation \"#{abbr}\" was not found") unless result
57
+ result
58
+ end
59
+
60
+ # Custom Error Classes
61
+ class NotFound < StandardError; end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,12 @@
1
+ module Hyrax
2
+ module Preservation
3
+ class EventIndexPresenter < Blacklight::IndexPresenter
4
+ # Returns the value of PremisEventType#label for the PremisEventType instance
5
+ # whose #abbr value matches that which is in the solr document.
6
+ def search_result_title
7
+ premis_event_type = PremisEventType.all.find { |premis_event_type| premis_event_type.abbr == document.first(:premis_event_type_ssim) }
8
+ premis_event_type.label
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ module Hyrax
2
+ module Preservation
3
+ class EventShowPresenter < Blacklight::ShowPresenter
4
+ def heading
5
+ premis_event_type_abbr = document[Solrizer.solr_name(:premis_event_type, :symbol)].first
6
+ premis_event_type = Preservation::PremisEventType.all.find { |premis_event_type| premis_event_type.abbr == premis_event_type_abbr }
7
+ premis_event_type.label
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,105 @@
1
+ module Hyrax
2
+ module Preservation
3
+ class EventsSearchBuilder < Blacklight::SearchBuilder
4
+ include Blacklight::Solr::SearchBuilderBehavior
5
+
6
+ self.default_processor_chain += [:only_models_for_preservation_events, :apply_premis_event_date_time_filter,
7
+ :apply_premis_event_type_filter, :apply_premis_agent_filter,
8
+ :apply_premis_event_related_object_filter]
9
+
10
+
11
+ def only_models_for_preservation_events(solr_params)
12
+ solr_params[:fq] ||= []
13
+ solr_params[:fq] << "{!terms f=has_model_ssim}Hyrax::Preservation::Event"
14
+ end
15
+
16
+ def apply_premis_event_date_time_filter(solr_params)
17
+ if premis_event_date_time_filter
18
+ solr_params[:fq] ||= []
19
+ solr_params[:fq] << premis_event_date_time_filter
20
+ end
21
+ solr_params
22
+ end
23
+
24
+ def apply_premis_event_type_filter(solr_params)
25
+ if premis_event_type_filter
26
+ solr_params[:fq] ||= []
27
+ solr_params[:fq] << premis_event_type_filter
28
+ end
29
+ solr_params
30
+ end
31
+
32
+ def apply_premis_agent_filter(solr_params)
33
+ if premis_agent_filter
34
+ solr_params[:fq] ||= []
35
+ solr_params[:fq] << premis_agent_filter
36
+ end
37
+ end
38
+
39
+ def apply_premis_event_related_object_filter(solr_params)
40
+ if premis_event_related_object_filter
41
+ solr_params[:fq] ||= []
42
+ solr_params[:fq] << premis_event_related_object_filter
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ # Returns a date/time range for a Solr query for the 'after' and 'before'
49
+ # URL params.
50
+ def premis_event_date_time_filter
51
+ @premis_event_date_time_filter ||= begin
52
+ if premis_event_date_time_before || premis_event_date_time_after
53
+ range = "#{premis_event_date_time_after || '*'} TO #{premis_event_date_time_before || '*'}"
54
+ "premis_event_date_time_dtsim:[#{range}]"
55
+ end
56
+ end
57
+ end
58
+
59
+ # Returns the 'before' date time formatted for a Solr query.
60
+ def premis_event_date_time_before
61
+ @premis_event_date_time_before ||= formatted_premis_event_date_time(blacklight_params['before'])
62
+ end
63
+
64
+ # Returns the 'after' date time formatted for a Solr query.
65
+ def premis_event_date_time_after
66
+ @premis_event_date_time_after ||= formatted_premis_event_date_time(blacklight_params['after'])
67
+ end
68
+
69
+ # Converts an unformatted date (as passed in via URL) to a date formatted
70
+ # for a Solr query.
71
+ def formatted_premis_event_date_time(unformatted_date)
72
+ DateTime.parse(unformatted_date.to_s).utc.strftime("%Y-%m-%dT%H:%M:%SZ")
73
+ rescue ArgumentError => e
74
+ nil
75
+ end
76
+
77
+ def premis_event_type_filter
78
+ @premis_event_type_filter ||= begin
79
+ if blacklight_params['premis_event_type']
80
+ valid_premis_abbrs = blacklight_params['premis_event_type'] & Hyrax::Preservation::PremisEventType.all.map(&:abbr)
81
+ "(#{valid_premis_abbrs.map { |abbr| "premis_event_type_ssim:#{abbr}"}.join(" OR ")})"
82
+ end
83
+ end
84
+ end
85
+
86
+ def premis_agent_filter
87
+ @premis_agent_filter ||= begin
88
+ if blacklight_params['agent'].present?
89
+ # TODO: sanitize URL parameters. Do not simply trust user input.
90
+ "premis_agent_ssim:#{blacklight_params['agent']}"
91
+ end
92
+ end
93
+ end
94
+
95
+ def premis_event_related_object_filter
96
+ @premis_event_related_object_filter ||= begin
97
+ if blacklight_params['related_object'].present?
98
+ # TODO: sanitize URL parameters. Do not simply trust user input.
99
+ "hasEventRelatedObject_ssim:#{blacklight_params['related_object']}"
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1 @@
1
+ <%= render 'catalog/document', document: event, document_counter: event_counter %>
@@ -0,0 +1,6 @@
1
+ <div>
2
+ <%= link_to "Clear All Filters", hyrax_preservation.events_url %>
3
+ </div>
4
+ <%= render 'hyrax/preservation/events/filter_search/date_range' %>
5
+ <%= render 'hyrax/preservation/events/filter_search/premis_event_type' %>
6
+ <%= render 'hyrax/preservation/events/filter_search/premis_agent' %>
@@ -0,0 +1,20 @@
1
+ <%
2
+ # NOTE: This overrides Hyrax's
3
+ # app/views/catalog/_index_header_list_default.html.erb which (at the time of
4
+ # this writing) only calls `document.title_or_label`, where `document` is a
5
+ # `SolrDocument` instance. We need to use a presenter object, because we use a
6
+ # small bit of logic to return the title of a search result. Hyrax will
7
+ # probably be upgraded to use a presenter in it's own version of this view
8
+ # partial in the near future, but until then, we override it here.
9
+
10
+ # Furthermore, since we are using our own custom presenter, and our own custom
11
+ # view partial, configuring the title field using `config.index.title_file` in
12
+ # the EventsController's `configure_blacklight` block has no effect here. Instead,
13
+ # we're just overriding the default index presenter, giving it a special
14
+ # method, and calling that special method from this overridden partial.
15
+
16
+ # Confused yet? Yeah, me too.
17
+ %>
18
+ <h2>
19
+ <%= link_to index_presenter(document).search_result_title, document %>
20
+ </h2>
@@ -0,0 +1,29 @@
1
+ <%
2
+ # This is an override of a partial from Blacklight 6.7.3
3
+ # in app/views/catalog/_previous_next_doc.html.erb.
4
+ # See inline comment below for details of why the override is needed.
5
+ %>
6
+
7
+ <% #Using the Bootstrap Pagination class -%>
8
+ <% #DEPRECATED - using id="previousNextDocument" as a selector is deprecated and will be removed in Blacklight 6.0 %>
9
+ <div id='previousNextDocument' class='pagination-search-widgets'>
10
+ <% if @previous_document || @next_document %>
11
+ <div class="page_links">
12
+ <%= link_to_previous_document @previous_document %> |
13
+
14
+ <%= item_page_entry_info %> |
15
+
16
+ <%= link_to_next_document @next_document %>
17
+ </div>
18
+ <% end %>
19
+ <% if current_search_session %>
20
+ <div class="pull-right search-widgets">
21
+ <%
22
+ # Calling #link_back_to_catalog here results in a routing error: no route matches "preservatio/catalog"
23
+ # Filed a detailed bug: https://github.com/projectblacklight/blacklight/issues/1647
24
+ # In the meantime, just skip the link back to the catalog.
25
+ %>
26
+ <%=link_to t('blacklight.search.start_over'), start_over_path(current_search_session.try(:query_params) || {}), id: 'startOverLink', class: 'btn' %>
27
+ </div>
28
+ <% end %>
29
+ </div>
@@ -0,0 +1,18 @@
1
+ <h2>Date</h2>
2
+ <form id="date_time_range_filter" action="<%= hyrax_preservation.events_url %>">
3
+ <label>From</label>
4
+ <input name="after" value="<%= params['after'] %>" />
5
+ <label>To</label>
6
+ <input name="before" value="<%= params['before'] %>" />
7
+
8
+ <!-- TODO: This logic needs to be replicated for all filter forms. Better in a helper method? -->
9
+ <% Rack::Utils.parse_query(request.query_string).except("after", "before").each do |param, val| %>
10
+ <% # Sometimes `val` may be an array, but not always. So we just array-ify
11
+ # it here to cover both cases. %>
12
+ <% Array(val).each do |single_val| %>
13
+ <input type="hidden" name="<%= param %>" value="<%= single_val %>" />
14
+ <% end %>
15
+ <% end %>
16
+
17
+ <button type="submit">Submit</button>
18
+ </form>
@@ -0,0 +1,15 @@
1
+ <h2>PREMIS Agent</h2>
2
+ <form id="premis_agent_filter" action="<%= hyrax_preservation.events_url %>">
3
+ <label for="agent">Agent</label>
4
+ <input id="agent" name="agent" value="<%= params['agent'] %>" />
5
+ <!-- TODO: This logic needs to be replicated for all filter forms. Better in a helper method? -->
6
+ <% Rack::Utils.parse_query(request.query_string).except("agent").each do |param, val| %>
7
+ <% # Sometimes `val` may be an array, but not always. So we just array-ify
8
+ # it here to cover both cases. %>
9
+ <% Array(val).each do |single_val| %>
10
+ <input type="hidden" name="<%= param %>" value="<%= single_val %>" />
11
+ <% end %>
12
+ <% end %>
13
+
14
+ <button type="submit">Submit</button>
15
+ </form>
@@ -0,0 +1,18 @@
1
+ <h2>PREMIS Event Type</h2>
2
+ <form id="premis_event_type_filter" action="<%= hyrax_preservation.events_url %>">
3
+ <% Hyrax::Preservation::PremisEventType.all.each do |premis_event_type| %>
4
+ <input type="checkbox" id="premis_event_type_<%= premis_event_type.abbr %>" name="premis_event_type[]" value="<%= premis_event_type.abbr %>" <%= Array(request.params['premis_event_type']).include?(premis_event_type.abbr) ? 'checked="checked"' : '' %>/>
5
+ <label for="premis_event_type_<%= premis_event_type.abbr %>"><%= premis_event_type.label.sub(/^PREMIS/ , '') %></label>
6
+ <% end %>
7
+
8
+ <!-- TODO: This logic needs to be replicated for all filter forms. Better in a helper method? -->
9
+ <% Rack::Utils.parse_query(request.query_string).except("premis_event_type[]").each do |param, val| %>
10
+ <% # Sometimes `val` may be an array, but not always. So we just array-ify
11
+ # it here to cover both cases. %>
12
+ <% Array(val).each do |single_val| %>
13
+ <input type="hidden" name="<%= param %>" value="<%= single_val %>" />
14
+ <% end %>
15
+ <% end %>
16
+
17
+ <button type="submit">Submit</button>
18
+ </form>
@@ -0,0 +1,10 @@
1
+ <% provide :page_title, "#{application_name} Search Results" %>
2
+ <% provide :page_class, 'search' %>
3
+
4
+ <% provide :sidebar do %>
5
+ <%= render 'filter_search' %>
6
+ <% end %>
7
+
8
+ <div id="content">
9
+ <%= render 'search_results' %>
10
+ </div>
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Preservation</title>
5
+ <%= stylesheet_link_tag "preservation/application", media: "all" %>
6
+ <%= javascript_include_tag "preservation/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,3 @@
1
+ Hyrax::Preservation::Engine.routes.draw do
2
+ resources :events, only: [:index, :show]
3
+ end
@@ -0,0 +1,21 @@
1
+ module Hyrax
2
+ module Preservation
3
+ class InstallGenerator < Rails::Generators::Base
4
+
5
+ desc <<-EOS
6
+ This generator makes the following changes to your application:
7
+ 1. Adds Preservation routes to your ./config/routes.rb
8
+ EOS
9
+
10
+ # def inject_search_builder_behavior
11
+ # inject_into_file 'app/models/search_builder.rb', after: "include Hyrax::SearchFilters\n" do
12
+ # "\tinclude Hydradam::SearchBuilderBehavior\n"
13
+ # end
14
+ # end
15
+
16
+ def add_routes
17
+ route "mount Hyrax::Preservation::Engine, at: '/preservation'"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ if defined? Rails
2
+ require "hyrax/preservation/engine"
3
+ require 'hyrax'
4
+ end
5
+
6
+ module Hyrax
7
+ module Preservation
8
+ def self.root
9
+ Pathname.new(File.expand_path('../../../', __FILE__))
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,80 @@
1
+ require_relative 'event_logger'
2
+
3
+
4
+ module Hyrax
5
+ module Preservation
6
+ class Demo
7
+ def self.clean_slate!
8
+ delete_all_file_sets!
9
+ delete_all_preservation_events!
10
+ delete_all_hydra_access_controls!
11
+ end
12
+
13
+ def self.delete_all_file_sets!
14
+ FileSet.all.each { |fs| fs.delete }
15
+ end
16
+
17
+ def self.delete_all_preservation_events!
18
+ Preservation::Event.all.each { |pe| pe.delete }
19
+ end
20
+
21
+ def self.delete_all_hydra_access_controls!
22
+ ::Hydra::AccessControl.all.each { |ac| ac.delete }
23
+ end
24
+
25
+ def self.new_file_set
26
+ FileSet.new.tap do |fs|
27
+ fs.apply_depositor_metadata(User.first)
28
+ fs.save!
29
+ end
30
+ end
31
+
32
+ def self.run_once!
33
+ file_set = new_file_set
34
+
35
+ # NOTE: PreservationEventLogger is a stateless service object that provides a "use anywhere" interface
36
+ # for logging preservation events. We don't have to use this pattern, but it has it's advantages.
37
+
38
+ # Create two sets of people for some realistic randomness in the demo records.
39
+ preservation_people = ['jlhardes@iu.edu', 'heidowdi@indiana.edu']
40
+ ingest_people = ['akhedkar@iu.edu', 'afredmyers@gmail.com']
41
+
42
+
43
+ # Create a fake capture date of sometime between now and 90 days ago.
44
+ capture_date = DateTime.now - rand(2..90).days
45
+ # Create a fake fixity check date sometime between the capture date and now.
46
+ fixity_date = rand(capture_date..DateTime.now)
47
+ # Create a fake ingest date of sometime beetween the capture date and now.
48
+ ingest_date = rand(capture_date..DateTime.now)
49
+
50
+ # Create an event for the file's capture.
51
+ Preservation::EventLogger.log_preservation_event(
52
+ file_set: file_set,
53
+ premis_event_type: 'cap',
54
+ premis_agent: preservation_people.sample,
55
+ premis_event_date_time: capture_date
56
+ )
57
+
58
+ # Create an event for the file's fixity check.
59
+ Preservation::EventLogger.log_preservation_event(
60
+ file_set: file_set,
61
+ premis_event_type: 'fix',
62
+ premis_agent: preservation_people.sample,
63
+ premis_event_date_time: fixity_date
64
+ )
65
+
66
+ # Create an event for the file's ingestion.
67
+ Preservation::EventLogger.log_preservation_event(
68
+ file_set: file_set,
69
+ premis_event_type: 'ing',
70
+ premis_agent: ingest_people.sample,
71
+ premis_event_date_time: ingest_date
72
+ )
73
+ end
74
+
75
+ def self.run!(iterations=1)
76
+ iterations.times { self.run_once! }
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,13 @@
1
+ module Hyrax::Preservation
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Hyrax::Preservation
4
+
5
+ config.autoload_paths += %W(
6
+ #{config.root}/app/helpers
7
+ #{config.root}/app/indexers
8
+ #{config.root}/app/presenters
9
+ #{config.root}/app/search_builders
10
+ #{config.root}/lib/preservation
11
+ )
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ # TODO: Only use this as-is for Demo. If you want to use it as a service object
2
+ # it should be moved to a different location probably.
3
+ module Hyrax
4
+ module Preservation
5
+ class EventLogger
6
+ def self.log_preservation_event(opts={})
7
+ Preservation::Event.new.tap do |pe|
8
+ pe.premis_event_type += [ Preservation::PremisEventType.all.select { |premis_event_type| premis_event_type.abbr == opts[:premis_event_type].to_sym } ]
9
+ pe.premis_event_related_object = opts[:file_set]
10
+ # Assume opts[:premis_agent] is an email address, and make a 'mailto:' RDF::URI out of it.
11
+ pe.premis_agent += [::RDF::URI.new("mailto:#{opts[:premis_agent]}")]
12
+ pe.premis_event_date_time += [opts[:premis_event_date_time]]
13
+ pe.save!
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ module Hyrax
2
+ module Preservation
3
+ class SearchState < Hyrax::SearchState
4
+ def url_for_document(doc, options = {})
5
+ [controller.hyrax_preservation, doc]
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,65 @@
1
+ require 'hyrax/preservation'
2
+ require 'solr_wrapper'
3
+ require 'fcrepo_wrapper'
4
+ require 'active_support/core_ext/hash/keys'
5
+
6
+ module Hyrax
7
+ module Preservation
8
+ class ServiceEnvironment
9
+ attr_reader :env
10
+
11
+ def initialize(env)
12
+ @env = (env || 'development').to_s
13
+ end
14
+
15
+ def start
16
+ wrap do
17
+ begin
18
+ puts "\nServices started. Ctrl+C to stop.\n\n"
19
+ sleep
20
+ rescue Interrupt
21
+ # TODO: Why doesn't this message get printed?
22
+ puts "Stopping services..."
23
+ end
24
+ end
25
+ end
26
+
27
+ def wrap
28
+ SolrWrapper.wrap(solr_wrapper_config) do |solr_wrapper_instance|
29
+ # If a solr collection exists, but is not configured to persist,
30
+ # then we must delete it before we try to re-create it. Otherwise
31
+ # SolrWrapper with throw an error. Note this is kind of an awkward
32
+ # way to check for existence of the core, but it's due to current
33
+ # limitation of the SolrWrapper interfaces.
34
+ if !solr_wrapper_config[:collection][:persist] && SolrWrapper::Client.new(solr_wrapper_instance.url).exists?(solr_wrapper_config[:collection][:name])
35
+ solr_wrapper_instance.delete solr_wrapper_config[:collection][:name]
36
+ end
37
+
38
+ solr_wrapper_instance.with_collection(solr_wrapper_config[:collection]) do
39
+ FcrepoWrapper.wrap(fcrepo_wrapper_config) do
40
+ yield
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def solr_wrapper_config
49
+ @solr_wrapper_config ||= YAML.load(File.read(solr_wrapper_config_path)).deep_symbolize_keys
50
+ end
51
+
52
+ def solr_wrapper_config_path
53
+ File.join(Hyrax::Preservation.root, ".solr_wrapper.#{env}.yml")
54
+ end
55
+
56
+ def fcrepo_wrapper_config
57
+ @fcrepo_wrapper_config ||= YAML.load(File.read(fcrepo_wrapper_config_path)).deep_symbolize_keys
58
+ end
59
+
60
+ def fcrepo_wrapper_config_path
61
+ File.join(Hyrax::Preservation.root, ".fcrepo_wrapper.#{env}.yml")
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,5 @@
1
+ module Hyrax
2
+ module Preservation
3
+ VERSION = '0.1.1'
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,307 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hyrax-preservation
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Myers
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-03-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hyrax
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.0.4
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.4
33
+ - !ruby/object:Gem::Dependency
34
+ name: jquery-ui-rails
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '5.0'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 5.0.5
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '5.0'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 5.0.5
53
+ - !ruby/object:Gem::Dependency
54
+ name: sqlite3
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.3'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.3.13
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.3'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.3.13
73
+ - !ruby/object:Gem::Dependency
74
+ name: rspec-rails
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '3.6'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 3.6.1
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.6'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 3.6.1
93
+ - !ruby/object:Gem::Dependency
94
+ name: capybara
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '2.15'
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 2.15.1
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '2.15'
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 2.15.1
113
+ - !ruby/object:Gem::Dependency
114
+ name: solr_wrapper
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: '1.1'
120
+ - - '='
121
+ - !ruby/object:Gem::Version
122
+ version: 1.1.0
123
+ type: :development
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '1.1'
130
+ - - '='
131
+ - !ruby/object:Gem::Version
132
+ version: 1.1.0
133
+ - !ruby/object:Gem::Dependency
134
+ name: fcrepo_wrapper
135
+ requirement: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: '0.8'
140
+ type: :development
141
+ prerelease: false
142
+ version_requirements: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - "~>"
145
+ - !ruby/object:Gem::Version
146
+ version: '0.8'
147
+ - !ruby/object:Gem::Dependency
148
+ name: factory_girl_rails
149
+ requirement: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - "~>"
152
+ - !ruby/object:Gem::Version
153
+ version: '4.8'
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: 4.8.0
157
+ type: :development
158
+ prerelease: false
159
+ version_requirements: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - "~>"
162
+ - !ruby/object:Gem::Version
163
+ version: '4.8'
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: 4.8.0
167
+ - !ruby/object:Gem::Dependency
168
+ name: pry-rails
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '0.3'
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: 0.3.6
177
+ type: :development
178
+ prerelease: false
179
+ version_requirements: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - "~>"
182
+ - !ruby/object:Gem::Version
183
+ version: '0.3'
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: 0.3.6
187
+ - !ruby/object:Gem::Dependency
188
+ name: pry-byebug
189
+ requirement: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - "~>"
192
+ - !ruby/object:Gem::Version
193
+ version: '3.6'
194
+ - - ">="
195
+ - !ruby/object:Gem::Version
196
+ version: 3.6.0
197
+ type: :development
198
+ prerelease: false
199
+ version_requirements: !ruby/object:Gem::Requirement
200
+ requirements:
201
+ - - "~>"
202
+ - !ruby/object:Gem::Version
203
+ version: '3.6'
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ version: 3.6.0
207
+ - !ruby/object:Gem::Dependency
208
+ name: launchy
209
+ requirement: !ruby/object:Gem::Requirement
210
+ requirements:
211
+ - - "~>"
212
+ - !ruby/object:Gem::Version
213
+ version: '2.4'
214
+ - - ">="
215
+ - !ruby/object:Gem::Version
216
+ version: 2.4.3
217
+ type: :development
218
+ prerelease: false
219
+ version_requirements: !ruby/object:Gem::Requirement
220
+ requirements:
221
+ - - "~>"
222
+ - !ruby/object:Gem::Version
223
+ version: '2.4'
224
+ - - ">="
225
+ - !ruby/object:Gem::Version
226
+ version: 2.4.3
227
+ - !ruby/object:Gem::Dependency
228
+ name: rb-readline
229
+ requirement: !ruby/object:Gem::Requirement
230
+ requirements:
231
+ - - "~>"
232
+ - !ruby/object:Gem::Version
233
+ version: '0.5'
234
+ type: :development
235
+ prerelease: false
236
+ version_requirements: !ruby/object:Gem::Requirement
237
+ requirements:
238
+ - - "~>"
239
+ - !ruby/object:Gem::Version
240
+ version: '0.5'
241
+ description: Hyrax Preservation provides models for storing and searching for preservation
242
+ metadata.
243
+ email:
244
+ - afredmyers@gmail.com
245
+ executables: []
246
+ extensions: []
247
+ extra_rdoc_files: []
248
+ files:
249
+ - MIT-LICENSE
250
+ - README.md
251
+ - Rakefile
252
+ - app/assets/config/preservation_manifest.js
253
+ - app/assets/javascripts/hyrax/preservation/application.js
254
+ - app/assets/stylesheets/hyrax/preservation/application.css
255
+ - app/controllers/hyrax/preservation/events_controller.rb
256
+ - app/helpers/hyrax/preservation/application_helper.rb
257
+ - app/indexers/hyrax/preservation/event_indexer.rb
258
+ - app/jobs/hyrax/preservation/application_job.rb
259
+ - app/mailers/hyrax/preservation/application_mailer.rb
260
+ - app/models/hyrax/preservation/event.rb
261
+ - app/models/hyrax/preservation/premis_event_type.rb
262
+ - app/presenters/hyrax/preservation/event_index_presenter.rb
263
+ - app/presenters/hyrax/preservation/event_show_presenter.rb
264
+ - app/search_builders/hyrax/preservation/events_search_builder.rb
265
+ - app/views/hyrax/preservation/events/_event.html.erb
266
+ - app/views/hyrax/preservation/events/_filter_search.html.erb
267
+ - app/views/hyrax/preservation/events/_index_header_list_default.html.erb
268
+ - app/views/hyrax/preservation/events/_previous_next_doc.html.erb
269
+ - app/views/hyrax/preservation/events/filter_search/_date_range.html.erb
270
+ - app/views/hyrax/preservation/events/filter_search/_premis_agent.html.erb
271
+ - app/views/hyrax/preservation/events/filter_search/_premis_event_type.html.erb
272
+ - app/views/hyrax/preservation/events/index.html.erb
273
+ - app/views/layouts/hyrax/preservation/application.html.erb
274
+ - config/routes.rb
275
+ - lib/generators/hyrax/preservation/install_generator.rb
276
+ - lib/hyrax/preservation.rb
277
+ - lib/hyrax/preservation/demo.rb
278
+ - lib/hyrax/preservation/engine.rb
279
+ - lib/hyrax/preservation/event_logger.rb
280
+ - lib/hyrax/preservation/search_state.rb
281
+ - lib/hyrax/preservation/service_environment.rb
282
+ - lib/hyrax/preservation/version.rb
283
+ homepage: https://github.com/IUBLibTech/hyrax-preservation
284
+ licenses:
285
+ - MIT
286
+ metadata: {}
287
+ post_install_message:
288
+ rdoc_options: []
289
+ require_paths:
290
+ - lib
291
+ required_ruby_version: !ruby/object:Gem::Requirement
292
+ requirements:
293
+ - - ">="
294
+ - !ruby/object:Gem::Version
295
+ version: '0'
296
+ required_rubygems_version: !ruby/object:Gem::Requirement
297
+ requirements:
298
+ - - ">="
299
+ - !ruby/object:Gem::Version
300
+ version: '0'
301
+ requirements: []
302
+ rubyforge_project:
303
+ rubygems_version: 2.7.6
304
+ signing_key:
305
+ specification_version: 4
306
+ summary: Preservation features for Hydra
307
+ test_files: []