diff --git a/app/controllers/steps_controller.rb b/app/controllers/steps_controller.rb
index a8fc4e33c..44558c1c6 100644
--- a/app/controllers/steps_controller.rb
+++ b/app/controllers/steps_controller.rb
@@ -5,7 +5,8 @@ class StepsController < ApplicationController
include MarvinJsActions
before_action :load_vars, only: %i(edit update destroy show toggle_step_state checklistitem_state update_view_state
- move_up move_down update_asset_view_mode elements attachments upload_attachment)
+ move_up move_down update_asset_view_mode elements
+ attachments upload_attachment duplicate)
before_action :load_vars_nested, only: %i(new create index reorder)
before_action :convert_table_contents_to_utf8, only: %i(create update)
@@ -231,6 +232,19 @@ class StepsController < ApplicationController
end
end
+ def duplicate
+ ActiveRecord::Base.transaction do
+ position = @step.position
+ @protocol.steps.where('position > ?', position).order(position: :desc).each do |step|
+ step.update(position: step.position + 1)
+ end
+ new_step = @step.duplicate(@protocol, current_user, position + 1)
+ render json: new_step, serializer: StepSerializer, user: current_user
+ end
+ rescue ActiveRecord::RecordInvalid
+ head :unprocessable_entity
+ end
+
def update_old
respond_to do |format|
old_description = @step.description
diff --git a/app/javascript/vue/protocol/container.vue b/app/javascript/vue/protocol/container.vue
index 1b8d9ad1b..9607fffc8 100644
--- a/app/javascript/vue/protocol/container.vue
+++ b/app/javascript/vue/protocol/container.vue
@@ -104,6 +104,7 @@
@step:delete="updateStepsPosition"
@step:update="updateStep"
@stepUpdated="refreshProtocolStatus"
+ @step:insert="updateStepsPosition"
:reorderStepUrl="steps.length > 1 ? urls.reorder_steps_url : null"
/>
diff --git a/app/javascript/vue/protocol/step.vue b/app/javascript/vue/protocol/step.vue
index bed532b32..b11637f9e 100644
--- a/app/javascript/vue/protocol/step.vue
+++ b/app/javascript/vue/protocol/step.vue
@@ -113,6 +113,10 @@
{{ i18n.t('protocols.steps.options_dropdown.rearrange') }}
+
+
+ {{ i18n.t('protocols.steps.options_dropdown.duplicate') }}
+
{{ i18n.t('protocols.steps.options_dropdown.delete') }}
@@ -452,6 +456,11 @@
})
elements.push(element);
this.reorderElements(elements);
+ },
+ duplicateStep() {
+ $.post(this.urls.duplicate_step_url, (result) => {
+ this.$emit('step:insert', result.data);
+ });
}
}
}
diff --git a/app/models/protocol.rb b/app/models/protocol.rb
index a328cf142..4d3aaea2b 100644
--- a/app/models/protocol.rb
+++ b/app/models/protocol.rb
@@ -254,7 +254,6 @@ class Protocol < ApplicationRecord
end
def self.clone_contents(src, dest, current_user, clone_keywords, only_contents = false)
- assets_to_clone = []
dest.update(description: src.description, name: src.name) unless only_contents
src.clone_tinymce_assets(dest, dest.team)
@@ -271,40 +270,8 @@ class Protocol < ApplicationRecord
# Copy steps
src.steps.each do |step|
- step2 = Step.new(
- name: step.name,
- position: step.position,
- completed: false,
- user: current_user,
- protocol: dest
- )
- step2.save!
-
- # Copy texts
- step.step_texts.each do |step_text|
- step_text.duplicate(step2, step_text.step_orderable_element.position)
- end
-
- # Copy checklists
- step.checklists.asc.each do |checklist|
- checklist.duplicate(step2, current_user, checklist.step_orderable_element.position)
- end
-
- # "Shallow" Copy assets
- step.assets.each do |asset|
- asset2 = asset.dup
- asset2.save!
- step2.assets << asset2
- assets_to_clone << [asset.id, asset2.id]
- end
-
- # Copy tables
- step.tables.each do |table|
- table.duplicate(step2, current_user, table.step_table.step_orderable_element.position)
- end
+ step.duplicate(dest, current_user, step.position)
end
- # Call clone helper
- Protocol.delay(queue: :assets).deep_clone_assets(assets_to_clone)
end
def in_repository_active?
diff --git a/app/models/step.rb b/app/models/step.rb
index a12b929fd..5f33f7b99 100644
--- a/app/models/step.rb
+++ b/app/models/step.rb
@@ -151,6 +151,45 @@ class Step < ApplicationRecord
step_texts.order(created_at: :asc).first
end
+ def duplicate(protocol, user, step_position = 0)
+ assets_to_clone = []
+
+ new_step = protocol.steps.new(
+ name: name,
+ position: step_position || protocol.steps.length,
+ completed: false,
+ user: user
+ )
+ new_step.save!
+
+ # Copy texts
+ step_texts.each do |step_text|
+ step_text.duplicate(new_step, step_text.step_orderable_element.position)
+ end
+
+ # Copy checklists
+ checklists.asc.each do |checklist|
+ checklist.duplicate(new_step, user, checklist.step_orderable_element.position)
+ end
+
+ # "Shallow" Copy assets
+ assets.each do |asset|
+ new_asset = asset.dup
+ new_asset.save!
+ new_step.assets << new_asset
+ assets_to_clone << [asset.id, new_asset.id]
+ end
+
+ # Copy tables
+ tables.each do |table|
+ table.duplicate(new_step, user, table.step_table.step_orderable_element.position)
+ end
+
+ # Call clone helper
+ Protocol.delay(queue: :assets).deep_clone_assets(assets_to_clone)
+
+ new_step
+ end
private
def move_in_protocol(direction)
diff --git a/app/serializers/step_serializer.rb b/app/serializers/step_serializer.rb
index 17674da02..128454adf 100644
--- a/app/serializers/step_serializer.rb
+++ b/app/serializers/step_serializer.rb
@@ -89,7 +89,8 @@ class StepSerializer < ActiveModel::Serializer
update_view_state_step_url: update_view_state_step_path(object),
direct_upload_url: rails_direct_uploads_url,
upload_attachment_url: upload_attachment_step_path(object),
- reorder_elements_url: reorder_step_step_orderable_elements_path(step_id: object.id)
+ reorder_elements_url: reorder_step_step_orderable_elements_path(step_id: object.id),
+ duplicate_step_url: duplicate_step_path(object)
})
end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 32cb83050..f8a14e4c2 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -2558,6 +2558,7 @@ en:
options_dropdown:
title: 'Step options'
rearrange: 'Rearrange content'
+ duplicate: 'Duplicate'
delete: 'Delete'
insert:
button: 'Insert'
diff --git a/config/routes.rb b/config/routes.rb
index 31acbe05d..83954e89d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -482,6 +482,7 @@ Rails.application.routes.draw do
put 'move_up'
post 'update_view_state'
post 'update_asset_view_mode'
+ post 'duplicate'
end
end