diff --git a/app/models/asset.rb b/app/models/asset.rb index e9ed753cd..74b839d11 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb @@ -146,49 +146,61 @@ class Asset < ApplicationRecord file&.blob&.content_type end - def duplicate(new_name: nil) + def duplicate(new_name: nil, include_file_versions: false) new_asset = dup file.filename = new_name if new_name return unless new_asset.save - duplicate_file(new_asset, new_name: new_name) + duplicate_file(new_asset, new_name: new_name, include_file_versions: include_file_versions) + new_asset end - def duplicate_file(to_asset, new_name: nil) + def duplicate_blob(blob, attach_method, metadata: nil) + new_blob = nil + + blob.open do |tmp_file| + new_blob = ActiveStorage::Blob.create_and_upload!( + io: tmp_file, + filename: blob.filename, + metadata: metadata || blob.metadata + ) + + attach_method.call(new_blob) + end + + new_blob + end + + def duplicate_file_versions(to_asset) + previous_files.map(&:blob).sort_by(&:created_at).each do |blob| + duplicate_blob(blob, to_asset.previous_files.method(:attach)) # .update_column(:created_at, blob.created_at) + end + end + + def duplicate_file(to_asset, new_name: nil, include_file_versions: false) return unless file.attached? raise ArgumentError, 'Destination asset should be persisted first!' unless to_asset.persisted? - file.blob.open do |tmp_file| - metadata = blob.metadata.dup + metadata = file.blob.metadata.dup - # set new name in metadata for OVE and MarvinJS files - if new_name && file.blob.metadata['asset_type'].in?(%w(gene_sequence marvinjs)) - new_metadata_name = File.basename(new_name, File.extname(new_name)) - metadata['name'] = new_metadata_name - end - - to_blob = ActiveStorage::Blob.create_and_upload!( - io: tmp_file, - filename: blob.filename, - metadata: metadata - ) - to_asset.attach_file_version(to_blob) + # set new name in metadata for OVE and MarvinJS files + if new_name && file.blob.metadata['asset_type'].in?(%w(gene_sequence marvinjs)) + new_metadata_name = File.basename(new_name, File.extname(new_name)) + metadata['name'] = new_metadata_name end - if preview_image.attached? - preview_image.blob.open do |tmp_preview_image| - to_blob = ActiveStorage::Blob.create_and_upload!( - io: tmp_preview_image, - filename: blob.filename, - metadata: blob.metadata - ) - to_asset.attach_preview_image_version(to_blob) - end + unless include_file_versions + metadata.delete('version') + metadata.delete('restored_from_version') end + duplicate_file_versions(to_asset) if include_file_versions + duplicate_blob(blob, to_asset.method(:attach_file_version), metadata: metadata) + duplicate_blob(preview_image.blob, to_asset.method(:attach_preview_image_version)) if preview_image.attached? + to_asset.post_process_file end diff --git a/app/models/protocol.rb b/app/models/protocol.rb index 46156dc67..061801c69 100644 --- a/app/models/protocol.rb +++ b/app/models/protocol.rb @@ -351,7 +351,9 @@ class Protocol < ApplicationRecord end # Deep-clone given array of assets - def self.deep_clone_assets(assets_to_clone) + # There is an issue with Delayed Job delayed methods, ruby 3, and keyword arguments (https://github.com/collectiveidea/delayed_job/issues/1134) + # so we're forced to use a normal argument with default value. + def self.deep_clone_assets(assets_to_clone, include_file_versions = false) ActiveRecord::Base.no_touching do assets_to_clone.each do |src_id, dest_id| src = Asset.find_by(id: src_id) @@ -360,12 +362,12 @@ class Protocol < ApplicationRecord next unless src.present? && dest.present? # Clone file - src.duplicate_file(dest) + src.duplicate_file(dest, include_file_versions: include_file_versions) end end end - def self.clone_contents(src, dest, current_user, clone_keywords, only_contents = false) + def self.clone_contents(src, dest, current_user, clone_keywords, only_contents: false, include_file_versions: false) dest.update(description: src.description, name: src.name) unless only_contents src.clone_tinymce_assets(dest, dest.team) @@ -382,7 +384,7 @@ class Protocol < ApplicationRecord # Copy steps src.steps.each do |step| - step.duplicate(dest, current_user, step_position: step.position) + step.duplicate(dest, current_user, step_position: step.position, include_file_versions: include_file_versions) end end @@ -615,7 +617,7 @@ class Protocol < ApplicationRecord return draft if draft.invalid? ActiveRecord::Base.no_touching do - draft = deep_clone(draft, current_user) + draft = deep_clone(draft, current_user, include_file_versions: true) end parent_protocol.user_assignments.each do |parent_user_assignment| @@ -760,7 +762,7 @@ class Protocol < ApplicationRecord end end - def deep_clone(clone, current_user) + def deep_clone(clone, current_user, include_file_versions: false) # Save cloned protocol first success = clone.save @@ -772,7 +774,7 @@ class Protocol < ApplicationRecord raise ActiveRecord::RecordNotSaved unless success - Protocol.clone_contents(self, clone, current_user, true, true) + Protocol.clone_contents(self, clone, current_user, true, only_contents: true, include_file_versions: include_file_versions) clone.reload clone diff --git a/app/models/step.rb b/app/models/step.rb index 4652e0c47..b777c7694 100644 --- a/app/models/step.rb +++ b/app/models/step.rb @@ -117,7 +117,7 @@ class Step < ApplicationRecord step_texts.order(created_at: :asc).first end - def duplicate(protocol, user, step_position: nil, step_name: nil) + def duplicate(protocol, user, step_position: nil, step_name: nil, include_file_versions: false) ActiveRecord::Base.transaction do assets_to_clone = [] @@ -153,7 +153,7 @@ class Step < ApplicationRecord end # Call clone helper - Protocol.delay(queue: :assets).deep_clone_assets(assets_to_clone) + Protocol.delay(queue: :assets).deep_clone_assets(assets_to_clone, include_file_versions) new_step end