curation_concerns 1.0.0.beta1 → 1.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/app/actors/curation_concerns/actors/abstract_actor.rb +30 -0
  3. data/app/actors/curation_concerns/actors/actor_stack.rb +29 -0
  4. data/app/actors/curation_concerns/actors/add_to_collection_actor.rb +40 -0
  5. data/app/actors/curation_concerns/actors/apply_order_actor.rb +26 -0
  6. data/app/actors/curation_concerns/actors/assign_identifier_actor.rb +9 -0
  7. data/app/actors/curation_concerns/actors/assign_representative_actor.rb +20 -0
  8. data/app/actors/curation_concerns/actors/attach_files_actor.rb +41 -0
  9. data/app/actors/curation_concerns/actors/base_actor.rb +78 -0
  10. data/app/actors/curation_concerns/actors/embargo_actor.rb +21 -0
  11. data/app/actors/curation_concerns/actors/file_actor.rb +81 -0
  12. data/app/actors/curation_concerns/actors/file_set_actor.rb +159 -0
  13. data/app/actors/curation_concerns/actors/interpret_visibility_actor.rb +125 -0
  14. data/app/actors/curation_concerns/actors/lease_actor.rb +21 -0
  15. data/app/actors/curation_concerns/actors/root_actor.rb +19 -0
  16. data/app/actors/curation_concerns/actors/work_actor_behavior.rb +12 -0
  17. data/app/actors/curation_concerns/actors.rb +18 -0
  18. data/app/controllers/concerns/curation_concerns/embargoes_controller_behavior.rb +2 -2
  19. data/app/controllers/concerns/curation_concerns/file_sets_controller_behavior.rb +21 -15
  20. data/app/controllers/concerns/curation_concerns/leases_controller_behavior.rb +2 -2
  21. data/app/forms/curation_concerns/forms/collection_edit_form.rb +1 -1
  22. data/app/forms/curation_concerns/forms/file_set_edit_form.rb +2 -2
  23. data/app/forms/curation_concerns/forms/work_form.rb +1 -1
  24. data/app/forms/curation_concerns/forms.rb +14 -0
  25. data/app/helpers/curation_concerns/collections_helper.rb +10 -8
  26. data/app/jobs/audit_job.rb +0 -2
  27. data/app/jobs/characterize_job.rb +1 -1
  28. data/app/jobs/create_derivatives_job.rb +1 -1
  29. data/app/jobs/import_url_job.rb +2 -2
  30. data/app/jobs/ingest_file_job.rb +1 -1
  31. data/app/jobs/ingest_local_file_job.rb +2 -2
  32. data/app/jobs/resolrize_job.rb +0 -2
  33. data/app/jobs/visibility_copy_job.rb +0 -2
  34. data/app/models/concerns/curation_concerns/basic_metadata.rb +1 -1
  35. data/app/models/concerns/curation_concerns/characterization.rb +41 -0
  36. data/app/models/concerns/curation_concerns/collection.rb +0 -1
  37. data/app/models/concerns/curation_concerns/file_set_behavior.rb +1 -1
  38. data/app/models/concerns/curation_concerns/solr_document_behavior.rb +6 -5
  39. data/app/presenters/curation_concerns/collection_presenter.rb +1 -1
  40. data/app/presenters/curation_concerns/file_set_presenter.rb +1 -1
  41. data/app/presenters/curation_concerns/presents_attributes.rb +1 -1
  42. data/app/renderers/curation_concerns/renderers/attribute_renderer.rb +100 -0
  43. data/app/renderers/curation_concerns/renderers/configured_microdata.rb +42 -0
  44. data/app/renderers/renderers.rb +11 -0
  45. data/app/services/curation_concerns/actors/actor_factory.rb +26 -0
  46. data/app/services/curation_concerns/curation_concern.rb +1 -1
  47. data/app/services/curation_concerns/thumbnail_path_service.rb +1 -1
  48. data/app/views/collections/show.html.erb +7 -3
  49. data/app/views/curation_concerns/base/_representative_media.html.erb +1 -1
  50. data/app/views/curation_concerns/file_sets/show.html.erb +1 -1
  51. data/config/locales/curation_concerns.en.yml +4 -4
  52. data/curation_concerns.gemspec +1 -4
  53. data/lib/curation_concerns/configuration.rb +7 -0
  54. data/lib/curation_concerns/version.rb +1 -1
  55. data/lib/generators/curation_concerns/templates/catalog_controller.rb +4 -4
  56. data/lib/generators/curation_concerns/work/templates/actor.rb.erb +2 -2
  57. data/lib/generators/curation_concerns/work/templates/actor_spec.rb.erb +1 -1
  58. data/lib/generators/curation_concerns/work/work_generator.rb +2 -2
  59. data/spec/actors/curation_concerns/add_to_collections_actor_spec.rb +6 -6
  60. data/spec/actors/curation_concerns/embargo_actor_spec.rb +1 -1
  61. data/spec/actors/curation_concerns/file_actor_spec.rb +1 -1
  62. data/spec/actors/curation_concerns/file_set_actor_spec.rb +11 -10
  63. data/spec/actors/curation_concerns/interpret_visibility_actor_spec.rb +6 -6
  64. data/spec/actors/curation_concerns/lease_actor_spec.rb +1 -1
  65. data/spec/actors/curation_concerns/work_actor_spec.rb +1 -1
  66. data/spec/controllers/curation_concerns/collections_controller_spec.rb +3 -3
  67. data/spec/controllers/curation_concerns/file_sets_controller_spec.rb +4 -4
  68. data/spec/controllers/embargoes_controller_spec.rb +1 -1
  69. data/spec/controllers/leases_controller_spec.rb +1 -1
  70. data/spec/factories/generic_works.rb +1 -1
  71. data/spec/features/add_file_spec.rb +1 -1
  72. data/spec/features/work_generator_spec.rb +1 -1
  73. data/spec/forms/collection_edit_form_spec.rb +2 -2
  74. data/spec/forms/file_set_edit_form_spec.rb +1 -1
  75. data/spec/forms/work_form_spec.rb +2 -2
  76. data/spec/indexers/file_set_indexer_spec.rb +12 -9
  77. data/spec/jobs/import_url_job_spec.rb +2 -2
  78. data/spec/jobs/ingest_local_file_job_spec.rb +1 -1
  79. data/spec/models/curation_concerns/collection_behavior_spec.rb +12 -3
  80. data/spec/models/file_set_spec.rb +25 -19
  81. data/spec/presenters/curation_concerns/collection_presenter_spec.rb +1 -1
  82. data/spec/presenters/curation_concerns/file_set_presenter_spec.rb +1 -1
  83. data/spec/presenters/curation_concerns/work_show_presenter_spec.rb +2 -2
  84. data/spec/renderers/curation_concerns/{attribute_renderer_spec.rb → renderers/attribute_renderer_spec.rb} +2 -2
  85. data/spec/services/curation_concern_spec.rb +1 -1
  86. data/spec/services/thumbnail_path_service_spec.rb +13 -9
  87. data/spec/support/curation_concerns/factory_helpers.rb +18 -0
  88. metadata +28 -50
  89. data/app/actors/curation_concerns/abstract_actor.rb +0 -28
  90. data/app/actors/curation_concerns/actor_stack.rb +0 -27
  91. data/app/actors/curation_concerns/add_to_collection_actor.rb +0 -38
  92. data/app/actors/curation_concerns/apply_order_actor.rb +0 -24
  93. data/app/actors/curation_concerns/assign_identifier_actor.rb +0 -7
  94. data/app/actors/curation_concerns/assign_representative_actor.rb +0 -18
  95. data/app/actors/curation_concerns/attach_files_actor.rb +0 -39
  96. data/app/actors/curation_concerns/base_actor.rb +0 -76
  97. data/app/actors/curation_concerns/embargo_actor.rb +0 -19
  98. data/app/actors/curation_concerns/file_actor.rb +0 -79
  99. data/app/actors/curation_concerns/file_set_actor.rb +0 -157
  100. data/app/actors/curation_concerns/interpret_visibility_actor.rb +0 -123
  101. data/app/actors/curation_concerns/lease_actor.rb +0 -19
  102. data/app/actors/curation_concerns/root_actor.rb +0 -17
  103. data/app/actors/curation_concerns/work_actor_behavior.rb +0 -8
  104. data/app/renderers/curation_concerns/attribute_renderer.rb +0 -98
  105. data/app/renderers/curation_concerns/configured_microdata.rb +0 -40
  106. data/app/services/curation_concerns/actor_factory.rb +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 82ff29ee951976ef550ed0a64a07495a4eacad37
4
- data.tar.gz: c2537a3a7611199f193a9643a53e98dacccb2d96
3
+ metadata.gz: 96756d166c6631a6999f710b4d914c1633bf87b6
4
+ data.tar.gz: 3a0dee7f354c71afc0b72844e3eb6a37cbc48b3e
5
5
  SHA512:
6
- metadata.gz: 6b97ceab4d2982f871070aa678f66e8d37dfb04f1184a22b66b0d34d07fc08269e3ac0817a2fbc0fce55767bddfa952eed57871141d2f3ceadacee084336bf34
7
- data.tar.gz: e1b11872f569fe9a090a35a71984374812b85de8cdcbd022fe2a0f3f8a0ea22203b57a3e802ae51c7bbe3f55f72dd37880606d8e334703463855f8bd250ed34a
6
+ metadata.gz: 616e0cec3bf1225238270505c6966c83639857dd54b5fbcb500c9b677341d6e775ea6abec678dfe2a8e113644c51379fca9f9fc5df0bf61716c46a707df0b67a
7
+ data.tar.gz: 09ca08d9a68ef729446a05e92f4e604ce8b2b764e35a73dcb0b12c8876fc2c68b2cb2711a3c7580d6e2fdc6730f85c150bc49ad4b20a32a6f9438bb6f069a4cf
@@ -0,0 +1,30 @@
1
+ module CurationConcerns
2
+ module Actors
3
+ # The CurationConcern Abstract actor responds to two primary actions:
4
+ # * #create
5
+ # * #update
6
+ #
7
+ # and the following attributes
8
+ #
9
+ # * next_actor
10
+ # * curation_concern
11
+ # * user
12
+ #
13
+ # it must instantiate the next actor in the chain and instantiate it.
14
+ # it should respond to curation_concern, user and attributes.
15
+ # it ha to next_actor
16
+ class AbstractActor
17
+ attr_reader :next_actor
18
+
19
+ def initialize(_curation_concern, _user, next_actor)
20
+ @next_actor = next_actor
21
+ end
22
+
23
+ delegate :curation_concern, :user, to: :next_actor
24
+
25
+ delegate :create, to: :next_actor
26
+
27
+ delegate :update, to: :next_actor
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ module CurationConcerns
2
+ module Actors
3
+ class ActorStack
4
+ attr_reader :curation_concern, :user, :first_actor_class, :more_actors
5
+ def initialize(curation_concern, user, more_actors)
6
+ @curation_concern = curation_concern
7
+ @user = user
8
+ @more_actors = more_actors
9
+ @first_actor_class = @more_actors.shift || RootActor
10
+ end
11
+
12
+ def inner_stack
13
+ Actors::ActorStack.new(curation_concern, user, more_actors)
14
+ end
15
+
16
+ def actor
17
+ first_actor_class.new(curation_concern, user, inner_stack)
18
+ end
19
+
20
+ def create(attributes)
21
+ actor.create(attributes.with_indifferent_access)
22
+ end
23
+
24
+ def update(attributes)
25
+ actor.update(attributes.with_indifferent_access)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,40 @@
1
+ module CurationConcerns
2
+ module Actors
3
+ class AddToCollectionActor < AbstractActor
4
+ def create(attributes)
5
+ collection_ids = attributes.delete(:collection_ids)
6
+ next_actor.create(attributes) && add_to_collections(collection_ids)
7
+ end
8
+
9
+ def update(attributes)
10
+ collection_ids = attributes.delete(:collection_ids)
11
+ add_to_collections(collection_ids) && next_actor.update(attributes)
12
+ end
13
+
14
+ private
15
+
16
+ # The default behavior of active_fedora's aggregates association,
17
+ # when assigning the id accessor (e.g. collection_ids = ['foo:1']) is to add
18
+ # to new collections, but not remove from old collections.
19
+ # This method ensures it's removed from the old collections.
20
+ def add_to_collections(new_collection_ids)
21
+ return true unless new_collection_ids
22
+ # remove from old collections
23
+ # TODO: Implement in_collection_ids https://github.com/projecthydra-labs/hydra-pcdm/issues/157
24
+ (curation_concern.in_collections.map(&:id) - new_collection_ids).each do |old_id|
25
+ collection = ::Collection.find(old_id)
26
+ collection.members.delete(curation_concern)
27
+ collection.save
28
+ end
29
+
30
+ # add to new
31
+ new_collection_ids.each do |coll_id|
32
+ collection = ::Collection.find(coll_id)
33
+ collection.members << curation_concern
34
+ collection.save
35
+ end
36
+ true
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ module CurationConcerns
2
+ module Actors
3
+ class ApplyOrderActor < AbstractActor
4
+ def update(attributes)
5
+ ordered_member_ids = attributes.delete(:ordered_member_ids)
6
+ apply_order(ordered_member_ids) && next_actor.update(attributes)
7
+ end
8
+
9
+ private
10
+
11
+ def apply_order(new_order)
12
+ return true unless new_order
13
+ curation_concern.ordered_member_proxies.each_with_index do |proxy, index|
14
+ unless new_order[index]
15
+ proxy.prev.next = curation_concern.ordered_member_proxies.last.next
16
+ break
17
+ end
18
+ proxy.proxy_for = ActiveFedora::Base.id_to_uri(new_order[index])
19
+ proxy.target = nil
20
+ end
21
+ curation_concern.list_source.order_will_change!
22
+ true
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ module CurationConcerns
2
+ module Actors
3
+ class AssignIdentifierActor < AbstractActor
4
+ def create(attributes)
5
+ curation_concern.assign_id && next_actor.create(attributes)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,20 @@
1
+ module CurationConcerns
2
+ module Actors
3
+ class AssignRepresentativeActor < AbstractActor
4
+ def create(attributes)
5
+ next_actor.create(attributes) && assign_representative
6
+ end
7
+
8
+ private
9
+
10
+ def assign_representative
11
+ unless curation_concern.representative_id
12
+ # TODO: Possible optimization here. Does this cause a fetch of ordered_members if they're already loaded?
13
+ representative = nil # curation_concern.ordered_members.association.reader.first.target
14
+ curation_concern.representative = representative if representative
15
+ end
16
+ curation_concern.save
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,41 @@
1
+ module CurationConcerns
2
+ module Actors
3
+ class AttachFilesActor < AbstractActor
4
+ def create(attributes)
5
+ files = [attributes.delete(:files)].flatten.compact
6
+ attach_files(files, visibility_attributes(attributes)) &&
7
+ next_actor.create(attributes)
8
+ end
9
+
10
+ def update(attributes)
11
+ files = [attributes.delete(:files)].flatten.compact
12
+ next_actor.update(attributes) &&
13
+ attach_files(files, visibility_attributes(attributes))
14
+ end
15
+
16
+ private
17
+
18
+ def attach_files(files, visibility_attr)
19
+ files.all? do |file|
20
+ attach_file(file, visibility_attr)
21
+ end
22
+ end
23
+
24
+ def attach_file(file, visibility_attr)
25
+ file_set = ::FileSet.new
26
+ file_set_actor = CurationConcerns::Actors::FileSetActor.new(file_set, user)
27
+ file_set_actor.create_metadata(curation_concern, visibility_attr)
28
+ file_set_actor.create_content(file)
29
+ end
30
+
31
+ # The attributes used for visibility - used to send as initial params to
32
+ # created FileSets.
33
+ def visibility_attributes(attributes)
34
+ attributes.slice(:visibility, :visibility_during_lease,
35
+ :visibility_after_lease, :lease_expiration_date,
36
+ :embargo_release_date, :visibility_during_embargo,
37
+ :visibility_after_embargo)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,78 @@
1
+ module CurationConcerns
2
+ module Actors
3
+ # The CurationConcern base actor responds to two primary actions:
4
+ # * #create
5
+ # * #update
6
+ # it must instantiate the next actor in the chain and instantiate it.
7
+ # it should respond to curation_concern, user and attributes.
8
+ class BaseActor < AbstractActor
9
+ attr_reader :cloud_resources
10
+
11
+ def create(attributes)
12
+ @cloud_resources = attributes.delete(:cloud_resources.to_s)
13
+ apply_creation_data_to_curation_concern
14
+ apply_save_data_to_curation_concern(attributes)
15
+ next_actor.create(attributes) && save && run_callbacks(:after_create_concern)
16
+ end
17
+
18
+ def update(attributes)
19
+ apply_update_data_to_curation_concern
20
+ apply_save_data_to_curation_concern(attributes)
21
+ next_actor.update(attributes) && save && run_callbacks(:after_update_metadata)
22
+ end
23
+
24
+ protected
25
+
26
+ def run_callbacks(hook)
27
+ CurationConcerns.config.callback.run(hook, curation_concern, user)
28
+ true
29
+ end
30
+
31
+ def apply_creation_data_to_curation_concern
32
+ apply_depositor_metadata
33
+ apply_deposit_date
34
+ end
35
+
36
+ def apply_update_data_to_curation_concern
37
+ true
38
+ end
39
+
40
+ def apply_depositor_metadata
41
+ curation_concern.apply_depositor_metadata(user.user_key)
42
+ curation_concern.edit_users += [user.user_key]
43
+ end
44
+
45
+ def apply_deposit_date
46
+ curation_concern.date_uploaded = CurationConcerns::TimeService.time_in_utc
47
+ end
48
+
49
+ def save
50
+ curation_concern.save
51
+ end
52
+
53
+ def apply_save_data_to_curation_concern(attributes)
54
+ attributes[:rights] = Array(attributes[:rights]) if attributes.key? :rights
55
+ remove_blank_attributes!(attributes)
56
+ curation_concern.attributes = attributes.symbolize_keys
57
+ curation_concern.date_modified = CurationConcerns::TimeService.time_in_utc
58
+ end
59
+
60
+ # If any attributes are blank remove them
61
+ # e.g.:
62
+ # self.attributes = { 'title' => ['first', 'second', ''] }
63
+ # remove_blank_attributes!
64
+ # self.attributes
65
+ # => { 'title' => ['first', 'second'] }
66
+ def remove_blank_attributes!(attributes)
67
+ multivalued_form_attributes(attributes).each_with_object(attributes) do |(k, v), h|
68
+ h[k] = v.instance_of?(Array) ? v.select(&:present?) : v
69
+ end
70
+ end
71
+
72
+ # Return the hash of attributes that are multivalued and not uploaded files
73
+ def multivalued_form_attributes(attributes)
74
+ attributes.select { |_, v| v.respond_to?(:select) && !v.respond_to?(:read) }
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,21 @@
1
+ module CurationConcerns
2
+ module Actors
3
+ class EmbargoActor
4
+ attr_reader :work
5
+
6
+ # @param [Hydra::Works::Work] work
7
+ def initialize(work)
8
+ @work = work
9
+ end
10
+
11
+ # Update the visibility of the work to match the correct state of the embargo, then clear the embargo date, etc.
12
+ # Saves the embargo and the work
13
+ def destroy
14
+ work.embargo_visibility! # If the embargo has lapsed, update the current visibility.
15
+ work.deactivate_embargo!
16
+ work.embargo.save!
17
+ work.save!
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,81 @@
1
+ module CurationConcerns
2
+ module Actors
3
+ # actions for a file identified by file_set and relation (maps to use predicate)
4
+ class FileActor
5
+ attr_reader :file_set, :relation, :user
6
+
7
+ # @param [FileSet] file_set the parent FileSet
8
+ # @param [String] relation the type/use for the file.
9
+ # @param [User] user the user to record as the Agent acting upon the file
10
+ def initialize(file_set, relation, user)
11
+ @file_set = file_set
12
+ @relation = relation
13
+ @user = user
14
+ end
15
+
16
+ # Puts the uploaded content into a staging directory. Then kicks off a
17
+ # job to characterize and create derivatives with this on disk variant.
18
+ # Simultaneously moving a preservation copy to the repostiory.
19
+ # TODO: create a job to monitor this directory and prune old files that
20
+ # have made it to the repo
21
+ # @param [File, ActionDigest::HTTP::UploadedFile, Tempfile] file the file to save in the repository
22
+ def ingest_file(file)
23
+ working_file = copy_file_to_working_directory(file, file_set.id)
24
+ mime_type = file.respond_to?(:content_type) ? file.content_type : nil
25
+ IngestFileJob.perform_later(file_set, working_file, mime_type, user, relation)
26
+ make_derivative(file_set, working_file)
27
+ true
28
+ end
29
+
30
+ def revert_to(revision_id)
31
+ repository_file = file_set.send(relation.to_sym)
32
+ repository_file.restore_version(revision_id)
33
+
34
+ return false unless file_set.save
35
+
36
+ CurationConcerns::VersioningService.create(repository_file, user)
37
+
38
+ # Retrieve a copy of the orginal file from the repository
39
+ working_file = copy_repository_resource_to_working_directory(repository_file)
40
+ make_derivative(file_set, working_file)
41
+ true
42
+ end
43
+
44
+ private
45
+
46
+ def make_derivative(file_set, working_file)
47
+ CharacterizeJob.perform_later(file_set, working_file)
48
+ end
49
+
50
+ # @param [File, ActionDispatch::Http::UploadedFile] file
51
+ # @param [String] id the identifer of the FileSet
52
+ # @return [String] path of the working file
53
+ def copy_file_to_working_directory(file, id)
54
+ file_name = file.respond_to?(:original_filename) ? file.original_filename : ::File.basename(file)
55
+ copy_stream_to_working_directory(id, file_name, file)
56
+ end
57
+
58
+ # @param [ActiveFedora::File] file the resource in the repo
59
+ # @return [String] path of the working file
60
+ def copy_repository_resource_to_working_directory(file)
61
+ copy_stream_to_working_directory(file_set.id, file.original_name, StringIO.new(file.content))
62
+ end
63
+
64
+ # @param [String] id the identifer
65
+ # @param [String] name the file name
66
+ # @param [#read] stream the stream to copy to the working directory
67
+ # @return [String] path of the working file
68
+ def copy_stream_to_working_directory(id, name, stream)
69
+ working_path = full_filename(id, name)
70
+ FileUtils.mkdir_p(File.dirname(working_path))
71
+ IO.copy_stream(stream, working_path)
72
+ working_path
73
+ end
74
+
75
+ def full_filename(id, original_name)
76
+ pair = id.scan(/..?/).first(4)
77
+ File.join(CurationConcerns.config.working_path, *pair, original_name)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,159 @@
1
+ module CurationConcerns
2
+ module Actors
3
+ # Actions are decoupled from controller logic so that they may be called from a controller or a background job.
4
+ class FileSetActor
5
+ include CurationConcerns::Lockable
6
+
7
+ attr_reader :file_set, :user, :attributes
8
+
9
+ def initialize(file_set, user)
10
+ @file_set = file_set
11
+ @user = user
12
+ end
13
+
14
+ # Adds the appropriate metadata, visibility and relationships to file_set
15
+ #
16
+ # *Note*: In past versions of Sufia this method did not perform a save because it is mainly used in conjunction with
17
+ # create_content, which also performs a save. However, due to the relationship between Hydra::PCDM objects,
18
+ # we have to save both the parent work and the file_set in order to record the "metadata" relationship
19
+ # between them.
20
+ # @param [ActiveFedora::Base] work the parent work that will contain the file_set.
21
+ # @param [Hash] file_set specifying the visibility, lease and/or embargo of the file set. If you don't provide at least one of visibility, embargo_release_date or lease_expiration_date, visibility will be copied from the parent.
22
+
23
+ def create_metadata(work, file_set_params = {})
24
+ file_set.apply_depositor_metadata(user)
25
+ now = CurationConcerns::TimeService.time_in_utc
26
+ file_set.date_uploaded = now
27
+ file_set.date_modified = now
28
+ file_set.creator = [user.user_key]
29
+
30
+ Actors::ActorStack.new(file_set, user, [InterpretVisibilityActor]).create(file_set_params) if assign_visibility?(file_set_params)
31
+ attach_file_to_work(work, file_set, file_set_params) if work
32
+ yield(file_set) if block_given?
33
+ end
34
+
35
+ # @param [File, ActionDigest::HTTP::UploadedFile, Tempfile] file the file uploaded by the user.
36
+ # @param [String] relation ('original_file')
37
+ def create_content(file, relation = 'original_file')
38
+ # If the file set doesn't have a title or label assigned, set a default.
39
+ file_set.label ||= file.respond_to?(:original_filename) ? file.original_filename : ::File.basename(file)
40
+ file_set.title = [file_set.label] if file_set.title.blank?
41
+
42
+ # Need to save the file_set in order to get an id
43
+ return false unless file_set.save
44
+
45
+ file_actor_class.new(file_set, relation, user).ingest_file(file)
46
+ true
47
+ end
48
+
49
+ # @param [String] revision_id the revision to revert to
50
+ # @param [String] relation ('original_file')
51
+ def revert_content(revision_id, relation = 'original_file')
52
+ file_actor = file_actor_class.new(file_set, relation, user)
53
+ if file_actor.revert_to(revision_id)
54
+ CurationConcerns.config.callback.run(:after_revert_content, file_set, user, revision_id)
55
+ true
56
+ else
57
+ false
58
+ end
59
+ end
60
+
61
+ # @param [File, ActionDigest::HTTP::UploadedFile, Tempfile] file the file uploaded by the user.
62
+ # @param [String] relation ('original_file')
63
+ def update_content(file, relation = 'original_file')
64
+ file_actor_class.new(file_set, relation, user).ingest_file(file)
65
+ CurationConcerns.config.callback.run(:after_update_content, file_set, user)
66
+ true
67
+ end
68
+
69
+ def update_metadata(attributes)
70
+ stack = Actors::ActorStack.new(file_set,
71
+ user,
72
+ [InterpretVisibilityActor, BaseActor])
73
+ stack.update(attributes)
74
+ end
75
+
76
+ def destroy
77
+ unlink_from_work
78
+ file_set.destroy
79
+ CurationConcerns.config.callback.run(:after_destroy, file_set.id, user)
80
+ end
81
+
82
+ def file_actor_class
83
+ CurationConcerns::Actors::FileActor
84
+ end
85
+
86
+ private
87
+
88
+ # Takes an optional block and executes the block if the save was successful.
89
+ # returns false if the save was unsuccessful
90
+ def save
91
+ save_tries = 0
92
+ begin
93
+ return false unless file_set.save
94
+ rescue RSolr::Error::Http => error
95
+ ActiveFedora::Base.logger.warn "CurationConcerns::Actors::FileSetActor#save Caught RSOLR error #{error.inspect}"
96
+ save_tries += 1
97
+ # fail for good if the tries is greater than 3
98
+ raise error if save_tries >= 3
99
+ sleep 0.01
100
+ retry
101
+ end
102
+ yield if block_given?
103
+ true
104
+ end
105
+
106
+ # Adds a FileSet to the work using ore:Aggregations.
107
+ # Locks to ensure that only one process is operating on
108
+ # the list at a time.
109
+ def attach_file_to_work(work, file_set, file_set_params)
110
+ acquire_lock_for(work.id) do
111
+ # Ensure we have an up-to-date copy of the members association, so
112
+ # that we append to the end of the list.
113
+ work.reload unless work.new_record?
114
+ unless assign_visibility?(file_set_params)
115
+ copy_visibility(work, file_set)
116
+ end
117
+ work.ordered_members << file_set
118
+ set_representative(work, file_set)
119
+ set_thumbnail(work, file_set)
120
+
121
+ # Save the work so the association between the work and the file_set is persisted (head_id)
122
+ work.save
123
+ end
124
+ CurationConcerns.config.callback.run(:after_create_fileset, file_set, user)
125
+ end
126
+
127
+ def assign_visibility?(file_set_params = {})
128
+ !((file_set_params || {}).keys & %w(visibility embargo_release_date lease_expiration_date)).empty?
129
+ end
130
+
131
+ # copy visibility from source_concern to destination_concern
132
+ def copy_visibility(source_concern, destination_concern)
133
+ destination_concern.visibility = source_concern.visibility
134
+ end
135
+
136
+ def set_representative(work, file_set)
137
+ return unless work.representative_id.blank?
138
+ work.representative = file_set
139
+ end
140
+
141
+ def set_thumbnail(work, file_set)
142
+ return unless work.thumbnail_id.blank?
143
+ work.thumbnail = file_set
144
+ end
145
+
146
+ def unlink_from_work
147
+ work = file_set.parent
148
+ return unless work && (work.thumbnail_id == file_set.id || work.representative_id == file_set.id)
149
+ # This is required to clear the thumbnail_id and representative_id
150
+ # fields on the work and force it to be re-solrized. Although
151
+ # ActiveFedora clears the children nodes it leaves the work's
152
+ # thumbnail_id and representative_id fields in Solr populated.
153
+ work.thumbnail = nil if work.thumbnail_id == file_set.id
154
+ work.representative = nil if work.representative_id == file_set.id
155
+ work.save!
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,125 @@
1
+ module CurationConcerns
2
+ module Actors
3
+ class InterpretVisibilityActor < AbstractActor
4
+ class Intention
5
+ def initialize(attributes)
6
+ @attributes = attributes
7
+ end
8
+
9
+ # returns a copy of attributes with the necessary params removed
10
+ # If the lease or embargo is valid, or if they selected something besides lease
11
+ # or embargo, remove all the params.
12
+ def sanitize_params
13
+ if valid_lease?
14
+ @attributes.except(:visibility,
15
+ :embargo_release_date,
16
+ :visibility_during_embargo,
17
+ :visibility_after_embargo)
18
+ elsif valid_embargo?
19
+ @attributes.except(:visibility,
20
+ :lease_expiration_date,
21
+ :visibility_during_lease,
22
+ :visibility_after_lease)
23
+ elsif !wants_lease? && !wants_embargo?
24
+ @attributes.except(:lease_expiration_date,
25
+ :visibility_during_lease,
26
+ :visibility_after_lease,
27
+ :embargo_release_date,
28
+ :visibility_during_embargo,
29
+ :visibility_after_embargo)
30
+ else
31
+ @attributes
32
+ end
33
+ end
34
+
35
+ def wants_lease?
36
+ visibility == Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_LEASE
37
+ end
38
+
39
+ def wants_embargo?
40
+ visibility == Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_EMBARGO
41
+ end
42
+
43
+ def valid_lease?
44
+ wants_lease? && @attributes[:lease_expiration_date].present?
45
+ end
46
+
47
+ def valid_embargo?
48
+ wants_embargo? && @attributes[:embargo_release_date].present?
49
+ end
50
+
51
+ def lease_params
52
+ [:lease_expiration_date,
53
+ :visibility_during_lease,
54
+ :visibility_after_lease].map { |key| @attributes[key] }
55
+ end
56
+
57
+ def embargo_params
58
+ [:embargo_release_date,
59
+ :visibility_during_embargo,
60
+ :visibility_after_embargo].map { |key| @attributes[key] }
61
+ end
62
+
63
+ private
64
+
65
+ def visibility
66
+ @attributes[:visibility]
67
+ end
68
+ end
69
+
70
+ def create(attributes)
71
+ @intention = Intention.new(attributes)
72
+ attributes = @intention.sanitize_params
73
+ validate && apply_visibility(attributes) && next_actor.create(attributes)
74
+ end
75
+
76
+ def update(attributes)
77
+ @intention = Intention.new(attributes)
78
+ attributes = @intention.sanitize_params
79
+ validate && apply_visibility(attributes) && next_actor.update(attributes)
80
+ end
81
+
82
+ private
83
+
84
+ def validate
85
+ validate_lease && validate_embargo
86
+ end
87
+
88
+ def apply_visibility(attributes)
89
+ result = apply_lease && apply_embargo
90
+ if attributes[:visibility]
91
+ curation_concern.visibility = attributes[:visibility]
92
+ end
93
+ result
94
+ end
95
+
96
+ def validate_lease
97
+ return true unless @intention.wants_lease? && !@intention.valid_lease?
98
+ curation_concern.errors.add(:visibility, 'When setting visibility to "lease" you must also specify lease expiration date.')
99
+ false
100
+ end
101
+
102
+ def validate_embargo
103
+ return true unless @intention.wants_embargo? && !@intention.valid_embargo?
104
+ curation_concern.errors.add(:visibility, 'When setting visibility to "embargo" you must also specify embargo release date.')
105
+ false
106
+ end
107
+
108
+ # If they want a lease, we can assume it's valid
109
+ def apply_lease
110
+ return true unless @intention.wants_lease?
111
+ curation_concern.apply_lease(*@intention.lease_params)
112
+ return unless curation_concern.lease
113
+ curation_concern.lease.save # see https://github.com/projecthydra/hydra-head/issues/226
114
+ end
115
+
116
+ # If they want an embargo, we can assume it's valid
117
+ def apply_embargo
118
+ return true unless @intention.wants_embargo?
119
+ curation_concern.apply_embargo(*@intention.embargo_params)
120
+ return unless curation_concern.embargo
121
+ curation_concern.embargo.save # see https://github.com/projecthydra/hydra-head/issues/226
122
+ end
123
+ end
124
+ end
125
+ end