Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions app/api/locomotive/api/resources/content_entry_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,19 @@ def service
present @content_entry, with: entity_klass
end

desc 'Clone a content entry'
params do
requires :id, type: String, desc: 'Content entry ID or SLUG'
end
post ':id/clone' do
@content_entry = content_type.entries.by_id_or_slug(params[:id]).first

authorize @content_entry, :clone?
@clone_content_entry = service.entry_clone(@content_entry)

present @clone_content_entry, with: entity_klass
end

desc "Delete a content entry"
params do
requires :id, type: String, desc: 'Content entry ID or SLUG'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ class Locomotive.Views.ContentEntries.IndexView extends Backbone.View

remove: ->
@list_view.remove()
super()
super()

clone: ->
@list_view.clone()
super()
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@ class Locomotive.Views.Shared.ListItemView extends Backbone.View

if confirm $(event.target).closest('a').data('confirm')
@model.destroy()

clone_item: (event) ->
event.stopPropagation() & event.preventDefault()

if confirm $(event.target).closest('a').data('confirm')
@model.clone()
8 changes: 7 additions & 1 deletion app/controllers/locomotive/content_entries_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class ContentEntriesController < BaseController
before_filter :back_to_default_site_locale, only: [:new, :create]

before_filter :load_content_type
before_filter :load_content_entry, only: [:show, :show_in_form, :edit, :update, :destroy]
before_filter :load_content_entry, only: [:show, :show_in_form, :clone, :edit, :update, :destroy]
before_filter :store_location, only: [:edit, :update]

respond_to :json, only: [:index, :sort]
Expand All @@ -22,6 +22,12 @@ def index
respond_with @content_entries
end

def clone
authorize @content_entry
@clone_content_entry = service.entry_clone(@content_entry)
redirect_to edit_content_entry_path(current_site, @content_type.slug, @clone_content_entry.id)
end

def export
authorize ContentEntry, :index?
@content_entries = @content_type.ordered_entries
Expand Down
4 changes: 4 additions & 0 deletions app/policies/locomotive/content_entry_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,9 @@ def destroy?
site_staff?
end

def clone?
site_staff?
end

end
end
29 changes: 29 additions & 0 deletions app/services/locomotive/content_entry_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,28 @@ def public_create(attributes, options = {})
end
end

# Clone a content entry.
# It sets the updated_by column with the current account.
#
# @param [ Object ] entry The content entry to update.
#
# @return [ Object ] The instance of the content entry.
#
def entry_clone(source_entry)
attributes = source_entry.as_json.delete_if {|k, v| %w(id _id).include?(k)}

sanitize_attributes!(attributes)

content_type.entries.build(attributes).tap do |entry|
entry.created_by = account if account
clone_files!(entry, source_entry)

if entry.save
track_activity 'content_entry.cloned', parameters: activity_parameters(entry)
end
end
end

# Update a content entry from the attributes passed in parameter.
# It sets the updated_by column with the current account.
#
Expand Down Expand Up @@ -225,6 +247,13 @@ def prepare_options_for_all(options)
end
end

def clone_files!(entry, source_entry)
source_entry.file_custom_fields.each do |field|
entry.send(:"remote_#{field}_url=", source_entry.send(:"remote_#{field}_url"))
entry.send(:"#{field}=", source_entry.send(:"#{field}"))
end
end

def prepare_where_statement(options)
where = (case options[:where]
when String then options[:where].blank? ? {} : JSON.parse(options[:where])
Expand Down
2 changes: 2 additions & 0 deletions app/views/locomotive/content_entries/_list.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
.actions
= link_to content_entry_path(current_site, content_type.slug, entry), title: t(:delete, scope: 'simple_form.buttons.defaults.locomotive'), class: 'remove', data: { confirm: t('locomotive.messages.confirm') }, method: :delete do
i.fa.fa-trash-o
= link_to clone_content_entry_path(current_site, content_type.slug, entry), title: t(:clone, scope: 'simple_form.buttons.defaults.locomotive'), class: 'clone', data: { confirm: t('locomotive.messages.confirm') }, method: :post do
i.fa.fa-clone-o


- if entries.respond_to?(:current_page)
Expand Down
4 changes: 4 additions & 0 deletions app/views/locomotive/content_entries/edit.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
= locale_picker_link
| &nbsp;

= link_to t(:clone, scope: 'locomotive.content_entries.shared').html_safe, clone_content_entry_path(current_site, @content_type.slug, params[:id]), data: { confirm: t('locomotive.messages.confirm') }, method: :post, class: 'btn btn-sm btn-default'

| &nbsp;

= link_to t(:back, scope: 'locomotive.content_entries.shared').html_safe, content_entries_path(current_site, @content_type.slug), class: 'btn btn-sm btn-default'

= locomotive_form_for @content_entry, as: :content_entry, url: content_entry_path(current_site, @content_type.slug, @content_entry), html: { multipart: true } do |f|
Expand Down
1 change: 1 addition & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ en:
edit:
title: '%{type} &mdash; editing entry'
shared:
clone: Clone
back: '&larr; Back to list'

developers_documentation:
Expand Down
1 change: 1 addition & 0 deletions config/locales/ru.yml
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ ru:
edit:
title: '%{type} &mdash; редактировать запись'
shared:
clone: Клонировать
back: '&larr; Вернуться к списку'
developers_documentation:
show:
Expand Down
1 change: 1 addition & 0 deletions config/locales/simple_form.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ en:
update: Save
submit: Ok
cancel: Cancel
clone: Clone
delete: Delete
close: Close
loading_text: "Saving..."
Expand Down
1 change: 1 addition & 0 deletions config/locales/simple_form.ru.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ ru:
submit: Лады
cancel: Отмена
delete: Удалить
clone: Клонировать
close: Закрыть
loading_text: "Сохранение..."
or: или
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
get :show_in_form, on: :collection
put :sort, on: :collection
get :export, on: :collection
post :clone, on: :member
end

namespace :custom_fields, path: 'content_types/:slug/fields/:name' do
Expand Down
42 changes: 41 additions & 1 deletion lib/locomotive/simple_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,43 @@ def inputs(name = nil, options = {}, &block)
end

def actions(options = {}, &block)
if options[:back_url]
if options[:clone_url] && options[:back_url]
actions_with_back_clone_button(options)
elsif options[:clone_url]
actions_with_clone_button(options)
elsif options[:back_url]
actions_with_back_button(options)
else
options[:class] ||= 'text-right form-actions'
template.content_tag(:div, options, &block)
end
end

def actions_with_back_clone_button(options = {})
clone_button = clone_button_action(options)
back_button = back_button_action(options)

template.content_tag(:div, action +
'&nbsp;'.html_safe +
translate_button(:or) +
'&nbsp;'.html_safe +
back_button +
'&nbsp;'.html_safe +
translate_button(:or) +
'&nbsp;'.html_safe +
clone_button, class: 'text-right form-actions')
end

def actions_with_clone_button(options = {})
clone_button = clone_button_action(options)

template.content_tag(:div, action +
'&nbsp;'.html_safe +
translate_button(:or) +
'&nbsp;'.html_safe +
clone_button, class: 'text-right form-actions')
end

def actions_with_back_button(options = {})
back_button = back_button_action(options)

Expand All @@ -109,6 +138,17 @@ def back_button_action(options = {})
template.link_to(label, url)
end

def clone_button_action(options = {})
label = translate_button(:clone)
url = options[:clone_url]
loading_text = translate_button(:loading_text)

template.link_to label, url,
class: options[:change_class] || "btn btn-primary btn-sm #{options[:class]}",
data: { loading_text: loading_text },
method: :post
end

def action(options = {})
action = object.persisted? ? :update : :create
label = options[:label] || translate_button(action)
Expand Down
11 changes: 11 additions & 0 deletions spec/api/locomotive/api/resources/content_entry_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,17 @@
end
end

describe "POST :id/clone" do
context 'JSON' do
let(:post_request) { post("#{url_prefix}/#{content_entry._slug}/clone.json") }

it 'clone the content entry' do
expect { post_request }.to change { Locomotive::ContentEntry.count }.by(+1)
end

end
end

describe "DELETE destroy" do
context 'JSON' do
let(:delete_request) { delete("#{url_prefix}/#{content_entry.id}.json") }
Expand Down
10 changes: 10 additions & 0 deletions spec/controllers/locomotive/content_entries_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ module Locomotive
end
end

describe "#POST :id/clone" do
subject do
put :clone, site_handle: site, slug: content_type.slug, id: content_entry.id, locale: :en
end
it { is_expected.to be_redirect }
specify do
expect { subject }.to change(Locomotive::ContentEntry, :count).by(+1)
end
end

describe "#PUT update" do
subject do
put :update, site_handle: site, slug: content_type.slug, id: content_entry.id, locale: :en,
Expand Down
11 changes: 11 additions & 0 deletions spec/services/locomotive/content_entry_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,17 @@

end

describe '#entry_clone' do

let!(:entry) { create_content_entry(title: 'Hello world', body: 'Lorem ipsum', published: true) }

subject { service.entry_clone(entry) }

it { expect(subject.created_by).to eq account }
it { expect { subject }.to change { Locomotive::ContentEntry.count }.by(1) }

end

describe '#sort' do

let(:content_type) { create_content_type(order_by: '_position') }
Expand Down