summaryrefslogtreecommitdiff
path: root/app/models/node.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/node.rb')
-rw-r--r--app/models/node.rb62
1 files changed, 62 insertions, 0 deletions
diff --git a/app/models/node.rb b/app/models/node.rb
index dc69508..a2e9981 100644
--- a/app/models/node.rb
+++ b/app/models/node.rb
@@ -19,6 +19,7 @@ class Node < ApplicationRecord
19 before_save :check_for_changed_slug 19 before_save :check_for_changed_slug
20 after_save :update_unique_names_of_children 20 after_save :update_unique_names_of_children
21 before_destroy :refuse_destroy_with_children 21 before_destroy :refuse_destroy_with_children
22 before_destroy :refuse_destroying_trash_node
22 23
23 # Validations 24 # Validations
24 validates_length_of :slug, :within => 1..255, :unless => -> { parent_id.nil? || slug.blank? } 25 validates_length_of :slug, :within => 1..255, :unless => -> { parent_id.nil? || slug.blank? }
@@ -26,6 +27,8 @@ class Node < ApplicationRecord
26 validates_uniqueness_of :slug, :scope => :parent_id, :unless => -> { parent_id.nil? } 27 validates_uniqueness_of :slug, :scope => :parent_id, :unless => -> { parent_id.nil? }
27 validates_presence_of :parent_id, :unless => -> { Node.root.nil? } 28 validates_presence_of :parent_id, :unless => -> { Node.root.nil? }
28 29
30 validate :reserved_slug_stays_reserved
31 validate :no_head_inside_trash
29 validates :default_template_name, 32 validates :default_template_name,
30 :inclusion => { :in => ->(_) { Page.custom_templates } }, 33 :inclusion => { :in => ->(_) { Page.custom_templates } },
31 :allow_nil => true, 34 :allow_nil => true,
@@ -57,6 +60,15 @@ class Node < ApplicationRecord
57 nil 60 nil
58 end 61 end
59 62
63 # The Trash container node. Lazily self-creating and idempotent, so
64 # every environment acquires it on first touch.
65 # Never call from validations. the positional predicates below
66 # exist for that.
67 def self.trash
68 root.children.find_by(:slug => CccConventions::TRASH_SLUG) ||
69 root.children.create!(:slug => CccConventions::TRASH_SLUG)
70 end
71
60 # Instance Methods 72 # Instance Methods
61 73
62 # Acquires (or reaffirms) the editing lock without creating a draft or 74 # Acquires (or reaffirms) the editing lock without creating a draft or
@@ -203,6 +215,10 @@ class Node < ApplicationRecord
203 # Return nil if nothing to publish and no staged changes 215 # Return nil if nothing to publish and no staged changes
204 return nil unless self.draft || staged_slug || staged_parent_id 216 return nil unless self.draft || staged_slug || staged_parent_id
205 217
218 if in_trash? || trash_node?
219 raise ActiveRecord::RecordInvalid.new(self), "Cannot publish a node in the Trash"
220 end
221
206 path_before = self.unique_name 222 path_before = self.unique_name
207 223
208 ActiveRecord::Base.transaction do 224 ActiveRecord::Base.transaction do
@@ -322,6 +338,20 @@ class Node < ApplicationRecord
322 autosave || draft || head 338 autosave || draft || head
323 end 339 end
324 340
341 def trash_node?
342 parent&.root? && slug == CccConventions::TRASH_SLUG
343 end
344
345 # Inside the Trash subtree. False for the Trash node itself.
346 def in_trash?
347 current = parent
348 while current
349 return true if current.trash_node?
350 current = current.parent
351 end
352 false
353 end
354
325 # Returns immutable node id for all new nodes so that the atom feed entry ids 355 # Returns immutable node id for all new nodes so that the atom feed entry ids
326 # stay the same eventhough the slug or positions changes. 356 # stay the same eventhough the slug or positions changes.
327 # Can be removed after a year or so ;) 357 # Can be removed after a year or so ;)
@@ -435,4 +465,36 @@ class Node < ApplicationRecord
435 end 465 end
436 end 466 end
437 end 467 end
468
469 private
470
471 def reserved_slug_stays_reserved
472 if parent&.root? && !trash_node_already_me?
473 errors.add(:slug, "is reserved for the Trash") if slug == CccConventions::TRASH_SLUG
474 errors.add(:staged_slug, "is reserved for the Trash") if staged_slug == CccConventions::TRASH_SLUG
475 end
476
477 if persisted? && slug_was == CccConventions::TRASH_SLUG && Node.find(id).trash_node?
478 errors.add(:slug, "of the Trash node cannot change") if slug_changed?
479 errors.add(:parent_id, "of the Trash node cannot change") if parent_id_changed?
480 errors.add(:staged_slug, "must stay empty on the Trash node") if staged_slug.present?
481 errors.add(:staged_parent_id, "must stay empty on the Trash node") if staged_parent_id.present?
482 end
483 end
484
485 def trash_node_already_me?
486 existing = Node.root&.children&.find_by(:slug => CccConventions::TRASH_SLUG)
487 existing.nil? || existing.id == id
488 end
489
490 def no_head_inside_trash
491 return unless head_id.present?
492 errors.add(:head_id, "cannot exist inside the Trash") if in_trash? || trash_node?
493 end
494
495 def refuse_destroying_trash_node
496 return unless trash_node?
497 errors.add(:base, "The Trash node cannot be destroyed")
498 throw :abort
499 end
438end 500end