releaf-content 0.2.1 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +19 -21
- data/app/assets/javascripts/{releaf/controllers → controllers}/releaf/content/nodes.js +0 -0
- data/app/assets/stylesheets/{releaf/controllers → controllers}/releaf/content/nodes.scss +0 -0
- data/app/builders/releaf/content/builders/action_dialog.rb +9 -1
- data/app/builders/releaf/content/builders/tree.rb +1 -1
- data/app/builders/releaf/content/content_type_dialog_builder.rb +2 -2
- data/app/builders/releaf/content/nodes/form_builder.rb +3 -3
- data/app/builders/releaf/content/nodes/index_builder.rb +1 -1
- data/app/builders/releaf/content/nodes/toolbox_builder.rb +0 -5
- data/app/controllers/releaf/content/nodes_controller.rb +109 -129
- data/app/middleware/releaf/content/routes_reloader.rb +12 -4
- data/app/services/releaf/content/node/copy.rb +90 -0
- data/app/services/releaf/content/node/move.rb +21 -0
- data/app/services/releaf/content/node/save_under_parent.rb +22 -0
- data/app/services/releaf/content/node/service.rb +17 -0
- data/app/validators/releaf/content/node/singleness_validator.rb +1 -24
- data/lib/releaf-content.rb +85 -6
- data/lib/releaf/content/acts_as_node.rb +0 -5
- data/lib/releaf/content/acts_as_node/active_record/acts/node.rb +2 -8
- data/lib/releaf/content/configuration.rb +61 -0
- data/lib/releaf/content/engine.rb +1 -34
- data/lib/releaf/content/node.rb +30 -101
- data/lib/releaf/content/node_mapper.rb +28 -2
- data/lib/releaf/content/route.rb +37 -25
- data/spec/builders/content/nodes/action_dialog_spec.rb +39 -0
- data/spec/builders/content/nodes/form_builder_spec.rb +47 -3
- data/spec/builders/content/nodes/toolbox_builder_spec.rb +1 -32
- data/spec/controllers/releaf/content/nodes_controller_spec.rb +42 -11
- data/spec/features/nodes_services_spec.rb +207 -0
- data/spec/features/nodes_spec.rb +328 -30
- data/spec/lib/releaf/content/acts_as_node_spec.rb +4 -32
- data/spec/lib/releaf/content/configuration_spec.rb +159 -0
- data/spec/lib/releaf/content/engine_spec.rb +149 -0
- data/spec/lib/releaf/content/node_spec.rb +66 -324
- data/spec/lib/releaf/content/route_spec.rb +223 -34
- data/spec/middleware/routes_reloader_spec.rb +62 -14
- data/spec/routing/node_mapper_spec.rb +223 -55
- data/spec/services/releaf/content/node/copy_spec.rb +115 -0
- data/spec/services/releaf/content/node/move_spec.rb +20 -0
- data/spec/services/releaf/content/node/save_under_parent_spec.rb +49 -0
- data/spec/services/releaf/content/node/service_spec.rb +19 -0
- metadata +38 -21
- data/app/builders/releaf/content/go_to_dialog_builder.rb +0 -9
- data/app/views/releaf/content/nodes/go_to_dialog.ruby +0 -1
- data/lib/releaf/content/builders_autoload.rb +0 -18
- data/releaf-content.gemspec +0 -20
@@ -0,0 +1,90 @@
|
|
1
|
+
module Releaf
|
2
|
+
module Content::Node
|
3
|
+
class Copy
|
4
|
+
include Releaf::Content::Node::Service
|
5
|
+
attribute :parent_id, Integer, strict: false
|
6
|
+
|
7
|
+
def call
|
8
|
+
prevent_infinite_copy_loop
|
9
|
+
begin
|
10
|
+
new_node = nil
|
11
|
+
node.class.transaction do
|
12
|
+
new_node = make_copy
|
13
|
+
end
|
14
|
+
new_node
|
15
|
+
rescue ActiveRecord::RecordInvalid
|
16
|
+
add_error_and_raise 'descendant invalid'
|
17
|
+
else
|
18
|
+
node.update_settings_timestamp
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def prevent_infinite_copy_loop
|
23
|
+
return if node.self_and_descendants.find_by_id(parent_id).blank?
|
24
|
+
add_error_and_raise("source or descendant node can't be parent of new node")
|
25
|
+
end
|
26
|
+
|
27
|
+
def duplicate_under
|
28
|
+
new_node = nil
|
29
|
+
node.class.transaction do
|
30
|
+
new_node = node.class.new
|
31
|
+
new_node.assign_attributes_from(node)
|
32
|
+
new_node.content_id = duplicate_content.try(:id)
|
33
|
+
new_node.prevent_auto_update_settings_timestamp do
|
34
|
+
Releaf::Content::Node::SaveUnderParent.call(node: new_node, parent_id: parent_id)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
new_node
|
38
|
+
end
|
39
|
+
|
40
|
+
def duplicate_content
|
41
|
+
return if node.content_id.blank?
|
42
|
+
|
43
|
+
new_content = node.content.class.new(cloned_content_attributes)
|
44
|
+
duplicate_content_dragonfly_attributes(new_content)
|
45
|
+
|
46
|
+
new_content.save!
|
47
|
+
new_content
|
48
|
+
end
|
49
|
+
|
50
|
+
def cloned_content_attributes
|
51
|
+
skippable_attribute_names = ["id"] + content_dragonfly_attributes
|
52
|
+
node.content.attributes.except(*skippable_attribute_names)
|
53
|
+
end
|
54
|
+
|
55
|
+
def duplicate_content_dragonfly_attributes(new_content)
|
56
|
+
content_dragonfly_attributes.each do |attribute_name|
|
57
|
+
accessor_name = attribute_name.gsub("_uid", "")
|
58
|
+
dragonfly_attachment = node.content.send(accessor_name)
|
59
|
+
|
60
|
+
if dragonfly_attachment.present?
|
61
|
+
begin
|
62
|
+
dragonfly_attachment.path # verify that the file exists
|
63
|
+
rescue Dragonfly::Job::Fetch::NotFound
|
64
|
+
dragonfly_attachment = nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
new_content.send("#{attribute_name}=", nil)
|
69
|
+
new_content.send("#{accessor_name}=", dragonfly_attachment)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def content_dragonfly_attributes
|
74
|
+
node.content.class.attribute_names.select do |attribute_name|
|
75
|
+
Releaf::Builders::Utilities::ResolveAttributeFieldMethodName.new(object: node.content, attribute_name: attribute_name).file?
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def make_copy
|
80
|
+
new_node = duplicate_under
|
81
|
+
|
82
|
+
node.children.each do |child|
|
83
|
+
self.class.new(node: child, parent_id: new_node.id).make_copy
|
84
|
+
end
|
85
|
+
|
86
|
+
new_node
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Releaf
|
2
|
+
module Content::Node
|
3
|
+
class Move
|
4
|
+
include Releaf::Content::Node::Service
|
5
|
+
attribute :parent_id, Integer, strict: false
|
6
|
+
|
7
|
+
def call
|
8
|
+
return if node.parent_id.to_i == parent_id
|
9
|
+
|
10
|
+
node.class.transaction do
|
11
|
+
Releaf::Content::Node::SaveUnderParent.call(node: node, parent_id: parent_id)
|
12
|
+
|
13
|
+
node.descendants.each do |descendant_node|
|
14
|
+
next if descendant_node.valid?
|
15
|
+
add_error_and_raise("descendant invalid")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Releaf
|
2
|
+
module Content::Node
|
3
|
+
class SaveUnderParent
|
4
|
+
include Releaf::Content::Node::Service
|
5
|
+
attribute :parent_id, Integer, strict: false
|
6
|
+
|
7
|
+
def call
|
8
|
+
node.parent_id = parent_id
|
9
|
+
if node.validate_root_locale_uniqueness?
|
10
|
+
# When copying root nodes it is important to reset locale to nil.
|
11
|
+
# Later user should fill in locale. This is needed to prevent
|
12
|
+
# Rails errors about conflicting routes.
|
13
|
+
node.locale = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
node.maintain_name
|
17
|
+
node.reasign_slug
|
18
|
+
node.save!
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Releaf
|
2
|
+
module Content::Node
|
3
|
+
module Service
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
include Releaf::Service
|
6
|
+
|
7
|
+
included do
|
8
|
+
attribute :node, Releaf::Content::Node
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_error_and_raise(error)
|
12
|
+
node.errors.add(:base, error)
|
13
|
+
raise ActiveRecord::RecordInvalid.new(node)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,28 +1,5 @@
|
|
1
1
|
module Releaf
|
2
2
|
module Content::Node
|
3
|
-
# Validator to test if node is valid, when created under given parrent node
|
4
|
-
#
|
5
|
-
# validator needs :for and :under options, both can be either array of classes
|
6
|
-
# or single class.
|
7
|
-
#
|
8
|
-
# :for option specifies for which nodes validation should be applied
|
9
|
-
#
|
10
|
-
# :under option specifies under which nodes only single node of given type
|
11
|
-
# should be valid.
|
12
|
-
# If under is unspecified, then only single node with given class can be
|
13
|
-
# created under entrie content tree.
|
14
|
-
#
|
15
|
-
# @example
|
16
|
-
#
|
17
|
-
# class Node < ActiveRecord::Base
|
18
|
-
# includes Releaf::Content::Node
|
19
|
-
# validates_with Releaf::Content::Node::ParentValidator, for: [Text, Book], under: Store
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
# In the example above, Text node or book node can be added in any leve
|
23
|
-
# under Store node, but only once.
|
24
|
-
# However they can be added to any other node as many times as needed, as
|
25
|
-
# long as none of ancestors are Store node.
|
26
3
|
class SinglenessValidator < ActiveModel::Validator
|
27
4
|
|
28
5
|
def validate node
|
@@ -67,7 +44,7 @@ module Releaf
|
|
67
44
|
# return some other parent, than @node.parent.ancestors, even though
|
68
45
|
# both return same node.
|
69
46
|
# Seams like a bug in AwesomeNestedSet (in case of @node.parent.ancestors).
|
70
|
-
parent_node =
|
47
|
+
parent_node = @node.class.find(@node.parent_id)
|
71
48
|
|
72
49
|
ancestor_node = parent_node.self_and_ancestors.where(content_type: ancestor_class_names).reorder(:depth).last
|
73
50
|
if ancestor_node.nil?
|
data/lib/releaf-content.rb
CHANGED
@@ -1,6 +1,85 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
|
4
|
-
|
5
|
-
require 'releaf/content/
|
6
|
-
require 'releaf/content/
|
1
|
+
require 'awesome_nested_set'
|
2
|
+
require 'stringex'
|
3
|
+
|
4
|
+
module Releaf::Content
|
5
|
+
require 'releaf/content/engine'
|
6
|
+
require 'releaf/content/configuration'
|
7
|
+
require 'releaf/content/router_proxy'
|
8
|
+
require 'releaf/content/node_mapper'
|
9
|
+
require 'releaf/content/acts_as_node'
|
10
|
+
require 'releaf/content/node'
|
11
|
+
require 'releaf/content/route'
|
12
|
+
|
13
|
+
# expose configuration wrapper methods as class methods for easier access
|
14
|
+
# so that, for example,
|
15
|
+
# Releaf::Content.models
|
16
|
+
# can be used instead of
|
17
|
+
# Releaf::Content.configuration.models
|
18
|
+
class << self
|
19
|
+
delegate :resources, :models, :default_model, :controllers, :routing, to: :configuration
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.configuration
|
23
|
+
Releaf.application.config.content
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.configure_component
|
27
|
+
Releaf.application.config.add_configuration(
|
28
|
+
Releaf::Content::Configuration.new(
|
29
|
+
resources: { 'Node' => { controller: 'Releaf::Content::NodesController' } }
|
30
|
+
)
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.initialize_component
|
35
|
+
Rails.application.config.middleware.use Releaf::Content::RoutesReloader
|
36
|
+
|
37
|
+
ActiveSupport.on_load :action_controller do
|
38
|
+
ActionDispatch::Routing::Mapper.send(:include, Releaf::Content::NodeMapper)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.draw_component_routes(router)
|
43
|
+
resources.each do |_model_name, options|
|
44
|
+
draw_resource_routes(router, options)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.draw_resource_routes router, options
|
49
|
+
route_params = resource_route_params options
|
50
|
+
|
51
|
+
router.releaf_resources(*route_params) do
|
52
|
+
router.collection do
|
53
|
+
router.get :content_type_dialog
|
54
|
+
router.get :generate_url
|
55
|
+
end
|
56
|
+
|
57
|
+
router.member do
|
58
|
+
router.get :copy_dialog
|
59
|
+
router.post :copy
|
60
|
+
router.get :move_dialog
|
61
|
+
router.post :move
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.resource_route_params options
|
67
|
+
# Releaf::Content::NodesController -> releaf/content/nodes
|
68
|
+
controller_path = options[:controller].constantize.controller_path
|
69
|
+
controller_path_parts = controller_path.split('/')
|
70
|
+
|
71
|
+
resources_name = controller_path_parts.last
|
72
|
+
|
73
|
+
route_options = {
|
74
|
+
# releaf/content/nodes -> releaf/content
|
75
|
+
module: controller_path_parts.slice(0...-1).join('/'),
|
76
|
+
|
77
|
+
# releaf/content/nodes -> releaf_content_nodes
|
78
|
+
as: controller_path.gsub(/\//, '_'),
|
79
|
+
|
80
|
+
except: [:show]
|
81
|
+
}
|
82
|
+
|
83
|
+
[ resources_name, route_options ]
|
84
|
+
end
|
85
|
+
end
|
@@ -22,7 +22,7 @@ module ActiveRecord
|
|
22
22
|
|
23
23
|
def acts_as_node_params
|
24
24
|
if acts_as_node_configuration[:params].nil?
|
25
|
-
Releaf::
|
25
|
+
Releaf::ResourceParams.new(self).values << :id
|
26
26
|
else
|
27
27
|
acts_as_node_configuration[:params] << :id
|
28
28
|
end
|
@@ -33,7 +33,7 @@ module ActiveRecord
|
|
33
33
|
# @return [Array] list of fields to display
|
34
34
|
def acts_as_node_fields
|
35
35
|
if acts_as_node_configuration[:fields].nil?
|
36
|
-
Releaf::
|
36
|
+
Releaf::ResourceFields.new(self).values
|
37
37
|
else
|
38
38
|
acts_as_node_configuration[:fields]
|
39
39
|
end
|
@@ -43,12 +43,6 @@ module ActiveRecord
|
|
43
43
|
# All the methods available to a record that has had <tt>acts_as_node</tt> specified.
|
44
44
|
module InstanceMethods
|
45
45
|
|
46
|
-
# Return object corresponding node object
|
47
|
-
# @return [::Node]
|
48
|
-
def node
|
49
|
-
::Node.find_by(content_type: self.class.name, content_id: id)
|
50
|
-
end
|
51
|
-
|
52
46
|
end
|
53
47
|
end
|
54
48
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Releaf::Content
|
2
|
+
class Configuration
|
3
|
+
include Virtus.model(strict: true)
|
4
|
+
attribute :resources, Hash
|
5
|
+
|
6
|
+
def resources=(value)
|
7
|
+
verify_resources_config(value)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def verify_resources_config(resource_config)
|
12
|
+
# perform some basic config structure validation
|
13
|
+
unless resource_config.is_a? Hash
|
14
|
+
raise Releaf::Error, "Releaf.application.config.content.resources must be a Hash"
|
15
|
+
end
|
16
|
+
|
17
|
+
resource_config.each do | key, values |
|
18
|
+
unless key.is_a? String
|
19
|
+
raise Releaf::Error, "Releaf.application.config.content.resources must have string keys"
|
20
|
+
end
|
21
|
+
unless values.is_a? Hash
|
22
|
+
raise Releaf::Error, "#{key} in Releaf.application.config.content.resources must have a hash value"
|
23
|
+
end
|
24
|
+
unless values[:controller].is_a? String
|
25
|
+
raise Releaf::Error, "#{key} in Releaf.application.config.content.resources must have controller class specified as a string"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def models
|
31
|
+
model_names.map(&:constantize)
|
32
|
+
end
|
33
|
+
|
34
|
+
def model_names
|
35
|
+
@model_names ||= resources.keys
|
36
|
+
end
|
37
|
+
|
38
|
+
def default_model
|
39
|
+
models.first
|
40
|
+
end
|
41
|
+
|
42
|
+
def controllers
|
43
|
+
controller_names.map(&:constantize)
|
44
|
+
end
|
45
|
+
|
46
|
+
def controller_names
|
47
|
+
@controller_names ||= resources.values.map { |options| options[:controller] }
|
48
|
+
end
|
49
|
+
|
50
|
+
def routing
|
51
|
+
@routing ||= resources.map do | node_class_name, options |
|
52
|
+
routing = options[:routing] || {}
|
53
|
+
routing[:site] ||= nil
|
54
|
+
routing[:constraints] ||= nil
|
55
|
+
[ node_class_name, routing ]
|
56
|
+
end.to_h
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -1,40 +1,7 @@
|
|
1
|
-
require 'awesome_nested_set'
|
2
|
-
require 'stringex'
|
3
|
-
|
4
1
|
module Releaf::Content
|
5
|
-
require 'releaf/content/builders_autoload'
|
6
2
|
class Engine < ::Rails::Engine
|
7
3
|
initializer 'precompile', group: :all do |app|
|
8
|
-
app.config.assets.precompile += %w(
|
4
|
+
app.config.assets.precompile += %w(controllers/releaf/content/*)
|
9
5
|
end
|
10
6
|
end
|
11
|
-
|
12
|
-
def self.initialize_component
|
13
|
-
Rails.application.config.middleware.use Releaf::Content::RoutesReloader
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.draw_component_routes router
|
17
|
-
router.namespace :releaf, path: nil do
|
18
|
-
router.namespace :content, path: nil do
|
19
|
-
router.releaf_resources :nodes, except: [:show] do
|
20
|
-
router.collection do
|
21
|
-
router.get :content_type_dialog
|
22
|
-
router.get :generate_url
|
23
|
-
router.get :go_to_dialog
|
24
|
-
end
|
25
|
-
|
26
|
-
router.member do
|
27
|
-
router.get :copy_dialog
|
28
|
-
router.post :copy
|
29
|
-
router.get :move_dialog
|
30
|
-
router.post :move
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
ActiveSupport.on_load :action_controller do
|
38
|
-
ActionDispatch::Routing::Mapper.send(:include, Releaf::Content::NodeMapper)
|
39
|
-
end
|
40
7
|
end
|
data/lib/releaf/content/node.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
module Releaf::Content
|
2
2
|
module Node
|
3
3
|
extend ActiveSupport::Concern
|
4
|
-
# TODO Node should be configurable
|
5
4
|
|
6
5
|
def locale_selection_enabled?
|
7
6
|
false
|
8
7
|
end
|
9
8
|
|
10
|
-
def build_content(params = {}
|
9
|
+
def build_content(params = {})
|
11
10
|
self.content = content_class.new(params)
|
12
11
|
end
|
13
12
|
|
@@ -17,11 +16,18 @@ module Releaf::Content
|
|
17
16
|
|
18
17
|
# Return node public path
|
19
18
|
def path
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
"/" + path_parts.join("/") + (trailing_slash_for_path? ? "/" : "")
|
20
|
+
end
|
21
|
+
|
22
|
+
def path_parts
|
23
|
+
list = []
|
24
|
+
list += parent.path_parts if parent
|
25
|
+
list << slug.to_s
|
26
|
+
list
|
27
|
+
end
|
28
|
+
|
29
|
+
def trailing_slash_for_path?
|
30
|
+
Rails.application.routes.default_url_options[:trailing_slash] == true
|
25
31
|
end
|
26
32
|
|
27
33
|
def to_s
|
@@ -45,40 +51,21 @@ module Releaf::Content
|
|
45
51
|
end
|
46
52
|
|
47
53
|
def attributes_to_not_copy
|
48
|
-
%w[content_id depth id item_position lft rgt slug created_at updated_at]
|
54
|
+
list = %w[content_id depth id item_position lft rgt slug created_at updated_at]
|
55
|
+
list << "locale" if locale_before_type_cast.blank?
|
56
|
+
list
|
49
57
|
end
|
50
58
|
|
51
59
|
def attributes_to_copy
|
52
60
|
self.class.column_names - attributes_to_not_copy
|
53
61
|
end
|
54
62
|
|
55
|
-
def copy
|
56
|
-
|
57
|
-
begin
|
58
|
-
new_node = nil
|
59
|
-
self.class.transaction do
|
60
|
-
new_node = _copy!(parent_id)
|
61
|
-
end
|
62
|
-
new_node
|
63
|
-
rescue ActiveRecord::RecordInvalid
|
64
|
-
add_error_and_raise 'descendant invalid'
|
65
|
-
else
|
66
|
-
update_settings_timestamp
|
67
|
-
end
|
63
|
+
def copy(parent_id)
|
64
|
+
Releaf::Content::Node::Copy.call(node: self, parent_id: parent_id)
|
68
65
|
end
|
69
66
|
|
70
|
-
def move
|
71
|
-
|
72
|
-
|
73
|
-
self.class.transaction do
|
74
|
-
save_under(parent_id)
|
75
|
-
descendants.each do |node|
|
76
|
-
next if node.valid?
|
77
|
-
add_error_and_raise 'descendant invalid'
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
self
|
67
|
+
def move(parent_id)
|
68
|
+
Releaf::Content::Node::Move.call(node: self, parent_id: parent_id)
|
82
69
|
end
|
83
70
|
|
84
71
|
# Maintain unique name within parent_id scope.
|
@@ -118,59 +105,18 @@ module Releaf::Content
|
|
118
105
|
self_and_ancestors.where(active: false).any? == false
|
119
106
|
end
|
120
107
|
|
121
|
-
def add_error_and_raise error
|
122
|
-
errors.add(:base, error)
|
123
|
-
raise ActiveRecord::RecordInvalid.new(self)
|
124
|
-
end
|
125
|
-
|
126
|
-
def duplicate_content
|
127
|
-
return nil if content_id.blank?
|
128
|
-
|
129
|
-
new_content = content.dup
|
130
|
-
new_content.save!
|
131
|
-
new_content
|
132
|
-
end
|
133
|
-
|
134
|
-
def copy_attributes_from node
|
135
|
-
node.attributes_to_copy.each do |attribute|
|
136
|
-
send("#{attribute}=", node.send(attribute))
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
def duplicate_under! parent_id
|
141
|
-
new_node = nil
|
142
|
-
self.class.transaction do
|
143
|
-
new_node = self.class.new
|
144
|
-
new_node.copy_attributes_from self
|
145
|
-
new_node.content_id = duplicate_content.try(:id)
|
146
|
-
new_node.prevent_auto_update_settings_timestamp do
|
147
|
-
new_node.save_under(parent_id)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
new_node
|
151
|
-
end
|
152
|
-
|
153
108
|
def reasign_slug
|
154
109
|
self.slug = nil
|
155
110
|
ensure_unique_url
|
156
111
|
end
|
157
112
|
|
158
|
-
def
|
159
|
-
|
160
|
-
|
161
|
-
# When copying root nodes it is important to reset locale to nil.
|
162
|
-
# Later user should fill in locale. This is needed to prevent
|
163
|
-
# Rails errors about conflicting routes.
|
164
|
-
self.locale = nil
|
113
|
+
def assign_attributes_from(source_node)
|
114
|
+
source_node.attributes_to_copy.each do |attribute|
|
115
|
+
send("#{attribute}=", source_node.send(attribute))
|
165
116
|
end
|
166
|
-
|
167
|
-
self.item_position = self.class.children_max_item_position(self.parent) + 1
|
168
|
-
maintain_name
|
169
|
-
reasign_slug
|
170
|
-
save!
|
171
117
|
end
|
172
118
|
|
173
|
-
def prevent_auto_update_settings_timestamp
|
119
|
+
def prevent_auto_update_settings_timestamp
|
174
120
|
original = @prevent_auto_update_settings_timestamp
|
175
121
|
@prevent_auto_update_settings_timestamp = true
|
176
122
|
yield
|
@@ -178,12 +124,16 @@ module Releaf::Content
|
|
178
124
|
@prevent_auto_update_settings_timestamp = original
|
179
125
|
end
|
180
126
|
|
181
|
-
|
127
|
+
def update_settings_timestamp
|
128
|
+
self.class.updated
|
129
|
+
end
|
182
130
|
|
183
131
|
def validate_root_locale_uniqueness?
|
184
132
|
locale_selection_enabled? && root?
|
185
133
|
end
|
186
134
|
|
135
|
+
protected
|
136
|
+
|
187
137
|
def validate_parent_node_is_not_self
|
188
138
|
return if parent_id.nil?
|
189
139
|
return if parent_id.to_i != id
|
@@ -198,28 +148,10 @@ module Releaf::Content
|
|
198
148
|
|
199
149
|
private
|
200
150
|
|
201
|
-
def _copy! parent_id
|
202
|
-
new_node = duplicate_under! parent_id
|
203
|
-
|
204
|
-
children.each do |child|
|
205
|
-
child.send(:_copy!, new_node.id)
|
206
|
-
end
|
207
|
-
new_node
|
208
|
-
end
|
209
|
-
|
210
|
-
def prevent_infinite_copy_loop(parent_id)
|
211
|
-
return if self_and_descendants.find_by_id(parent_id).blank?
|
212
|
-
add_error_and_raise("source or descendant node can't be parent of new node")
|
213
|
-
end
|
214
|
-
|
215
151
|
def prevent_auto_update_settings_timestamp?
|
216
152
|
@prevent_auto_update_settings_timestamp == true
|
217
153
|
end
|
218
154
|
|
219
|
-
def update_settings_timestamp
|
220
|
-
self.class.updated
|
221
|
-
end
|
222
|
-
|
223
155
|
module ClassMethods
|
224
156
|
def updated_at
|
225
157
|
Releaf::Settings['releaf.content.nodes.updated_at']
|
@@ -254,7 +186,7 @@ module Releaf::Content
|
|
254
186
|
|
255
187
|
included do
|
256
188
|
acts_as_nested_set order_column: :item_position
|
257
|
-
acts_as_list scope: :parent_id, column: :item_position
|
189
|
+
acts_as_list scope: :parent_id, column: :item_position, add_new_at: :bottom
|
258
190
|
|
259
191
|
default_scope { order(:item_position) }
|
260
192
|
scope :active, ->() { where(active: true) }
|
@@ -266,9 +198,6 @@ module Releaf::Content
|
|
266
198
|
validates_presence_of :parent, if: :parent_id?
|
267
199
|
validate :validate_parent_node_is_not_self
|
268
200
|
validate :validate_parent_is_not_descendant
|
269
|
-
|
270
|
-
alias_attribute :to_text, :name
|
271
|
-
|
272
201
|
belongs_to :content, polymorphic: true, dependent: :destroy
|
273
202
|
accepts_nested_attributes_for :content
|
274
203
|
|