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,105 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
class FileSetAuditService
|
3
|
+
attr_reader :file_set
|
4
|
+
def initialize(file_set)
|
5
|
+
@file_set = file_set
|
6
|
+
end
|
7
|
+
|
8
|
+
NO_RUNS = 999
|
9
|
+
|
10
|
+
# provides a human readable version of the audit status
|
11
|
+
# This may trigger audits to be run if required
|
12
|
+
# @param [Hydra::PCDM::File] file the file to get the audit status for, defaults to the original_file.
|
13
|
+
def human_readable_audit_status(file = file_set.original_file)
|
14
|
+
audit_stat(file)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Check the file by only what is in the audit log.
|
18
|
+
# Do not try to access the versions if we do not have access to them.
|
19
|
+
# Use this when a file_set is loaded from solr instead of fedora
|
20
|
+
def logged_audit_status
|
21
|
+
audit_results = ChecksumAuditLog.logs_for(file_set.id, "original_file")
|
22
|
+
.collect { |result| result["pass"] }
|
23
|
+
|
24
|
+
if audit_results.length > 0
|
25
|
+
stat_to_string(audit_results.reduce(true) { |sum, value| sum && value })
|
26
|
+
else
|
27
|
+
'Audits have not yet been run on this file.'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Audits each version of each file if it hasn't been audited recently
|
32
|
+
# Returns the set of most recent audit status for each version of the content file
|
33
|
+
# @param [Hash] log container for messages, mapping file ids to status
|
34
|
+
def audit(log = {})
|
35
|
+
file_set.files.each { |f| log[f.id] = audit_file(f) }
|
36
|
+
log
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def stat_to_string(stat)
|
42
|
+
case stat
|
43
|
+
when 0
|
44
|
+
'failing'
|
45
|
+
when 1
|
46
|
+
'passing'
|
47
|
+
else
|
48
|
+
fail ArgumentError, "Unknown status `#{stat}'"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Retrieve or generate the audit check for a file (all versions are checked for versioned files)
|
53
|
+
# @param [ActiveFedora::File] file to audit
|
54
|
+
# @param [Array] log container for messages
|
55
|
+
def audit_file(file, log = [])
|
56
|
+
versions = file.has_versions? ? file.versions.all : file
|
57
|
+
versions.each { |v| log << audit_file_version(file.id, v.uri) }
|
58
|
+
log
|
59
|
+
end
|
60
|
+
|
61
|
+
# Retrieve or generate the audit check for a file and provide a human-readable status message.
|
62
|
+
# @param [ActiveFedora::File] file to audit
|
63
|
+
def audit_stat(file)
|
64
|
+
audit_results = audit_file(file).collect { |result| result['pass'] }
|
65
|
+
# check how many non runs we had
|
66
|
+
non_runs = audit_results.reduce(0) { |sum, value| value == NO_RUNS ? sum + 1 : sum }
|
67
|
+
if non_runs == 0
|
68
|
+
result = audit_results.reduce(true) { |sum, value| sum && value }
|
69
|
+
stat_to_string(result)
|
70
|
+
elsif non_runs < audit_results.length
|
71
|
+
result = audit_results.reduce(true) { |sum, value| value == NO_RUNS ? sum : sum && value }
|
72
|
+
"Some audits have not been run, but the ones run were #{stat_to_string(result)}."
|
73
|
+
else
|
74
|
+
'Audits have not yet been run on this file.'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Retrieve or generate the audit check for a specific version of a file
|
79
|
+
# @param [String] file_id used to find the file within its parent object (usually "original_file")
|
80
|
+
# @param [String] version_uri the version to be audited (or the file uri for non-versioned files)
|
81
|
+
def audit_file_version(file_id, version_uri)
|
82
|
+
latest_audit = ChecksumAuditLog.logs_for(file_set.id, file_id).first
|
83
|
+
return latest_audit unless needs_audit?(latest_audit)
|
84
|
+
AuditJob.perform_later(file_set, file_id, version_uri.to_s)
|
85
|
+
latest_audit || ChecksumAuditLog.new(pass: NO_RUNS, file_set_id: file_set.id, file_id: file_id, version: version_uri)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Check if time since the last audit is greater than the maximum days allowed between audits
|
89
|
+
# @param [ChecksumAuditLog] latest_audit the most recent audit event
|
90
|
+
def needs_audit?(latest_audit)
|
91
|
+
return true unless latest_audit
|
92
|
+
unless latest_audit.updated_at
|
93
|
+
logger.warn "***AUDIT*** problem with audit log! Latest Audit is not nil, but updated_at is not set #{latest_audit}"
|
94
|
+
return true
|
95
|
+
end
|
96
|
+
days_since_last_audit(latest_audit) >= CurationConcerns.config.max_days_between_audits
|
97
|
+
end
|
98
|
+
|
99
|
+
# Return the number of days since the latest audit event
|
100
|
+
# @param [ChecksumAuditLog] latest_audit the most recent audit event
|
101
|
+
def days_since_last_audit(latest_audit)
|
102
|
+
(DateTime.now - latest_audit.updated_at.to_date).to_i
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
module IndexesThumbnails
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :thumbnail_path_service
|
7
|
+
self.thumbnail_path_service = ThumbnailPathService
|
8
|
+
class_attribute :thumbnail_field
|
9
|
+
self.thumbnail_field = 'thumbnail_path_ss'.freeze
|
10
|
+
end
|
11
|
+
|
12
|
+
# Adds thumbnail indexing to the solr document
|
13
|
+
def generate_solr_document
|
14
|
+
super.tap do |solr_doc|
|
15
|
+
index_thumbnails(solr_doc)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Write the thumbnail paths into the solr_document
|
20
|
+
# @params [Hash] solr_document the solr document to add the field to
|
21
|
+
def index_thumbnails(solr_document)
|
22
|
+
solr_document[thumbnail_field] = thumbnail_path
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the value for the thumbnail path to put into the solr document
|
26
|
+
def thumbnail_path
|
27
|
+
self.class.thumbnail_path_service.call(object)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
class LocalFileService
|
3
|
+
# @param [String] file_name path to the file
|
4
|
+
# @param [Hash] _options
|
5
|
+
# @yield [File] opens the file and yields it to the block
|
6
|
+
def self.call(file_name, _options)
|
7
|
+
yield File.open(file_name)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'redlock'
|
2
|
+
module CurationConcerns
|
3
|
+
class LockManager
|
4
|
+
class UnableToAcquireLockError < StandardError; end
|
5
|
+
|
6
|
+
attr_reader :client
|
7
|
+
|
8
|
+
# @param [Fixnum] time_to_live How long to hold the lock in milliseconds
|
9
|
+
# @param [Fixnum] retry_count How many times to retry to acquire the lock before raising UnableToAcquireLockError
|
10
|
+
# @param [Fixnum] retry_delay Maximum wait time in milliseconds before retrying. Wait time is a random value between 0 and retry_delay.
|
11
|
+
def initialize(time_to_live, retry_count, retry_delay)
|
12
|
+
@ttl = time_to_live
|
13
|
+
@client = Redlock::Client.new([uri], retry_count: retry_count, retry_delay: retry_delay)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Blocks until lock is acquired or timeout.
|
17
|
+
def lock(key)
|
18
|
+
returned_from_block = nil
|
19
|
+
client.lock(key, @ttl) do |locked|
|
20
|
+
raise UnableToAcquireLockError unless locked
|
21
|
+
returned_from_block = yield
|
22
|
+
end
|
23
|
+
returned_from_block
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def uri
|
29
|
+
@uri ||= begin
|
30
|
+
opts = options
|
31
|
+
URI("#{opts[:scheme]}://#{opts[:host]}:#{opts[:port]}").to_s
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def options
|
36
|
+
::Resque.redis.redis.client.options
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
module Lockable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def acquire_lock_for(lock_key, &block)
|
6
|
+
lock_manager.lock(lock_key, &block)
|
7
|
+
end
|
8
|
+
|
9
|
+
def lock_manager
|
10
|
+
@lock_manager ||= CurationConcerns::LockManager.new(
|
11
|
+
CurationConcerns.config.lock_time_to_live,
|
12
|
+
CurationConcerns.config.lock_retry_count,
|
13
|
+
CurationConcerns.config.lock_retry_delay)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'active_fedora/noid'
|
2
|
+
|
3
|
+
module CurationConcerns
|
4
|
+
module Noid
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
## This overrides the default behavior, which is to ask Fedora for an id
|
8
|
+
# @see ActiveFedora::Persistence.assign_id
|
9
|
+
def assign_id
|
10
|
+
service.mint if CurationConcerns.config.enable_noids
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_param
|
14
|
+
id
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def service
|
20
|
+
@service ||= ActiveFedora::Noid::Service.new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
class PersistDerivatives < Hydra::Derivatives::PersistOutputFileService
|
3
|
+
# Persists a derivative to the local file system.
|
4
|
+
# This Service conforms to the signature of `Hydra::Derivatives::PersistOutputFileService`.
|
5
|
+
# This service is an alternative to the default Hydra::Derivatives::PersistOutputFileService.
|
6
|
+
# This service will always update existing and does not do versioning of persisted files.
|
7
|
+
#
|
8
|
+
# @param [#read] stream the derivative filestream
|
9
|
+
# @param [Hash] directives
|
10
|
+
# @option directives [String] :url a url to the file destination
|
11
|
+
def self.call(stream, directives)
|
12
|
+
output_file(directives) do |output|
|
13
|
+
IO.copy_stream(stream, output)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Open the output file to write and yield the block to the
|
18
|
+
# file. It makes the directories in the path if necessary.
|
19
|
+
def self.output_file(directives, &blk)
|
20
|
+
# name = derivative_path_factory.derivative_path_for_reference(object, destination_name)
|
21
|
+
raise ArgumentError, "No :url was provided in the transcoding directives" unless directives.key?(:url)
|
22
|
+
uri = URI(directives.fetch(:url))
|
23
|
+
raise ArgumentError, "Must provide a file uri" unless uri.scheme == 'file'
|
24
|
+
output_file_dir = File.dirname(uri.path)
|
25
|
+
FileUtils.mkdir_p(output_file_dir) unless File.directory?(output_file_dir)
|
26
|
+
File.open(uri.path, 'wb', &blk)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.derivative_path_factory
|
30
|
+
DerivativePath
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
# This Service is an implementation of the Hydra::Derivatives::PersistOutputFileService
|
3
|
+
# It supports directly contained files
|
4
|
+
class PersistDirectlyContainedOutputFileService < Hydra::Derivatives::PersistBasicContainedOutputFileService
|
5
|
+
# This method conforms to the signature of the .call method on Hydra::Derivatives::PersistOutputFileService
|
6
|
+
# * Persists the file within the DirectContainer specified by :container
|
7
|
+
#
|
8
|
+
# @param [#read] stream the data to be persisted
|
9
|
+
# @param [Hash] directives directions which can be used to determine where to persist to.
|
10
|
+
# @option directives [String] url URI for the parent object.
|
11
|
+
# @option directives [String] container Name of the container association.
|
12
|
+
def self.call(stream, directives)
|
13
|
+
file = Hydra::Derivatives::IoDecorator.new(stream, new_mime_type(directives.fetch(:format)))
|
14
|
+
o_name = determine_original_name(file)
|
15
|
+
m_type = determine_mime_type(file)
|
16
|
+
uri = URI(directives.fetch(:url))
|
17
|
+
raise ArgumentError, "#{uri} is not an http uri" unless uri.scheme == 'http'
|
18
|
+
file_set = ActiveFedora::Base.find(ActiveFedora::Base.uri_to_id(uri.to_s))
|
19
|
+
remote_file = file_set.send("build_#{directives.fetch(:container)}".to_sym)
|
20
|
+
remote_file.content = file
|
21
|
+
remote_file.mime_type = m_type
|
22
|
+
remote_file.original_name = o_name
|
23
|
+
file_set.save
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
class ThumbnailPathService
|
3
|
+
class << self
|
4
|
+
# @param [Work, FileSet] the object to get the thumbnail for
|
5
|
+
# @return [String] a path to the thumbnail
|
6
|
+
def call(object)
|
7
|
+
return default_image unless object.thumbnail_id
|
8
|
+
|
9
|
+
thumb = fetch_thumbnail(object)
|
10
|
+
return unless thumb
|
11
|
+
if thumb.audio?
|
12
|
+
audio_image
|
13
|
+
elsif thumbnail?(thumb)
|
14
|
+
Rails.application.routes.url_helpers.download_path(object.thumbnail_id, file: 'thumbnail')
|
15
|
+
else
|
16
|
+
default_image
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch_thumbnail(object)
|
21
|
+
return object if object.thumbnail_id == object.id
|
22
|
+
::FileSet.load_instance_from_solr(object.thumbnail_id)
|
23
|
+
rescue ActiveFedora::ObjectNotFoundError
|
24
|
+
Rails.logger.error("Couldn't find thumbnail #{object.thumbnail_id} for #{object.id}")
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def default_image
|
29
|
+
ActionController::Base.helpers.image_path 'default.png'
|
30
|
+
end
|
31
|
+
|
32
|
+
def audio_image
|
33
|
+
ActionController::Base.helpers.image_path 'audio.png'
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return true if there a file on disk for this object, otherwise false
|
37
|
+
def thumbnail?(thumb)
|
38
|
+
File.exist?(thumbnail_filepath(thumb))
|
39
|
+
end
|
40
|
+
|
41
|
+
def thumbnail_filepath(thumb)
|
42
|
+
CurationConcerns::DerivativePath.derivative_path_for_reference(thumb, 'thumbnail')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module CurationConcerns
|
2
|
+
class VersioningService
|
3
|
+
# Make a version and record the version committer
|
4
|
+
# @param [ActiveFedora::File] content
|
5
|
+
# @param [User, String] user
|
6
|
+
def self.create(content, user = nil)
|
7
|
+
content.create_version
|
8
|
+
record_committer(content, user) if user
|
9
|
+
end
|
10
|
+
|
11
|
+
# @param [ActiveFedora::File] file
|
12
|
+
def self.latest_version_of(file)
|
13
|
+
file.versions.last
|
14
|
+
end
|
15
|
+
|
16
|
+
# Record the version committer of the last version
|
17
|
+
# @param [ActiveFedora::File] content
|
18
|
+
# @param [User, String] user_key
|
19
|
+
def self.record_committer(content, user_key)
|
20
|
+
user_key = user_key.user_key if user_key.respond_to?(:user_key)
|
21
|
+
version = latest_version_of(content)
|
22
|
+
return if version.nil?
|
23
|
+
VersionCommitter.create(version_id: version.uri, committer_login: user_key)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<div class="btn-group batch-tools hidden" data-behavior="batch-tools">
|
2
|
+
<button class="btn"><i class="icon-cog"></i> Tools</button>
|
3
|
+
<button class="btn dropdown-toggle" data-toggle="dropdown">
|
4
|
+
<span class="caret"></span>
|
5
|
+
</button>
|
6
|
+
<ul class="dropdown-menu">
|
7
|
+
<li data-behavior="batch-edit-activate" data-state="<%= batch_edit_state %>"><a href="#"><i class=""></i> Batch edit</a></li>
|
8
|
+
</ul>
|
9
|
+
</div>
|
10
|
+
|
@@ -3,15 +3,15 @@
|
|
3
3
|
<ul class="dropdown-menu">
|
4
4
|
<% if can? :edit, document %>
|
5
5
|
<li>
|
6
|
-
<%= link_to [
|
6
|
+
<%= link_to [:edit, document], class: 'itemicon itemedit' do %><i class="glyphicon glyphicon-pencil"></i> Edit <%= document.human_readable_type %>
|
7
7
|
<% end %>
|
8
8
|
</li>
|
9
9
|
<li>
|
10
10
|
<% if @collection # We're on the view page for @collection. -%>
|
11
11
|
<%= button_for_remove_from_collection(document) %>
|
12
12
|
<% else %>
|
13
|
-
<%= link_to
|
14
|
-
confirm: "Deleting a collection from #{
|
13
|
+
<%= link_to document, class: 'itemicon itemtrash', title: 'Delete Collection', method: :delete, data: {
|
14
|
+
confirm: "Deleting a collection from #{application_name} is permanent. Click OK to delete this collection from #{application_name}, or Cancel to cancel this operation" } do %>
|
15
15
|
<i class="glyphicon glyphicon-trash"></i> Delete <%= document.human_readable_type %>
|
16
16
|
<% end %>
|
17
17
|
<% end %>
|
@@ -14,7 +14,7 @@
|
|
14
14
|
<%= link_to document, class: 'itemicon itemtrash',
|
15
15
|
title: "Delete #{document.human_readable_type}", method: :delete,
|
16
16
|
data: {
|
17
|
-
confirm: "Deleting a #{document.human_readable_type} from #{
|
17
|
+
confirm: "Deleting a #{document.human_readable_type} from #{application_name} is permanent. Click OK to delete this #{document.human_readable_type} from #{application_name}, or Cancel to cancel this operation" } do %>
|
18
18
|
<i class="glyphicon glyphicon-trash"></i> Delete <%= document.human_readable_type %>
|
19
19
|
<% end %>
|
20
20
|
<% end %>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<h3 id="document-list-heading" class="sr-only">List of items deposited in <%=
|
1
|
+
<h3 id="document-list-heading" class="sr-only">List of items deposited in <%= application_name %> that match your search criteria</h3>
|
2
2
|
<ol id="documents" class="container-fluid search-results-list" start="<%= document_counter_with_offset(0) %>" aria-labeled-by="document-list-heading">
|
3
3
|
<%= render documents, as: :document %>
|
4
4
|
</ol>
|
@@ -1,3 +1,3 @@
|
|
1
1
|
<div class="collection-empty">
|
2
|
-
<%= button_to label,
|
2
|
+
<%= button_to label, new_collection_path, method: :get, class: "btn btn-primary collection-add collection-empty", 'data-behavior'=>'hydra-collections', id: 'hydra-collection-add' %>
|
3
3
|
</div>
|
@@ -0,0 +1,4 @@
|
|
1
|
+
<%# button for deleting a collections %>
|
2
|
+
<%# collection -- collection to be deleted %>
|
3
|
+
<%= button_to label, collection_path(collection.id), data:{confirm: confirm, behavior: 'hydra-collections'}, method: :delete, class: "btn btn-primary collection-delete", id: 'hydra-collection-add' %>
|
4
|
+
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<%# button for removing a batch from a collection %>
|
2
|
+
<%# collection -- collection to be updated %>
|
3
|
+
<%# label -- button label %>
|
4
|
+
<%= form_for collection, url:collection_path(collection.id), :method=>:put do |f| %>
|
5
|
+
<%= f.hidden_field :members, :value => "remove" %>
|
6
|
+
<%= f.submit label, :class => "btn btn-primary collection-remove-selected submits-batches" %>
|
7
|
+
<% end %>
|
8
|
+
|
@@ -0,0 +1,4 @@
|
|
1
|
+
<%# button for updating a collections %>
|
2
|
+
<%# collection_id -- collection to be updated (use 'collection_replace_id' if you wish the form to be updated by a form value) %>
|
3
|
+
<%# label -- button label %>
|
4
|
+
<%= button_to label, collection_path(collection_id), :method=>:put, :class=>"btn btn-primary updates-collection submits-batches collection-update", 'data-behavior'=>'hydra-collections', :id=>'hydra-collection-update' %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<% # header bar for doc items in index view -%>
|
2
|
+
<div class="documentHeader clearfix">
|
3
|
+
<% # main title container for doc partial view -%>
|
4
|
+
<h5 class="index_title"><%= t('blacklight.search.documents.counter', :counter => (document_counter + 1 + @response.params[:start].to_i)) %><%= link_to_document document, :label=>document_show_link_field(document), :counter => (document_counter + 1 + @response.params[:start].to_i) %></h5>
|
5
|
+
|
6
|
+
<div class="documentFunctions span2">
|
7
|
+
<%= button_for_remove_from_collection(@collection, document) %>
|
8
|
+
</div>
|
9
|
+
</div>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%# This mimics the file_sets/descriptions partial & re-uses some of its sub-partials %>
|
2
2
|
<div id="descriptions_display" class="tab-pane active">
|
3
|
-
<%= form_for collection, url:
|
3
|
+
<%= form_for collection, url: collection_path, html: {multipart: true, class: 'form-horizontal'} do |f| %>
|
4
4
|
<%= hidden_field_tag('redirect_tab', 'descriptions') %>
|
5
5
|
<h2 class="non lower">Descriptions <small class="pull-right"><span class="error">*</span> indicates required fields</small> </h2>
|
6
6
|
<div class="well">
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<%= simple_form_for
|
1
|
+
<%= simple_form_for @form do |f| %>
|
2
2
|
|
3
3
|
<% if f.error_notification %>
|
4
4
|
<div class="alert alert-danger fade in">
|
@@ -15,7 +15,7 @@
|
|
15
15
|
<div class="col-md-12 form-actions">
|
16
16
|
<%= f.submit class: 'btn btn-primary require-contributor-agreement' %>
|
17
17
|
<% if @form.persisted? %>
|
18
|
-
<%= link_to 'Cancel',
|
18
|
+
<%= link_to 'Cancel', collection_path(@form), class: 'btn btn-link' %>
|
19
19
|
<% else %>
|
20
20
|
<%= link_to 'Cancel', main_app.root_path, class: 'btn btn-link' %>
|
21
21
|
<% end %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<% if user_collections.nil?%>
|
2
|
+
<b> Make sure you have included CurationConcerns::SelectsCollections in your controller for this page and
|
3
|
+
added a before filter with one of the find_collections variations depending on your access level: find_collections, find_collections_with_read_access, find_collections_with_edit_access
|
4
|
+
</b>
|
5
|
+
<% elsif user_collections.blank? %>
|
6
|
+
<b> You do not have access to any existing collections please create a collection. </b>
|
7
|
+
<% else %>
|
8
|
+
Please Select your collection to add you files to:
|
9
|
+
<%= form_tag(collection_path(@collection), :method => "put") do %>
|
10
|
+
<input type="hidden" name="test" value="val" />
|
11
|
+
<%= hash_as_hidden_fields({:collection =>{members:"move"}}) %>
|
12
|
+
<div class="collection-list">
|
13
|
+
<ul>
|
14
|
+
<% user_collections.each do |collection| %>
|
15
|
+
<li> <%= radio_button_tag(:destination_collection_id, collection.id, false, :class => "collection-selector") %><%= label_tag(:collection, collection.title) %> </li>
|
16
|
+
<% end %>
|
17
|
+
</ul>
|
18
|
+
</div>
|
19
|
+
<%= button_to "Move to Selected Collection", collections_path, :method=>:put, :class=>"btn btn-primary collection-update submits-batches", 'data-behavior'=>'hydra-collections', :id=>'hydra-collection-move' %>
|
20
|
+
<% end %>
|
21
|
+
<% end %>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<% fieldset_class ||= '' %>
|
2
2
|
<% select_label_id ||= '' %>
|
3
3
|
|
4
|
-
<%= form_tag
|
4
|
+
<%= form_tag main_app.collections_path, method: :put do %>
|
5
5
|
<fieldset class="required <%= fieldset_class %>">
|
6
6
|
<%= hidden_field_tag 'collection[members]', 'add' %>
|
7
7
|
<%= hidden_field_tag 'batch_document_ids[]', collectible.id %>
|
@@ -1 +1 @@
|
|
1
|
-
<a href="<%=
|
1
|
+
<a href="<%= collection_path %>" target="_new"><img src="Other.png" alt="No preview available" width="338" /></a>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<% if @response.total_pages > 1 %>
|
2
2
|
<div class="pager">
|
3
|
-
<%= paginate @response, outer_window: 2, theme: 'blacklight'
|
3
|
+
<%= paginate @response, outer_window: 2, theme: 'blacklight' %>
|
4
4
|
<div class="clearfix"></div>
|
5
5
|
</div><!-- /pager -->
|
6
6
|
<% end %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= paginate paginate_compact, page_entries_info: page_entries_info(paginate_compact), theme: :blacklight_compact %>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<div class="col-md-3 pull-right">
|
2
|
-
<%= form_for
|
2
|
+
<%= form_for @collection, url: edit_collection_path, method: :get, class: "well form-search" do |f| %>
|
3
3
|
|
4
4
|
<label class="sr-only">Search Collection <%= @collection.title %></label>
|
5
5
|
<div class="input-group">
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<div class="col-md-3 pull-right">
|
2
|
-
<%= form_for
|
2
|
+
<%= form_for @presenter, method: :get, class: "well form-search" do |f| %>
|
3
3
|
|
4
4
|
<label class="sr-only">Search Collection <%= @presenter.title %></label>
|
5
5
|
<div class="input-group">
|