hyrax-preservation 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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: []