Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Richard/eco 63 #346

Open
wants to merge 7 commits into
base: v6
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
5 changes: 5 additions & 0 deletions app/cells/folio/console/index/actions_cell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ def default_actions
icon: :edit_box,
url: -> (record) { through_aware_console_url_for(record, action: :edit, safe: true) },
},
clone: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

podle nejake tridni metody bych to zaroven pridal pro patricne tridy do default_actions

name: :clone,
icon: :plus_circle_multiple_outline,
url: -> (record) { through_aware_console_url_for(record, action: :clone, safe: true) },
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jeste prosim upravme patricne cancancan, aby se pocitalo s clone akci

show: {
name: :show,
icon: :eye,
Expand Down
9 changes: 9 additions & 0 deletions app/controllers/folio/console/pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ def index
end
end

def clone
page = Folio::Page.find(params[:id])
@page = page.create_clone
@page.title = t("folio.console.clone.cloned_title",
original_title: page.title,
date: Date.today.strftime("%d. %m. %Y"))
render :new
end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prosim do app/controllers/concerns/folio/console/default_actions.rb, ne jen pro pages

private
def index_filters
{
Expand Down
152 changes: 152 additions & 0 deletions app/models/concerns/folio/console/clonable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# frozen_string_literal: true

module Folio::Console::Clonable
extend ActiveSupport::Concern

DEFAULT_RESET_ATTRIBUTES = [:published_at, :published]

included do
class_attribute :reference_associations, :duplicated_associations, :reset_attributes,
default: [], instance_writer: false

self.reset_attributes = DEFAULT_RESET_ATTRIBUTES
end

class_methods do
def references_original(*associations)
validate_associations!(associations)
self.reference_associations = associations
end

def duplicates_with_relations(*associations)
validate_associations!(associations)
self.duplicated_associations = associations
end

def reset_attributes_on_clone(*attributes)
validate_attributes!(attributes)
self.reset_attributes = DEFAULT_RESET_ATTRIBUTES + attributes
end

private
def validate_associations!(associations)
associations.each do |assoc|
unless reflect_on_association(assoc)
raise ArgumentError, I18n.t("activerecord.errors.clonable.association_not_found",
association: assoc,
model: self.name)
end
end
end

def validate_attributes!(attributes)
attributes.each do |attr|
unless column_names.include?(attr.to_s)
raise ArgumentError, I18n.t("activerecord.errors.clonable.attribute_not_found",
attribute: attr,
model: self.name)
end
end
end
end

def create_clone
log("CLONING", :info)
log(I18n.t("cloning.start", model: self.class.name, id: id))

clone = deep_dup
log(I18n.t("cloning.deep_dup_finished"))

copy_references(clone)
log(I18n.t("cloning.references_copied", references: self.class.reference_associations))

duplicate_nested_records(clone)
log(I18n.t("cloning.associations_duplicated", associations: self.class.duplicated_associations))

reset_clone_attributes(clone)
log(I18n.t("cloning.finished"))
clone
rescue => e
log(I18n.t("cloning.error", message: e.message), :error)
log(e.backtrace.first(5).join("\n"), :error)
raise
end

private
def reset_clone_attributes(clone)
self.class.reset_attributes.each do |attr|
clone[attr] = nil if clone.has_attribute?(attr)
end
end

def copy_references(cloned)
return unless self.class.reference_associations.present?

self.class.reference_associations.each do |assoc|
cloned.public_send("#{assoc}=", public_send(assoc))
end
end

def duplicate_nested_records(cloned)
return unless self.class.duplicated_associations.present?

self.class.duplicated_associations.each do |assoc|
cloned.public_send("#{assoc}=", clone_associated_records(assoc))
end
end

def clone_associated_records(association)
originals = public_send(association)
originals = [originals] unless originals.is_a?(ActiveRecord::Relation)

clones = originals.map do |orig|
clone = orig.deep_dup

if orig.class.reflect_on_all_associations.present?
orig.class.reflect_on_all_associations.each do |association|
clone_association(orig, clone, association)
end
end

clone
end

originals.is_a?(ActiveRecord::Relation) ? clones : clones.first
end

def clone_association(original, clone, association)
if association.macro == :has_many
clone_has_many_association(original, clone, association)
else
clone_single_association(original, clone, association)
end
end

def clone_has_many_association(original, clone, association)
return if [:files, :placements].include?(association.name)
associated_records = original.public_send(association.name)
associated_records = associated_records.map { |r| r.deep_dup } unless self.class.reference_associations.include?(association.name)
clone.association(association.name).build(associated_records.map(&:attributes))
end

def clone_single_association(original, clone, association)
associated_record = original.public_send(association.name)
return unless associated_record

associated_record_dup = self.class.reference_associations.include?(association.name) ?
associated_record :
associated_record.deep_dup

clone.public_send("#{association.name}=", associated_record_dup)
end

def log(message, level = :info)
if Rails.env.development? && Rails.logger
Rails.logger.tagged("CLONING") do
Rails.logger.public_send(level, message)
end
else
puts "[CLONING] #{message}"
end
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moc se mi nelibi, ze je to cele jako concern na model. Pridava to strasne moc metod vcetne log a nejakych dalsich, ktere jsou dost obecne nazvane.

Predstovoval bych si to jako concern, ktery prida neco jako self.is_clonable? true / false a clone metody. clone by pak uz melo pracovat skrze vlastni tridu podobne jako napr. https://github.com/sinfin/folio/blob/v6/app/lib/folio/transportable/exporter.rb a jen vratit novy neulozeny zaznam.

Zaroven by to melo fungovat vice magicky a pro vsechny zaznamy (dejme to na Folio::ApplicationRecord a cele to v default_actions zapinejme pres Rails app config klic), lze pouzit reflect_on_all_associations a spis nez whitelistovat naopak blacklistovat per-model pres nejakou self.metodu, co nechci kopirovat. A pak pridat jeste nejaky volitelny callback na model zase jako tridni metodu.

5 changes: 5 additions & 0 deletions app/models/folio/page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class Folio::Page < Folio::ApplicationRecord
include Folio::Taggable
include Folio::Transportable::Model
include PgSearch::Model
include Folio::Console::Clonable


if Rails.application.config.folio_pages_audited
include Folio::Audited
Expand Down Expand Up @@ -64,6 +66,9 @@ class Folio::Page < Folio::ApplicationRecord
presence: true
end

references_original :cover
duplicates_with_relations :atoms

# Scopes
scope :ordered, -> { order(position: :asc, created_at: :asc) }
scope :featured, -> { where(featured: true) }
Expand Down
3 changes: 3 additions & 0 deletions config/locales/activerecord.cs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ cs:
attributes:
roles:
not_available_for_site: "Role %{roles} nejsou všechny dostupné pro web '%{site}'."
clonable:
association_not_found: "Asociace '%{association}' neexistuje pro %{model}"
attribute_not_found: "Atribut '%{attribute}' neexistuje pro %{model}"
models:
folio/atom:
few: Kapitoly
Expand Down
3 changes: 3 additions & 0 deletions config/locales/activerecord.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ en:
attributes:
roles:
not_available_for_site: "Roles %{roles} are not completelly available for site '%{site}'."
clonable:
association_not_found: "Association '%{association}' does not exist for %{model}"
attribute_not_found: "Attribute '%{attribute}' does not exist for %{model}"

models:
folio/atom:
Expand Down
1 change: 1 addition & 0 deletions config/locales/console.cs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ cs:
cancel: Zrušit
confirm: Potvrdit
continue: Pokračovat
clone: Duplikovat
close: Zavřít
destroy: Smazat
discard: Archivovat
Expand Down
1 change: 1 addition & 0 deletions config/locales/console.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ en:
cancel: Cancel
confirm: Confirm
continue: Continue
clone: Clone
close: Close
destroy: Destroy
discard: Archive
Expand Down
11 changes: 11 additions & 0 deletions config/locales/cs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ cs:
accept: Ano
cancel: Ne
confirmation: Určitě?
console:
clone:
cloned_title: "%{original_title} - duplikát %{date}"

head:
description: ''
Expand All @@ -69,3 +72,11 @@ cs:
time:
formats:
folio_short: "%d. %m. %Y, %H:%M"

cloning:
start: "Začátek klonování %{model} #%{id}"
deep_dup_finished: "Deep dup dokončen"
references_copied: "Reference zkopirovány: %{references}"
associations_duplicated: "Asociace duplikovány: %{associations}"
finished: "Klonování dokončeno"
error: "Chyba při klonování: %{message}"
12 changes: 12 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ en:
content: This image contains sensitive content. To see it, verify your age. Are you over 18?
accept: Yes
cancel: No
console:
pages:
clone:
cloned_title: "%{original_title} - copy %{date}"

head:
description: ''
Expand All @@ -68,3 +72,11 @@ en:
time:
formats:
folio_short: "%d. %m. %Y, %H:%M"

cloning:
start: "Started cloning %{model} #%{id}"
deep_dup_finished: "Deep dup finished"
references_copied: "References copied: %{references}"
associations_duplicated: "Associations duplicated: %{associations}"
finished: "Cloning finished"
error: "Cloning error: %{message}"
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
get :revision, path: "revision/:version"
post :restore, path: "restore/:version"
end
get :clone
end
end

Expand Down
6 changes: 6 additions & 0 deletions test/controllers/folio/console/pages_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,10 @@ class Folio::Console::PagesControllerTest < Folio::Console::BaseControllerTest

assert_redirected_to url_for([:edit, :console, page])
end

test "clone" do
page = create(:folio_page)
get url_for([:clone, :console, page])
assert_response :success
end
end
38 changes: 38 additions & 0 deletions test/dummy/app/views/folio/console/pages/index.slim
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
= index_header

= catalogue(@catalogue_model || @pages, @catalogue_options || {})
ruby:
edit_link :title

type

locale_flag if Rails.application.config.folio_pages_locales

published_toggle

date(:published_at)

position_controls if model && model[:ancestry]

locale = (Folio::Current.site.locales.size > 1 || Rails.application.config.folio_console_add_locale_to_preview_links) ? I18n.locale : nil

if record.is_a?(Folio::Page) && record.class.try(:public?)
preview = if record.published? && !model[:ancestry]
controller.main_app.page_path(record.to_preview_param,
locale: locale)
else
controller.main_app.page_path(record.to_preview_param,
locale: locale,
Folio::Publishable::PREVIEW_PARAM_NAME => record.preview_token)
end

actions({ preview: }, :edit, :destroy, :clone)
elsif record.is_a?(Folio::Page) && record.class.try(:public_rails_path)
actions({ preview: controller.main_app.send(record.class.public_rails_path, locale: locale) },
:edit,
:destroy)
else
actions(:edit, :destroy)
end

transportable_dropdown
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nemelo by byt pri zmene default_actions potreba prepisovat

50 changes: 50 additions & 0 deletions test/models/concerns/folio/clonable_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

require "test_helper"

class Folio::ClonableTest < ActiveSupport::TestCase
test "create clone of page" do
page = create(:folio_page)

create_atom(Dummy::Atom::Contents::Text,
placement: page,
content: "Původní text")

image = create(:folio_file_image)

create_atom(Dummy::Atom::Cards::Image,
placement: page,
title: "Původní titulek",
description: "Původní popis",
url: "https://example.com",
cover: image)
page.cover = image

original_attributes = page.attributes
clone = page.create_clone

clone.title = "clone"
assert clone.valid?

assert_not_equal page.atoms, clone.atoms
assert_equal page.cover, clone.cover
assert_not_equal page.cover_placement, clone.cover_placement

clone.atoms.first.update!(content: "Změněný text")
clone.atoms.last.update!(title: "Změněný titulek", description: "Změněný popis", url: "https://example2.com")

clone.update!(
title: "Nový titulek",
perex: "Nový perex",
published_at: Time.current,
published: true,
)

page.reload
assert_equal original_attributes, page.attributes
assert_equal "Původní text", page.atoms.first.content
assert_not_equal page.atoms.first.content, clone.atoms.first.content
assert_equal image, page.atoms.second.cover
assert_equal image, clone.atoms.second.cover
end
end