curation_concerns 0.12.0.pre1 → 0.12.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +24 -16
- data/Gemfile +0 -4
- data/README.md +14 -0
- data/RELEASING.md +2 -2
- data/Rakefile +2 -0
- data/app/actors/concerns/curation_concerns/manages_embargoes_actor.rb +28 -0
- data/app/actors/curation_concerns/abstract_actor.rb +28 -0
- data/app/actors/curation_concerns/add_to_collection_actor.rb +38 -0
- data/app/actors/curation_concerns/apply_order_actor.rb +24 -0
- data/app/actors/curation_concerns/assign_identifier_actor.rb +7 -0
- data/app/actors/curation_concerns/assign_representative_actor.rb +18 -0
- data/app/actors/curation_concerns/attach_files_actor.rb +39 -0
- data/app/actors/curation_concerns/base_actor.rb +71 -0
- data/app/actors/curation_concerns/embargo_actor.rb +19 -0
- data/app/actors/curation_concerns/file_actor.rb +79 -0
- data/app/actors/curation_concerns/file_set_actor.rb +146 -0
- data/app/actors/curation_concerns/interpret_visibility_actor.rb +123 -0
- data/app/actors/curation_concerns/lease_actor.rb +19 -0
- data/app/actors/curation_concerns/root_actor.rb +17 -0
- data/app/actors/curation_concerns/work_actor_behavior.rb +8 -0
- data/app/assets/javascripts/curation_concerns/batch_select.js +42 -0
- data/app/assets/javascripts/curation_concerns/collections.js +13 -0
- data/app/assets/javascripts/curation_concerns/curation_concerns.js +2 -0
- data/app/assets/stylesheets/curation_concerns/_curation_concerns.scss +0 -3
- data/app/assets/stylesheets/curation_concerns/_modules.scss +1 -1
- data/app/assets/stylesheets/curation_concerns/_positioning.scss +3 -6
- data/app/assets/stylesheets/curation_concerns/_theme.scss +0 -39
- data/app/assets/stylesheets/curation_concerns/_typography.scss +0 -69
- data/app/assets/stylesheets/curation_concerns/modules/classify_work.scss +0 -2
- data/app/assets/stylesheets/curation_concerns/modules/collections.scss +4 -0
- data/app/assets/stylesheets/curation_concerns/modules/forms.scss +0 -4
- data/app/assets/stylesheets/curation_concerns/modules/site_actions.scss +34 -29
- data/app/assets/stylesheets/curation_concerns/modules/site_search.scss +0 -46
- data/app/assets/stylesheets/curation_concerns.scss +4 -0
- data/app/controllers/concerns/curation_concerns/collections_controller_behavior.rb +166 -21
- data/app/controllers/concerns/curation_concerns/embargoes_controller_behavior.rb +1 -1
- data/app/controllers/concerns/curation_concerns/leases_controller_behavior.rb +1 -1
- data/app/controllers/concerns/curation_concerns/selects_collections.rb +65 -0
- data/app/forms/curation_concerns/forms/collection_edit_form.rb +0 -29
- data/app/forms/curation_concerns/forms/work_form.rb +2 -1
- data/app/helpers/batch_select_helper.rb +23 -0
- data/app/helpers/collections_helper.rb +4 -0
- data/app/helpers/curation_concerns/collections_helper.rb +2 -2
- data/app/helpers/curation_concerns/collections_helper_behavior.rb +56 -0
- data/app/helpers/curation_concerns/render_constraints_helper.rb +14 -35
- data/app/helpers/curation_concerns/title_helper.rb +4 -0
- data/app/indexers/curation_concerns/collection_indexer.rb +16 -0
- data/app/indexers/curation_concerns/file_set_indexer.rb +46 -0
- data/app/indexers/curation_concerns/work_indexer.rb +15 -0
- data/app/jobs/audit_job.rb +49 -0
- data/app/jobs/characterize_job.rb +11 -0
- data/app/jobs/create_derivatives_job.rb +21 -0
- data/app/jobs/import_url_job.rb +48 -0
- data/app/jobs/ingest_file_job.rb +30 -0
- data/app/jobs/ingest_local_file_job.rb +20 -0
- data/app/jobs/resolrize_job.rb +7 -0
- data/app/models/checksum_audit_log.rb +20 -0
- data/app/models/collection.rb +6 -0
- data/app/models/concerns/curation_concerns/ability.rb +49 -0
- data/app/models/concerns/curation_concerns/basic_metadata.rb +64 -0
- data/app/models/concerns/curation_concerns/collection.rb +16 -0
- data/app/models/concerns/curation_concerns/collection_behavior.rb +62 -0
- data/app/models/concerns/curation_concerns/file_set/belongs_to_works.rb +47 -0
- data/app/models/concerns/curation_concerns/file_set/derivatives.rb +65 -0
- data/app/models/concerns/curation_concerns/file_set/full_text_indexing.rb +11 -0
- data/app/models/concerns/curation_concerns/file_set/indexing.rb +14 -0
- data/app/models/concerns/curation_concerns/file_set/querying.rb +17 -0
- data/app/models/concerns/curation_concerns/file_set_behavior.rb +36 -0
- data/app/models/concerns/curation_concerns/has_representative.rb +13 -0
- data/app/models/concerns/curation_concerns/human_readable_type.rb +17 -0
- data/app/models/concerns/curation_concerns/naming.rb +17 -0
- data/app/models/concerns/curation_concerns/permissions/readable.rb +18 -0
- data/app/models/concerns/curation_concerns/permissions/writable.rb +34 -0
- data/app/models/concerns/curation_concerns/permissions.rb +7 -0
- data/app/models/concerns/curation_concerns/required_metadata.rb +30 -0
- data/app/models/concerns/curation_concerns/serializers.rb +13 -0
- data/app/models/concerns/curation_concerns/solr_document_behavior.rb +147 -0
- data/app/models/concerns/curation_concerns/user.rb +18 -0
- data/app/models/concerns/curation_concerns/with_file_sets.rb +37 -0
- data/app/models/concerns/curation_concerns/work_behavior.rb +45 -0
- data/app/models/curation_concerns/classify_concern.rb +49 -0
- data/app/models/curation_concerns/quick_classification_query.rb +38 -0
- data/app/models/single_use_link.rb +34 -0
- data/app/models/version_committer.rb +2 -0
- data/app/search_builders/curation_concerns/collection_member_search_builder.rb +1 -1
- data/app/search_builders/curation_concerns/collection_search_builder.rb +33 -0
- data/app/search_builders/curation_concerns/member_search_builder.rb +17 -0
- data/app/services/curation_concerns/derivative_path.rb +49 -0
- data/app/services/curation_concerns/file_set_audit_service.rb +105 -0
- data/app/services/curation_concerns/indexes_thumbnails.rb +30 -0
- data/app/services/curation_concerns/local_file_service.rb +10 -0
- data/app/services/curation_concerns/lock_manager.rb +39 -0
- data/app/services/curation_concerns/lockable.rb +16 -0
- data/app/services/curation_concerns/noid.rb +23 -0
- data/app/services/curation_concerns/persist_derivatives.rb +33 -0
- data/app/services/curation_concerns/persist_directly_contained_output_file_service.rb +26 -0
- data/app/services/curation_concerns/repository_audit_service.rb +7 -0
- data/app/services/curation_concerns/thumbnail_path_service.rb +46 -0
- data/app/services/curation_concerns/time_service.rb +7 -0
- data/app/services/curation_concerns/versioning_service.rb +26 -0
- data/app/validators/has_one_title_validator.rb +8 -0
- data/app/views/batch_select/_add_button.html.erb +3 -0
- data/app/views/batch_select/_check_all.html.erb +4 -0
- data/app/views/batch_select/_tools.html.erb +10 -0
- data/app/views/catalog/_action_menu_partials/_collection.html.erb +3 -3
- data/app/views/catalog/_action_menu_partials/_default.html.erb +1 -1
- data/app/views/catalog/_document_list.html.erb +1 -1
- data/app/views/collections/_bookmark_control.html.erb +2 -0
- data/app/views/collections/_button_create_collection.html.erb +2 -0
- data/app/views/collections/_button_for_creating_empty_collection.html.erb +1 -1
- data/app/views/collections/_button_for_delete_collection.html.erb +4 -0
- data/app/views/collections/_button_for_remove_selected_from_collection.html.erb +8 -0
- data/app/views/collections/_button_for_update_collection.html.erb +4 -0
- data/app/views/collections/_button_remove_from_collection.html.erb +4 -0
- data/app/views/collections/_document_header.html.erb +9 -0
- data/app/views/collections/_edit_actions.html.erb +1 -1
- data/app/views/collections/_edit_descriptions.html.erb +1 -1
- data/app/views/collections/_form.html.erb +2 -2
- data/app/views/collections/_form_for_select_destination_collection.html.erb +21 -0
- data/app/views/collections/_form_to_add_member.html.erb +1 -1
- data/app/views/collections/_index_default.html.erb +2 -0
- data/app/views/collections/_index_header_default.html.erb +2 -0
- data/app/views/collections/_media_display.html.erb +1 -1
- data/app/views/collections/_paginate.html.erb +1 -1
- data/app/views/collections/_paginate_compact.html.erb +1 -0
- data/app/views/collections/_results_pagination.html.erb +9 -0
- data/app/views/collections/_search_collection_dashboard_form.html.erb +1 -1
- data/app/views/collections/_search_form.html.erb +1 -1
- data/app/views/collections/_search_results.html.erb +23 -0
- data/app/views/collections/_show_actions.html.erb +1 -1
- data/app/views/collections/_sort_and_per_page.html.erb +1 -1
- data/app/views/collections/_view_type_group.html.erb +1 -1
- data/app/views/collections/index.html.erb +9 -0
- data/app/views/collections/new.html.erb +3 -0
- data/app/views/curation_concerns/base/_form_permission.html.erb +10 -11
- data/app/views/curation_concerns/base/_form_permission_embargo.html.erb +1 -1
- data/app/views/curation_concerns/base/_form_permission_lease.html.erb +1 -1
- data/app/views/curation_concerns/base/_legally_binding_text.html.erb +7 -7
- data/app/views/curation_concerns/base/_related_files.html.erb +1 -1
- data/app/views/curation_concerns/base/_visibility.html.erb +2 -2
- data/app/views/curation_concerns/file_sets/_actions.html.erb +1 -1
- data/app/views/embargoes/_list_expired_active_embargoes.html.erb +1 -1
- data/app/views/error/single_use_error.html.erb +1 -1
- data/app/views/shared/_add_content.html.erb +17 -15
- data/app/views/shared/_brand_bar.html.erb +19 -10
- data/app/views/shared/_header.html.erb +2 -6
- data/app/views/shared/_my_actions.html.erb +28 -27
- data/app/views/shared/_site_actions.html.erb +5 -1
- data/app/views/shared/_site_search.html.erb +3 -2
- data/app/views/shared/_title_bar.html.erb +7 -16
- data/app/views/welcome/index.html.erb +2 -2
- data/config/locales/curation_concerns.en.yml +25 -1
- data/curation_concerns.gemspec +21 -5
- data/lib/curation_concerns/collections/accepts_batches.rb +53 -0
- data/lib/curation_concerns/collections/search_service.rb +57 -0
- data/lib/curation_concerns/collections.rb +10 -0
- data/lib/curation_concerns/configuration.rb +167 -0
- data/lib/curation_concerns/engine.rb +22 -1
- data/lib/curation_concerns/messages.rb +68 -0
- data/lib/curation_concerns/models.rb +42 -0
- data/lib/curation_concerns/name.rb +20 -0
- data/lib/curation_concerns/null_logger.rb +10 -0
- data/lib/curation_concerns/rails/routes.rb +1 -3
- data/lib/curation_concerns/version.rb +1 -1
- data/lib/curation_concerns.rb +2 -0
- data/lib/generators/curation_concerns/abstract_migration_generator.rb +31 -0
- data/lib/generators/curation_concerns/clamav_generator.rb +19 -0
- data/lib/generators/curation_concerns/collection_generator.rb +15 -0
- data/lib/generators/curation_concerns/install_generator.rb +1 -2
- data/lib/generators/curation_concerns/models_generator.rb +62 -0
- data/lib/generators/curation_concerns/templates/app/models/collection.rb +6 -0
- data/lib/generators/curation_concerns/templates/app/models/file_set.rb +4 -0
- data/lib/generators/curation_concerns/templates/config/clamav.rb +1 -0
- data/lib/generators/curation_concerns/templates/config/curation_concerns.rb +61 -0
- data/lib/generators/curation_concerns/templates/config/mime_types.rb +6 -0
- data/lib/generators/curation_concerns/templates/config/redis.yml +9 -0
- data/lib/generators/curation_concerns/templates/config/redis_config.rb +29 -0
- data/lib/generators/curation_concerns/templates/config/resque-pool.yml +1 -0
- data/lib/generators/curation_concerns/templates/config/resque_config.rb +6 -0
- data/lib/generators/curation_concerns/templates/curation_concerns.scss +3 -2
- data/lib/generators/curation_concerns/templates/migrations/create_checksum_audit_logs.rb +19 -0
- data/lib/generators/curation_concerns/templates/migrations/create_single_use_links.rb +12 -0
- data/lib/generators/curation_concerns/templates/migrations/create_version_committers.rb +15 -0
- data/lib/tasks/migrate.rake +11 -0
- data/lib/tasks/resque.rake +14 -0
- data/lib/tasks/solr_reindex.rake +8 -0
- data/spec/actors/curation_concerns/file_set_actor_spec.rb +31 -0
- data/spec/controllers/accepts_batches_controller_spec.rb +65 -0
- data/spec/controllers/collections_controller_spec.rb +272 -0
- data/spec/controllers/curation_concerns/collections_controller_spec.rb +1 -2
- data/spec/controllers/selects_collections_controller_spec.rb +109 -0
- data/spec/features/create_work_spec.rb +1 -1
- data/spec/features/work_generator_spec.rb +1 -1
- data/spec/forms/collection_edit_form_spec.rb +2 -9
- data/spec/forms/work_form_spec.rb +5 -0
- data/spec/helpers/collections_helper_spec.rb +129 -0
- data/spec/helpers/curation_concerns/collections_helper_spec.rb +2 -2
- data/spec/helpers/render_constraints_helper_spec.rb +23 -1
- data/spec/lib/curation_concerns/collections/search_service_spec.rb +33 -0
- data/spec/models/collection_spec.rb +165 -0
- data/spec/tasks/rake_spec.rb +1 -1
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +1 -1
- data/spec/views/curation_concerns/base/_form_permission.html.erb_spec.rb +4 -1
- data/spec/views/curation_concerns/file_sets/show.html.erb_spec.rb +1 -0
- data/spec/views/shared/_add_content.html.erb_spec.rb +3 -3
- metadata +341 -24
- data/VERSION +0 -1
- data/app/assets/stylesheets/curation_concerns/_global-variables.scss +0 -5
- data/app/assets/stylesheets/curation_concerns/modules/multi_value_fields.scss +0 -52
- data/app/views/collections/_form_required_information.html.erb +0 -11
- data/tasks/release.rake +0 -93
@@ -0,0 +1,15 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
class WorkIndexer < ActiveFedora::IndexingService
|
3
|
+
include IndexesThumbnails
|
4
|
+
def generate_solr_document
|
5
|
+
super.tap do |solr_doc|
|
6
|
+
# We know that all the members of GenericWorks are FileSets so we can use
|
7
|
+
# member_ids which requires fewer Fedora API calls than file_set_ids.
|
8
|
+
# file_set_ids requires loading all the members from Fedora but member_ids
|
9
|
+
# looks just at solr
|
10
|
+
solr_doc[Solrizer.solr_name('member_ids', :symbol)] = object.member_ids
|
11
|
+
Solrizer.set_field(solr_doc, 'generic_type', 'Work', :facetable)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class AuditJob < ActiveJob::Base
|
2
|
+
queue_as :audit
|
3
|
+
|
4
|
+
# URI of the resource to audit.
|
5
|
+
# This URI could include the actual resource (e.g. content) and the version to audit:
|
6
|
+
# http://localhost:8983/fedora/rest/test/a/b/c/abcxyz/content/fcr:versions/version1
|
7
|
+
# but it could also just be:
|
8
|
+
# http://localhost:8983/fedora/rest/test/a/b/c/abcxyz/content
|
9
|
+
# @param [FileSet] the parent object
|
10
|
+
# @param [String] file_id used to find the file within its parent object (usually "original_file")
|
11
|
+
# @param [String] uri of the specific file/version to be audited
|
12
|
+
def perform(file_set, file_id, uri)
|
13
|
+
log = run_audit(file_set, file_id, uri)
|
14
|
+
fixity_ok = log.pass == 1
|
15
|
+
unless fixity_ok
|
16
|
+
if CurationConcerns.config.callback.set?(:after_audit_failure)
|
17
|
+
login = file_set.depositor
|
18
|
+
user = User.find_by_user_key(login)
|
19
|
+
CurationConcerns.config.callback.run(:after_audit_failure, file_set, user, log.created_at)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
fixity_ok
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def run_audit(file_set, file_id, uri)
|
28
|
+
begin
|
29
|
+
fixity_ok = ActiveFedora::FixityService.new(uri).check
|
30
|
+
rescue Ldp::NotFound
|
31
|
+
error_msg = 'resource not found'
|
32
|
+
end
|
33
|
+
|
34
|
+
if fixity_ok
|
35
|
+
passing = 1
|
36
|
+
ChecksumAuditLog.prune_history(file_set.id, file_id)
|
37
|
+
else
|
38
|
+
logger.warn "***AUDIT*** Audit failed for #{uri} #{error_msg}"
|
39
|
+
passing = 0
|
40
|
+
end
|
41
|
+
ChecksumAuditLog.create!(pass: passing, file_set_id: file_set.id, version: uri, file_id: file_id)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def logger
|
47
|
+
ActiveFedora::Base.logger || CurationConcerns::NullLogger.new
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class CharacterizeJob < ActiveJob::Base
|
2
|
+
queue_as :characterize
|
3
|
+
|
4
|
+
# @param [FileSet] file_set
|
5
|
+
# @param [String] filename a local path for the file to characterize. By using this, we don't have to pull a copy out of fedora.
|
6
|
+
def perform(file_set, filename)
|
7
|
+
Hydra::Works::CharacterizationService.run(file_set, filename)
|
8
|
+
file_set.save!
|
9
|
+
CreateDerivativesJob.perform_later(file_set, filename)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class CreateDerivativesJob < ActiveJob::Base
|
2
|
+
queue_as :derivatives
|
3
|
+
|
4
|
+
# @param [FileSet] file_set
|
5
|
+
# @param [String] file_name
|
6
|
+
def perform(file_set, file_name)
|
7
|
+
return if file_set.video? && !CurationConcerns.config.enable_ffmpeg
|
8
|
+
|
9
|
+
file_set.create_derivatives(file_name)
|
10
|
+
# The thumbnail is indexed in the solr document, so reindex
|
11
|
+
file_set.update_index
|
12
|
+
file_set.parent.update_index if parent_needs_reindex?(file_set)
|
13
|
+
end
|
14
|
+
|
15
|
+
# If this file_set is the thumbnail for the parent work,
|
16
|
+
# then the parent also needs to be reindexed.
|
17
|
+
def parent_needs_reindex?(file_set)
|
18
|
+
return false unless file_set.parent
|
19
|
+
file_set.parent.thumbnail_id == file_set.id
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'uri'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
class ImportUrlJob < ActiveJob::Base
|
6
|
+
queue_as :import_url
|
7
|
+
|
8
|
+
def perform(file_set)
|
9
|
+
user = User.find_by_user_key(file_set.depositor)
|
10
|
+
|
11
|
+
Tempfile.open(file_set.id.tr('/', '_')) do |f|
|
12
|
+
copy_remote_file(file_set, f)
|
13
|
+
|
14
|
+
# reload the generic file once the data is copied since this is a long running task
|
15
|
+
file_set.reload
|
16
|
+
|
17
|
+
# attach downloaded file to generic file stubbed out
|
18
|
+
if CurationConcerns::FileSetActor.new(file_set, user).create_content(f)
|
19
|
+
# send message to user on download success
|
20
|
+
CurationConcerns.config.callback.run(:after_import_url_success, file_set, user)
|
21
|
+
else
|
22
|
+
CurationConcerns.config.callback.run(:after_import_url_failure, file_set, user)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def copy_remote_file(file_set, f)
|
30
|
+
f.binmode
|
31
|
+
# download file from url
|
32
|
+
uri = URI(file_set.import_url)
|
33
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
34
|
+
http.use_ssl = uri.scheme == 'https' # enable SSL/TLS
|
35
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
36
|
+
mime_type = nil
|
37
|
+
|
38
|
+
http.start do
|
39
|
+
http.request_get(uri.request_uri) do |resp|
|
40
|
+
mime_type = resp.content_type
|
41
|
+
resp.read_body do |segment|
|
42
|
+
f.write(segment)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
f.rewind
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class IngestFileJob < ActiveJob::Base
|
2
|
+
queue_as :ingest
|
3
|
+
|
4
|
+
# @param [FileSet] file_set
|
5
|
+
# @param [String] filename
|
6
|
+
# @param [String,NilClass] mime_type
|
7
|
+
# @param [String] user_key
|
8
|
+
# @param [String] relation ('original_file')
|
9
|
+
def perform(file_set, filename, mime_type, user_key, relation = 'original_file')
|
10
|
+
file = File.open(filename, "rb")
|
11
|
+
# If mime-type is known, wrap in an IO decorator
|
12
|
+
# Otherwise allow Hydra::Works service to determine mime_type
|
13
|
+
if mime_type
|
14
|
+
file = Hydra::Derivatives::IoDecorator.new(file)
|
15
|
+
file.mime_type = mime_type
|
16
|
+
file.original_name = File.basename(filename)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Tell AddFileToFileSet service to skip versioning because versions will be minted by VersionCommitter (called by save_characterize_and_record_committer) when necessary
|
20
|
+
Hydra::Works::AddFileToFileSet.call(file_set, file, relation.to_sym, versioning: false)
|
21
|
+
|
22
|
+
# Persist changes to the file_set
|
23
|
+
file_set.save!
|
24
|
+
|
25
|
+
# Do post file ingest actions
|
26
|
+
user = User.find_by_user_key(user_key)
|
27
|
+
CurationConcerns::VersioningService.create(file_set.send(relation.to_sym), user)
|
28
|
+
CurationConcerns.config.callback.run(:after_create_content, file_set, user)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class IngestLocalFileJob < ActiveJob::Base
|
2
|
+
queue_as :ingest_local
|
3
|
+
|
4
|
+
def perform(file_set_id, directory, filename, user_key)
|
5
|
+
user = User.find_by_user_key(user_key)
|
6
|
+
fail "Unable to find user for #{user_key}" unless user
|
7
|
+
file_set = FileSet.find(file_set_id)
|
8
|
+
file_set.label ||= filename
|
9
|
+
path = File.join(directory, filename)
|
10
|
+
|
11
|
+
actor = CurationConcerns::FileSetActor.new(file_set, user)
|
12
|
+
|
13
|
+
if actor.create_content(File.open(path))
|
14
|
+
FileUtils.rm(path)
|
15
|
+
CurationConcerns.config.callback.run(:after_import_local_file_success, file_set, user, filename)
|
16
|
+
else
|
17
|
+
CurationConcerns.config.callback.run(:after_import_local_file_failure, file_set, user, filename)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class ChecksumAuditLog < ActiveRecord::Base
|
2
|
+
def self.get_audit_log(id, path, version_uri)
|
3
|
+
ChecksumAuditLog.find_or_create_by(file_set_id: id, file_id: path, version: version_uri)
|
4
|
+
end
|
5
|
+
|
6
|
+
# Check to see if there are previous passing logs that we can delete
|
7
|
+
# we want to keep the first passing event after a failure, the most current passing event,
|
8
|
+
# and all failures so that this table doesn't grow too large
|
9
|
+
# Simple way (a little naieve): if the last 2 were passing, delete the first one
|
10
|
+
def self.prune_history(id, path)
|
11
|
+
list = logs_for(id, path).limit(2)
|
12
|
+
if list.size > 1 && (list[0].pass == 1) && (list[1].pass == 1)
|
13
|
+
list[0].destroy
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.logs_for(id, path)
|
18
|
+
ChecksumAuditLog.where(file_set_id: id, file_id: path).order('created_at desc, id desc')
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
module Ability
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
included do
|
5
|
+
self.ability_logic += [:curation_concerns_permissions, :add_to_collection]
|
6
|
+
end
|
7
|
+
|
8
|
+
def curation_concerns_permissions
|
9
|
+
unless current_user.new_record?
|
10
|
+
can :create, CurationConcerns::ClassifyConcern
|
11
|
+
end
|
12
|
+
|
13
|
+
# user can version if they can edit
|
14
|
+
alias_action :versions, to: :update
|
15
|
+
|
16
|
+
if admin?
|
17
|
+
admin_permissions
|
18
|
+
else
|
19
|
+
cannot :index, Hydra::AccessControls::Embargo
|
20
|
+
cannot :index, Hydra::AccessControls::Lease
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def admin_permissions
|
25
|
+
can [:create, :discover, :show, :read, :edit, :update, :destroy], :all
|
26
|
+
end
|
27
|
+
|
28
|
+
def admin?
|
29
|
+
user_groups.include? 'admin'
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_to_collection
|
33
|
+
return if current_user.new_record?
|
34
|
+
can :collect, :all
|
35
|
+
end
|
36
|
+
|
37
|
+
def registered_user?
|
38
|
+
user_groups.include? 'registered'
|
39
|
+
end
|
40
|
+
|
41
|
+
# Add this to your ability_logic if you want all logged in users to be able
|
42
|
+
# to submit content
|
43
|
+
def everyone_can_create_curation_concerns
|
44
|
+
return unless registered_user?
|
45
|
+
can :create, [::FileSet, ::Collection]
|
46
|
+
can :create, [CurationConcerns.config.curation_concerns]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
module BasicMetadata
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
property :label, predicate: ActiveFedora::RDF::Fcrepo::Model.downloadFilename, multiple: false
|
7
|
+
|
8
|
+
property :relative_path, predicate: ::RDF::URI.new('http://scholarsphere.psu.edu/ns#relativePath'), multiple: false
|
9
|
+
|
10
|
+
property :import_url, predicate: ::RDF::URI.new('http://scholarsphere.psu.edu/ns#importUrl'), multiple: false do |index|
|
11
|
+
index.as :symbol
|
12
|
+
end
|
13
|
+
|
14
|
+
property :part_of, predicate: ::RDF::Vocab::DC.isPartOf
|
15
|
+
property :resource_type, predicate: ::RDF::Vocab::DC.type do |index|
|
16
|
+
index.as :stored_searchable, :facetable
|
17
|
+
end
|
18
|
+
property :creator, predicate: ::RDF::Vocab::DC11.creator do |index|
|
19
|
+
index.as :stored_searchable, :facetable
|
20
|
+
end
|
21
|
+
property :contributor, predicate: ::RDF::Vocab::DC11.contributor do |index|
|
22
|
+
index.as :stored_searchable, :facetable
|
23
|
+
end
|
24
|
+
property :description, predicate: ::RDF::Vocab::DC11.description do |index|
|
25
|
+
index.type :text
|
26
|
+
index.as :stored_searchable
|
27
|
+
end
|
28
|
+
property :tag, predicate: ::RDF::Vocab::DC11.relation do |index|
|
29
|
+
index.as :stored_searchable, :facetable
|
30
|
+
end
|
31
|
+
property :rights, predicate: ::RDF::Vocab::DC.rights do |index|
|
32
|
+
index.as :stored_searchable
|
33
|
+
end
|
34
|
+
property :publisher, predicate: ::RDF::Vocab::DC11.publisher do |index|
|
35
|
+
index.as :stored_searchable, :facetable
|
36
|
+
end
|
37
|
+
property :date_created, predicate: ::RDF::Vocab::DC.created do |index|
|
38
|
+
index.as :stored_searchable
|
39
|
+
end
|
40
|
+
|
41
|
+
property :subject, predicate: ::RDF::Vocab::DC11.subject do |index|
|
42
|
+
index.as :stored_searchable, :facetable
|
43
|
+
end
|
44
|
+
property :language, predicate: ::RDF::Vocab::DC11.language do |index|
|
45
|
+
index.as :stored_searchable, :facetable
|
46
|
+
end
|
47
|
+
property :identifier, predicate: ::RDF::Vocab::DC.identifier do |index|
|
48
|
+
index.as :stored_searchable
|
49
|
+
end
|
50
|
+
property :based_near, predicate: ::RDF::Vocab::FOAF.based_near do |index|
|
51
|
+
index.as :stored_searchable, :facetable
|
52
|
+
end
|
53
|
+
property :related_url, predicate: ::RDF::RDFS.seeAlso do |index|
|
54
|
+
index.as :stored_searchable
|
55
|
+
end
|
56
|
+
property :bibliographic_citation, predicate: ::RDF::Vocab::DC.bibliographicCitation do |index|
|
57
|
+
index.as :stored_searchable
|
58
|
+
end
|
59
|
+
property :source, predicate: ::RDF::Vocab::DC.source do |index|
|
60
|
+
index.as :stored_searchable
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
module Collection
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
extend Deprecation
|
5
|
+
include Hydra::Works::CollectionBehavior
|
6
|
+
include Hydra::WithDepositor # for access to apply_depositor_metadata
|
7
|
+
include Hydra::AccessControls::Permissions
|
8
|
+
include CurationConcerns::RequiredMetadata
|
9
|
+
include Hydra::Works::CollectionBehavior
|
10
|
+
|
11
|
+
def add_members(new_member_ids)
|
12
|
+
return if new_member_ids.nil? || new_member_ids.empty?
|
13
|
+
members << ActiveFedora::Base.find(new_member_ids)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
module CollectionBehavior
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
include Hydra::AccessControls::WithAccessRight
|
6
|
+
include CurationConcerns::Collection
|
7
|
+
include CurationConcerns::Noid
|
8
|
+
include CurationConcerns::HumanReadableType
|
9
|
+
include CurationConcerns::HasRepresentative
|
10
|
+
include CurationConcerns::Permissions
|
11
|
+
|
12
|
+
included do
|
13
|
+
validates_with HasOneTitleValidator
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
title.present? ? title : 'No Title'
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def indexer
|
22
|
+
CurationConcerns::CollectionIndexer
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Compute the sum of each file in the collection using Solr to
|
27
|
+
# avoid having to access Fedora
|
28
|
+
#
|
29
|
+
# @return [Fixnum] size of collection in bytes
|
30
|
+
# @raise [RuntimeError] unsaved record does not exist in solr
|
31
|
+
def bytes
|
32
|
+
return 0 if member_ids.count == 0
|
33
|
+
|
34
|
+
raise "Collection must be saved to query for bytes" if new_record?
|
35
|
+
|
36
|
+
# One query per member_id because Solr is not a relational database
|
37
|
+
sizes = member_ids.collect do |work_id|
|
38
|
+
query = ActiveFedora::SolrQueryBuilder.construct_query_for_rel(has_model: ::FileSet.to_class_uri)
|
39
|
+
argz = { fl: "id, #{file_size_field}",
|
40
|
+
fq: "{!join from=#{member_ids_field} to=id}id:#{work_id}"
|
41
|
+
}
|
42
|
+
files = ActiveFedora::SolrService.query(query, argz)
|
43
|
+
files.reduce(0) { |sum, f| sum + f[file_size_field].to_i }
|
44
|
+
end
|
45
|
+
|
46
|
+
sizes.reduce(0, :+)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Field name to look up when locating the size of each file in Solr.
|
52
|
+
# Override for your own installation if using something different
|
53
|
+
def file_size_field
|
54
|
+
Solrizer.solr_name(:file_size, CurationConcerns::FileSetIndexer::STORED_INTEGER)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Solr field name collections and works use to index member ids
|
58
|
+
def member_ids_field
|
59
|
+
Solrizer.solr_name('member_ids', :symbol)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
module FileSet
|
3
|
+
module BelongsToWorks
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
before_destroy :remove_representative_relationship
|
8
|
+
end
|
9
|
+
|
10
|
+
def generic_works
|
11
|
+
in_objects # in_objects is provided by Hydra::PCDM::ObjectBehavior
|
12
|
+
end
|
13
|
+
|
14
|
+
# OPTIMIZE: We can load this from Solr much faster than loading the objects
|
15
|
+
def generic_work_ids
|
16
|
+
generic_works.map(&:id)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the first parent object
|
20
|
+
# This is a hack to handle things like FileSets inheriting access controls from their parent. (see CurationConcerns::ParentContainer in app/controllers/concerns/curation_concers/parent_container.rb)
|
21
|
+
def parent
|
22
|
+
in_objects.first
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the id of first parent object
|
26
|
+
# This is a hack to handle things like FileSets inheriting access controls from their parent. (see CurationConcerns::ParentContainer in app/controllers/concerns/curation_concers/parent_container.rb)
|
27
|
+
delegate :id, to: :parent, prefix: true
|
28
|
+
|
29
|
+
# Files with sibling relationships
|
30
|
+
# Returns all FileSets aggregated by any of the GenericWorks that aggregate the current object
|
31
|
+
def related_files
|
32
|
+
generic_works = self.generic_works
|
33
|
+
return [] if generic_works.empty?
|
34
|
+
generic_works.flat_map { |work| work.file_sets.select { |file_set| file_set.id != id } }
|
35
|
+
end
|
36
|
+
|
37
|
+
# If any parent works are pointing at this object as their representative, remove that pointer.
|
38
|
+
def remove_representative_relationship
|
39
|
+
generic_works = self.generic_works
|
40
|
+
return if generic_works.empty?
|
41
|
+
generic_works.each do |work|
|
42
|
+
work.update(representative_id: nil) if work.representative_id == id
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
module FileSet
|
3
|
+
module Derivatives
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
Hydra::Derivatives.source_file_service = CurationConcerns::LocalFileService
|
8
|
+
Hydra::Derivatives.output_file_service = CurationConcerns::PersistDerivatives
|
9
|
+
Hydra::Derivatives::FullTextExtract.output_file_service = CurationConcerns::PersistDirectlyContainedOutputFileService
|
10
|
+
after_destroy :cleanup_derivatives
|
11
|
+
end
|
12
|
+
|
13
|
+
# This completely overrides the version in Hydra::Works so that we
|
14
|
+
# read and write to a local file. It's important that characterization runs
|
15
|
+
# before derivatives so that we have a credible mime_type field to work with.
|
16
|
+
def create_derivatives(filename)
|
17
|
+
case mime_type
|
18
|
+
when *self.class.pdf_mime_types
|
19
|
+
Hydra::Derivatives::PdfDerivatives.create(filename,
|
20
|
+
outputs: [{ label: :thumbnail, format: 'jpg', size: '338x493', url: derivative_url('thumbnail') }])
|
21
|
+
Hydra::Derivatives::FullTextExtract.create(filename,
|
22
|
+
outputs: [{ url: uri, container: "extracted_text" }])
|
23
|
+
when *self.class.office_document_mime_types
|
24
|
+
Hydra::Derivatives::DocumentDerivatives.create(filename,
|
25
|
+
outputs: [{ label: :thumbnail, format: 'jpg',
|
26
|
+
size: '200x150>',
|
27
|
+
url: derivative_url('thumbnail') }])
|
28
|
+
Hydra::Derivatives::FullTextExtract.create(filename,
|
29
|
+
outputs: [{ url: uri, container: "extracted_text" }])
|
30
|
+
when *self.class.audio_mime_types
|
31
|
+
Hydra::Derivatives::AudioDerivatives.create(filename,
|
32
|
+
outputs: [{ label: 'mp3', format: 'mp3', url: derivative_url('mp3') },
|
33
|
+
{ label: 'ogg', format: 'ogg', url: derivative_url('ogg') }])
|
34
|
+
when *self.class.video_mime_types
|
35
|
+
Hydra::Derivatives::VideoDerivatives.create(filename,
|
36
|
+
outputs: [{ label: :thumbnail, format: 'jpg', url: derivative_url('thumbnail') },
|
37
|
+
{ label: 'webm', format: 'webm', url: derivative_url('webm') },
|
38
|
+
{ label: 'mp4', format: 'mp4', url: derivative_url('mp4') }])
|
39
|
+
when *self.class.image_mime_types
|
40
|
+
Hydra::Derivatives::ImageDerivatives.create(filename,
|
41
|
+
outputs: [{ label: :thumbnail, format: 'jpg', size: '200x150>', url: derivative_url('thumbnail') }])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# The destination_name parameter has to match up with the file parameter
|
48
|
+
# passed to the DownloadsController
|
49
|
+
def derivative_url(destination_name)
|
50
|
+
path = derivative_path_factory.derivative_path_for_reference(self, destination_name)
|
51
|
+
URI("file://#{path}").to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
def cleanup_derivatives
|
55
|
+
derivative_path_factory.derivatives_for_reference(self).each do |path|
|
56
|
+
FileUtils.rm_f(path)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def derivative_path_factory
|
61
|
+
DerivativePath
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
module FileSet
|
3
|
+
module Querying
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def where_digest_is(digest_string)
|
8
|
+
where Solrizer.solr_name('digest', :symbol) => urnify(digest_string)
|
9
|
+
end
|
10
|
+
|
11
|
+
def urnify(digest_string)
|
12
|
+
"urn:sha1:#{digest_string}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
module FileSetBehavior
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
include Hydra::Works::FileSetBehavior
|
5
|
+
include Hydra::Works::VirusCheck
|
6
|
+
include Hydra::Works::Characterization
|
7
|
+
include Hydra::WithDepositor
|
8
|
+
include CurationConcerns::Serializers
|
9
|
+
include CurationConcerns::Noid
|
10
|
+
include CurationConcerns::FileSet::Derivatives
|
11
|
+
include CurationConcerns::Permissions
|
12
|
+
include CurationConcerns::BasicMetadata
|
13
|
+
include CurationConcerns::FileSet::FullTextIndexing
|
14
|
+
include CurationConcerns::FileSet::Indexing
|
15
|
+
include CurationConcerns::FileSet::BelongsToWorks
|
16
|
+
include CurationConcerns::FileSet::Querying
|
17
|
+
include CurationConcerns::HumanReadableType
|
18
|
+
include CurationConcerns::RequiredMetadata
|
19
|
+
include CurationConcerns::Naming
|
20
|
+
include Hydra::AccessControls::Embargoable
|
21
|
+
include GlobalID::Identification
|
22
|
+
|
23
|
+
included do
|
24
|
+
attr_accessor :file
|
25
|
+
self.human_readable_type = 'File'
|
26
|
+
end
|
27
|
+
|
28
|
+
def representative_id
|
29
|
+
to_param
|
30
|
+
end
|
31
|
+
|
32
|
+
def thumbnail_id
|
33
|
+
to_param
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module CurationConcerns::HasRepresentative
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
belongs_to :representative,
|
6
|
+
predicate: ::RDF::Vocab::EBUCore.hasRelatedMediaFragment,
|
7
|
+
class_name: 'ActiveFedora::Base'
|
8
|
+
|
9
|
+
belongs_to :thumbnail,
|
10
|
+
predicate: ::RDF::Vocab::EBUCore.hasRelatedImage,
|
11
|
+
class_name: 'ActiveFedora::Base'
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
module HumanReadableType
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :human_readable_type
|
7
|
+
self.human_readable_type = name.demodulize.titleize
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_solr(solr_doc = {})
|
11
|
+
super(solr_doc).tap do |doc|
|
12
|
+
doc[Solrizer.solr_name('human_readable_type', :facetable)] = human_readable_type
|
13
|
+
doc[Solrizer.solr_name('human_readable_type', :stored_searchable)] = human_readable_type
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|