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.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +24 -16
  3. data/Gemfile +0 -4
  4. data/README.md +14 -0
  5. data/RELEASING.md +2 -2
  6. data/Rakefile +2 -0
  7. data/app/actors/concerns/curation_concerns/manages_embargoes_actor.rb +28 -0
  8. data/app/actors/curation_concerns/abstract_actor.rb +28 -0
  9. data/app/actors/curation_concerns/add_to_collection_actor.rb +38 -0
  10. data/app/actors/curation_concerns/apply_order_actor.rb +24 -0
  11. data/app/actors/curation_concerns/assign_identifier_actor.rb +7 -0
  12. data/app/actors/curation_concerns/assign_representative_actor.rb +18 -0
  13. data/app/actors/curation_concerns/attach_files_actor.rb +39 -0
  14. data/app/actors/curation_concerns/base_actor.rb +71 -0
  15. data/app/actors/curation_concerns/embargo_actor.rb +19 -0
  16. data/app/actors/curation_concerns/file_actor.rb +79 -0
  17. data/app/actors/curation_concerns/file_set_actor.rb +146 -0
  18. data/app/actors/curation_concerns/interpret_visibility_actor.rb +123 -0
  19. data/app/actors/curation_concerns/lease_actor.rb +19 -0
  20. data/app/actors/curation_concerns/root_actor.rb +17 -0
  21. data/app/actors/curation_concerns/work_actor_behavior.rb +8 -0
  22. data/app/assets/javascripts/curation_concerns/batch_select.js +42 -0
  23. data/app/assets/javascripts/curation_concerns/collections.js +13 -0
  24. data/app/assets/javascripts/curation_concerns/curation_concerns.js +2 -0
  25. data/app/assets/stylesheets/curation_concerns/_curation_concerns.scss +0 -3
  26. data/app/assets/stylesheets/curation_concerns/_modules.scss +1 -1
  27. data/app/assets/stylesheets/curation_concerns/_positioning.scss +3 -6
  28. data/app/assets/stylesheets/curation_concerns/_theme.scss +0 -39
  29. data/app/assets/stylesheets/curation_concerns/_typography.scss +0 -69
  30. data/app/assets/stylesheets/curation_concerns/modules/classify_work.scss +0 -2
  31. data/app/assets/stylesheets/curation_concerns/modules/collections.scss +4 -0
  32. data/app/assets/stylesheets/curation_concerns/modules/forms.scss +0 -4
  33. data/app/assets/stylesheets/curation_concerns/modules/site_actions.scss +34 -29
  34. data/app/assets/stylesheets/curation_concerns/modules/site_search.scss +0 -46
  35. data/app/assets/stylesheets/curation_concerns.scss +4 -0
  36. data/app/controllers/concerns/curation_concerns/collections_controller_behavior.rb +166 -21
  37. data/app/controllers/concerns/curation_concerns/embargoes_controller_behavior.rb +1 -1
  38. data/app/controllers/concerns/curation_concerns/leases_controller_behavior.rb +1 -1
  39. data/app/controllers/concerns/curation_concerns/selects_collections.rb +65 -0
  40. data/app/forms/curation_concerns/forms/collection_edit_form.rb +0 -29
  41. data/app/forms/curation_concerns/forms/work_form.rb +2 -1
  42. data/app/helpers/batch_select_helper.rb +23 -0
  43. data/app/helpers/collections_helper.rb +4 -0
  44. data/app/helpers/curation_concerns/collections_helper.rb +2 -2
  45. data/app/helpers/curation_concerns/collections_helper_behavior.rb +56 -0
  46. data/app/helpers/curation_concerns/render_constraints_helper.rb +14 -35
  47. data/app/helpers/curation_concerns/title_helper.rb +4 -0
  48. data/app/indexers/curation_concerns/collection_indexer.rb +16 -0
  49. data/app/indexers/curation_concerns/file_set_indexer.rb +46 -0
  50. data/app/indexers/curation_concerns/work_indexer.rb +15 -0
  51. data/app/jobs/audit_job.rb +49 -0
  52. data/app/jobs/characterize_job.rb +11 -0
  53. data/app/jobs/create_derivatives_job.rb +21 -0
  54. data/app/jobs/import_url_job.rb +48 -0
  55. data/app/jobs/ingest_file_job.rb +30 -0
  56. data/app/jobs/ingest_local_file_job.rb +20 -0
  57. data/app/jobs/resolrize_job.rb +7 -0
  58. data/app/models/checksum_audit_log.rb +20 -0
  59. data/app/models/collection.rb +6 -0
  60. data/app/models/concerns/curation_concerns/ability.rb +49 -0
  61. data/app/models/concerns/curation_concerns/basic_metadata.rb +64 -0
  62. data/app/models/concerns/curation_concerns/collection.rb +16 -0
  63. data/app/models/concerns/curation_concerns/collection_behavior.rb +62 -0
  64. data/app/models/concerns/curation_concerns/file_set/belongs_to_works.rb +47 -0
  65. data/app/models/concerns/curation_concerns/file_set/derivatives.rb +65 -0
  66. data/app/models/concerns/curation_concerns/file_set/full_text_indexing.rb +11 -0
  67. data/app/models/concerns/curation_concerns/file_set/indexing.rb +14 -0
  68. data/app/models/concerns/curation_concerns/file_set/querying.rb +17 -0
  69. data/app/models/concerns/curation_concerns/file_set_behavior.rb +36 -0
  70. data/app/models/concerns/curation_concerns/has_representative.rb +13 -0
  71. data/app/models/concerns/curation_concerns/human_readable_type.rb +17 -0
  72. data/app/models/concerns/curation_concerns/naming.rb +17 -0
  73. data/app/models/concerns/curation_concerns/permissions/readable.rb +18 -0
  74. data/app/models/concerns/curation_concerns/permissions/writable.rb +34 -0
  75. data/app/models/concerns/curation_concerns/permissions.rb +7 -0
  76. data/app/models/concerns/curation_concerns/required_metadata.rb +30 -0
  77. data/app/models/concerns/curation_concerns/serializers.rb +13 -0
  78. data/app/models/concerns/curation_concerns/solr_document_behavior.rb +147 -0
  79. data/app/models/concerns/curation_concerns/user.rb +18 -0
  80. data/app/models/concerns/curation_concerns/with_file_sets.rb +37 -0
  81. data/app/models/concerns/curation_concerns/work_behavior.rb +45 -0
  82. data/app/models/curation_concerns/classify_concern.rb +49 -0
  83. data/app/models/curation_concerns/quick_classification_query.rb +38 -0
  84. data/app/models/single_use_link.rb +34 -0
  85. data/app/models/version_committer.rb +2 -0
  86. data/app/search_builders/curation_concerns/collection_member_search_builder.rb +1 -1
  87. data/app/search_builders/curation_concerns/collection_search_builder.rb +33 -0
  88. data/app/search_builders/curation_concerns/member_search_builder.rb +17 -0
  89. data/app/services/curation_concerns/derivative_path.rb +49 -0
  90. data/app/services/curation_concerns/file_set_audit_service.rb +105 -0
  91. data/app/services/curation_concerns/indexes_thumbnails.rb +30 -0
  92. data/app/services/curation_concerns/local_file_service.rb +10 -0
  93. data/app/services/curation_concerns/lock_manager.rb +39 -0
  94. data/app/services/curation_concerns/lockable.rb +16 -0
  95. data/app/services/curation_concerns/noid.rb +23 -0
  96. data/app/services/curation_concerns/persist_derivatives.rb +33 -0
  97. data/app/services/curation_concerns/persist_directly_contained_output_file_service.rb +26 -0
  98. data/app/services/curation_concerns/repository_audit_service.rb +7 -0
  99. data/app/services/curation_concerns/thumbnail_path_service.rb +46 -0
  100. data/app/services/curation_concerns/time_service.rb +7 -0
  101. data/app/services/curation_concerns/versioning_service.rb +26 -0
  102. data/app/validators/has_one_title_validator.rb +8 -0
  103. data/app/views/batch_select/_add_button.html.erb +3 -0
  104. data/app/views/batch_select/_check_all.html.erb +4 -0
  105. data/app/views/batch_select/_tools.html.erb +10 -0
  106. data/app/views/catalog/_action_menu_partials/_collection.html.erb +3 -3
  107. data/app/views/catalog/_action_menu_partials/_default.html.erb +1 -1
  108. data/app/views/catalog/_document_list.html.erb +1 -1
  109. data/app/views/collections/_bookmark_control.html.erb +2 -0
  110. data/app/views/collections/_button_create_collection.html.erb +2 -0
  111. data/app/views/collections/_button_for_creating_empty_collection.html.erb +1 -1
  112. data/app/views/collections/_button_for_delete_collection.html.erb +4 -0
  113. data/app/views/collections/_button_for_remove_selected_from_collection.html.erb +8 -0
  114. data/app/views/collections/_button_for_update_collection.html.erb +4 -0
  115. data/app/views/collections/_button_remove_from_collection.html.erb +4 -0
  116. data/app/views/collections/_document_header.html.erb +9 -0
  117. data/app/views/collections/_edit_actions.html.erb +1 -1
  118. data/app/views/collections/_edit_descriptions.html.erb +1 -1
  119. data/app/views/collections/_form.html.erb +2 -2
  120. data/app/views/collections/_form_for_select_destination_collection.html.erb +21 -0
  121. data/app/views/collections/_form_to_add_member.html.erb +1 -1
  122. data/app/views/collections/_index_default.html.erb +2 -0
  123. data/app/views/collections/_index_header_default.html.erb +2 -0
  124. data/app/views/collections/_media_display.html.erb +1 -1
  125. data/app/views/collections/_paginate.html.erb +1 -1
  126. data/app/views/collections/_paginate_compact.html.erb +1 -0
  127. data/app/views/collections/_results_pagination.html.erb +9 -0
  128. data/app/views/collections/_search_collection_dashboard_form.html.erb +1 -1
  129. data/app/views/collections/_search_form.html.erb +1 -1
  130. data/app/views/collections/_search_results.html.erb +23 -0
  131. data/app/views/collections/_show_actions.html.erb +1 -1
  132. data/app/views/collections/_sort_and_per_page.html.erb +1 -1
  133. data/app/views/collections/_view_type_group.html.erb +1 -1
  134. data/app/views/collections/index.html.erb +9 -0
  135. data/app/views/collections/new.html.erb +3 -0
  136. data/app/views/curation_concerns/base/_form_permission.html.erb +10 -11
  137. data/app/views/curation_concerns/base/_form_permission_embargo.html.erb +1 -1
  138. data/app/views/curation_concerns/base/_form_permission_lease.html.erb +1 -1
  139. data/app/views/curation_concerns/base/_legally_binding_text.html.erb +7 -7
  140. data/app/views/curation_concerns/base/_related_files.html.erb +1 -1
  141. data/app/views/curation_concerns/base/_visibility.html.erb +2 -2
  142. data/app/views/curation_concerns/file_sets/_actions.html.erb +1 -1
  143. data/app/views/embargoes/_list_expired_active_embargoes.html.erb +1 -1
  144. data/app/views/error/single_use_error.html.erb +1 -1
  145. data/app/views/shared/_add_content.html.erb +17 -15
  146. data/app/views/shared/_brand_bar.html.erb +19 -10
  147. data/app/views/shared/_header.html.erb +2 -6
  148. data/app/views/shared/_my_actions.html.erb +28 -27
  149. data/app/views/shared/_site_actions.html.erb +5 -1
  150. data/app/views/shared/_site_search.html.erb +3 -2
  151. data/app/views/shared/_title_bar.html.erb +7 -16
  152. data/app/views/welcome/index.html.erb +2 -2
  153. data/config/locales/curation_concerns.en.yml +25 -1
  154. data/curation_concerns.gemspec +21 -5
  155. data/lib/curation_concerns/collections/accepts_batches.rb +53 -0
  156. data/lib/curation_concerns/collections/search_service.rb +57 -0
  157. data/lib/curation_concerns/collections.rb +10 -0
  158. data/lib/curation_concerns/configuration.rb +167 -0
  159. data/lib/curation_concerns/engine.rb +22 -1
  160. data/lib/curation_concerns/messages.rb +68 -0
  161. data/lib/curation_concerns/models.rb +42 -0
  162. data/lib/curation_concerns/name.rb +20 -0
  163. data/lib/curation_concerns/null_logger.rb +10 -0
  164. data/lib/curation_concerns/rails/routes.rb +1 -3
  165. data/lib/curation_concerns/version.rb +1 -1
  166. data/lib/curation_concerns.rb +2 -0
  167. data/lib/generators/curation_concerns/abstract_migration_generator.rb +31 -0
  168. data/lib/generators/curation_concerns/clamav_generator.rb +19 -0
  169. data/lib/generators/curation_concerns/collection_generator.rb +15 -0
  170. data/lib/generators/curation_concerns/install_generator.rb +1 -2
  171. data/lib/generators/curation_concerns/models_generator.rb +62 -0
  172. data/lib/generators/curation_concerns/templates/app/models/collection.rb +6 -0
  173. data/lib/generators/curation_concerns/templates/app/models/file_set.rb +4 -0
  174. data/lib/generators/curation_concerns/templates/config/clamav.rb +1 -0
  175. data/lib/generators/curation_concerns/templates/config/curation_concerns.rb +61 -0
  176. data/lib/generators/curation_concerns/templates/config/mime_types.rb +6 -0
  177. data/lib/generators/curation_concerns/templates/config/redis.yml +9 -0
  178. data/lib/generators/curation_concerns/templates/config/redis_config.rb +29 -0
  179. data/lib/generators/curation_concerns/templates/config/resque-pool.yml +1 -0
  180. data/lib/generators/curation_concerns/templates/config/resque_config.rb +6 -0
  181. data/lib/generators/curation_concerns/templates/curation_concerns.scss +3 -2
  182. data/lib/generators/curation_concerns/templates/migrations/create_checksum_audit_logs.rb +19 -0
  183. data/lib/generators/curation_concerns/templates/migrations/create_single_use_links.rb +12 -0
  184. data/lib/generators/curation_concerns/templates/migrations/create_version_committers.rb +15 -0
  185. data/lib/tasks/migrate.rake +11 -0
  186. data/lib/tasks/resque.rake +14 -0
  187. data/lib/tasks/solr_reindex.rake +8 -0
  188. data/spec/actors/curation_concerns/file_set_actor_spec.rb +31 -0
  189. data/spec/controllers/accepts_batches_controller_spec.rb +65 -0
  190. data/spec/controllers/collections_controller_spec.rb +272 -0
  191. data/spec/controllers/curation_concerns/collections_controller_spec.rb +1 -2
  192. data/spec/controllers/selects_collections_controller_spec.rb +109 -0
  193. data/spec/features/create_work_spec.rb +1 -1
  194. data/spec/features/work_generator_spec.rb +1 -1
  195. data/spec/forms/collection_edit_form_spec.rb +2 -9
  196. data/spec/forms/work_form_spec.rb +5 -0
  197. data/spec/helpers/collections_helper_spec.rb +129 -0
  198. data/spec/helpers/curation_concerns/collections_helper_spec.rb +2 -2
  199. data/spec/helpers/render_constraints_helper_spec.rb +23 -1
  200. data/spec/lib/curation_concerns/collections/search_service_spec.rb +33 -0
  201. data/spec/models/collection_spec.rb +165 -0
  202. data/spec/tasks/rake_spec.rb +1 -1
  203. data/spec/test_app_templates/lib/generators/test_app_generator.rb +1 -1
  204. data/spec/views/curation_concerns/base/_form_permission.html.erb_spec.rb +4 -1
  205. data/spec/views/curation_concerns/file_sets/show.html.erb_spec.rb +1 -0
  206. data/spec/views/shared/_add_content.html.erb_spec.rb +3 -3
  207. metadata +341 -24
  208. data/VERSION +0 -1
  209. data/app/assets/stylesheets/curation_concerns/_global-variables.scss +0 -5
  210. data/app/assets/stylesheets/curation_concerns/modules/multi_value_fields.scss +0 -52
  211. data/app/views/collections/_form_required_information.html.erb +0 -11
  212. 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,7 @@
1
+ module CurationConcerns
2
+ class RepositoryAuditService
3
+ def self.audit_everything
4
+ ::FileSet.find_each(&:audit)
5
+ end
6
+ end
7
+ 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,7 @@
1
+ module CurationConcerns
2
+ class TimeService
3
+ def self.time_in_utc
4
+ DateTime.now.utc
5
+ end
6
+ end
7
+ 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,8 @@
1
+ # validates that the title has at least one title
2
+ class HasOneTitleValidator < ActiveModel::Validator
3
+ def validate(record)
4
+ if record.title.reject(&:empty?).empty?
5
+ record.errors[:title] << "You must provide a title"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ <div data-behavior="batch-add-button">
2
+ <%= check_box_tag "batch_document_ids[]", document.id, false, class:"batch_document_selector", id: "batch_document_#{document.id}" %>
3
+ </div>
@@ -0,0 +1,4 @@
1
+ <%= form_tag(all_batch_edits_path, :method => :put, :class=> "batch-select-all hidden", "data-behavior" => 'batch-select-all') do -%>
2
+ <%= submit_tag(label, :class=>'batch-all-button btn') %>
3
+ <% end %>
4
+
@@ -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 [collections, :edit, document], class: 'itemicon itemedit' do %><i class="glyphicon glyphicon-pencil"></i> Edit <%= document.human_readable_type %>
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 [collections, document], class: 'itemicon itemtrash', title: 'Delete Collection', method: :delete, data: {
14
- confirm: "Deleting a collection from #{t('curation_concerns.product_name')} is permanent. Click OK to delete this collection from #{t('curation_concerns.product_name')}, or Cancel to cancel this operation" } do %>
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 #{t('curation_concerns.product_name')} is permanent. Click OK to delete this #{document.human_readable_type} from #{t('curation_concerns.product_name')}, or Cancel to cancel this operation" } do %>
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 <%= t('curation_concerns.product_name') %> that match your search criteria</h3>
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>
@@ -0,0 +1,2 @@
1
+ <%= render 'catalog/bookmark_control', document: document %>
2
+
@@ -0,0 +1,2 @@
1
+ <%= button_to label, new_collection_path, :method=>:get, :class=>"btn btn-primary collection-add submits-batches", 'data-behavior'=>'hydra-collections', :id=>'hydra-collection-add' %>
2
+
@@ -1,3 +1,3 @@
1
1
  <div class="collection-empty">
2
- <%= button_to label, collections.new_collection_path, method: :get, class: "btn btn-primary collection-add collection-empty", 'data-behavior'=>'hydra-collections', id: 'hydra-collection-add' %>
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,4 @@
1
+ <%= form_for collection, url: collection_path(collection), method: :put, as: 'collection' do |f| %>
2
+ <%= single_item_action_remove_form_fields(f, document) %>
3
+ <%= f.submit label, class: "btn btn-primary collection-remove" %>
4
+ <% end %>
@@ -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,5 +1,5 @@
1
1
  <h2 class="non lower">Actions</h2>
2
2
  <p>
3
- <%= link_to "Go to Browse View", collections.collection_path %> &nbsp;&nbsp;
3
+ <%= link_to "Go to Browse View", collection_path %> &nbsp;&nbsp;
4
4
  <%= link_to "Add files from your dashboard", search_path_for_my_works %>
5
5
  </p>
@@ -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: collections.collection_path, html: {multipart: true, class: 'form-horizontal'} do |f| %>
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 [collections, @form] do |f| %>
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', collections.collection_path(@form), class: 'btn btn-link' %>
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 collections.collections_path, method: :put do %>
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 %>
@@ -0,0 +1,2 @@
1
+ <%= render 'catalog/index_default', document: document, document_counter: document_counter %>
2
+
@@ -0,0 +1,2 @@
1
+ <%= render 'catalog/index_header_default', document: document, document_counter: document_counter %>
2
+
@@ -1 +1 @@
1
- <a href="<%= collections.collection_path %>" target="_new"><img src="Other.png" alt="No preview available" width="338" /></a>
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', route_set: collections %>
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 %>
@@ -0,0 +1,9 @@
1
+ <% if show_pagination? and @response.total_pages > 1 %>
2
+ <div class="row record-padding">
3
+ <div class="col-md-9">
4
+ <div class="pagination">
5
+ <%= paginate @response, :outer_window => 2, :theme => 'blacklight' %>
6
+ </div>
7
+ </div>
8
+ </div>
9
+ <% end %>
@@ -1,5 +1,5 @@
1
1
  <div class="col-md-3 pull-right">
2
- <%= form_for([collections, @collection], url: collections.edit_collection_path , method: :get, class: "well form-search") do |f| %>
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 [collections, @presenter], method: :get, class: "well form-search" do |f| %>
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">