diff options
Diffstat (limited to 'app/models/node.rb')
| -rw-r--r-- | app/models/node.rb | 110 |
1 files changed, 75 insertions, 35 deletions
diff --git a/app/models/node.rb b/app/models/node.rb index 6c11fed..92ecc12 100644 --- a/app/models/node.rb +++ b/app/models/node.rb | |||
| @@ -1,14 +1,14 @@ | |||
| 1 | class Node < ActiveRecord::Base | 1 | class Node < ApplicationRecord |
| 2 | # Mixins and Plugins | 2 | # Mixins and Plugins |
| 3 | acts_as_nested_set | 3 | acts_as_nested_set |
| 4 | 4 | ||
| 5 | # Associations | 5 | # Associations |
| 6 | has_many :pages, :order => "revision ASC" | 6 | has_many :pages, -> { order("revision ASC") }, :dependent => :destroy |
| 7 | belongs_to :head, :class_name => "Page", :foreign_key => :head_id | 7 | belongs_to :head, :class_name => "Page", :foreign_key => :head_id, :dependent => :destroy, optional: true |
| 8 | belongs_to :draft, :class_name => "Page", :foreign_key => :draft_id | 8 | belongs_to :draft, :class_name => "Page", :foreign_key => :draft_id, :dependent => :destroy, optional: true |
| 9 | has_many :permissions | 9 | has_many :permissions, :dependent => :destroy |
| 10 | has_one :event | 10 | has_one :event, :dependent => :destroy |
| 11 | belongs_to :lock_owner, :class_name => "User", :foreign_key => :locking_user_id | 11 | belongs_to :lock_owner, :class_name => "User", :foreign_key => :locking_user_id, optional: true |
| 12 | 12 | ||
| 13 | # Callbacks | 13 | # Callbacks |
| 14 | after_create :initialize_empty_page | 14 | after_create :initialize_empty_page |
| @@ -16,20 +16,20 @@ class Node < ActiveRecord::Base | |||
| 16 | after_save :update_unique_names_of_children | 16 | after_save :update_unique_names_of_children |
| 17 | 17 | ||
| 18 | # Validations | 18 | # Validations |
| 19 | validates_length_of :slug, :within => 1..255, :unless => "parent_id.nil?" | 19 | validates_length_of :slug, :within => 1..255, :unless => -> { parent_id.nil? } |
| 20 | validates_presence_of :slug, :unless => "parent_id.nil?" | 20 | validates_presence_of :slug, :unless => -> { parent_id.nil? } |
| 21 | validates_uniqueness_of :slug, :scope => :parent_id, :unless => "parent_id.nil?" | 21 | validates_uniqueness_of :slug, :scope => :parent_id, :unless => -> { parent_id.nil? } |
| 22 | validates_presence_of :parent_id, :unless => "Node.root.nil?" | 22 | validates_presence_of :parent_id, :unless => -> { Node.root.nil? } |
| 23 | 23 | ||
| 24 | validate :borders # This should never ever happen. | 24 | validate :borders # This should never ever happen. |
| 25 | 25 | ||
| 26 | # Index for Fulltext Search | 26 | # Index for Fulltext Search |
| 27 | define_index do | 27 | # define_index do |
| 28 | indexes head.translations.title | 28 | # indexes head.translations.title |
| 29 | indexes slug | 29 | # indexes slug |
| 30 | indexes unique_name | 30 | # indexes unique_name |
| 31 | indexes head.translations.body | 31 | # indexes head.translations.body |
| 32 | end | 32 | # end |
| 33 | 33 | ||
| 34 | # Class methods | 34 | # Class methods |
| 35 | 35 | ||
| @@ -39,8 +39,8 @@ class Node < ActiveRecord::Base | |||
| 39 | # revision with -1. It raises an Argument error if the revision is not a | 39 | # revision with -1. It raises an Argument error if the revision is not a |
| 40 | # Fixnum | 40 | # Fixnum |
| 41 | def self.find_page path, revision = -1 | 41 | def self.find_page path, revision = -1 |
| 42 | unless revision.class == Fixnum | 42 | unless revision.is_a?(Integer) |
| 43 | raise ArgumentError, "revision must be a Fixnum" | 43 | raise ArgumentError, "revision must be a Integer" |
| 44 | end | 44 | end |
| 45 | 45 | ||
| 46 | node = Node.find_by_unique_name(path) | 46 | node = Node.find_by_unique_name(path) |
| @@ -60,6 +60,7 @@ class Node < ActiveRecord::Base | |||
| 60 | # Instance Methods | 60 | # Instance Methods |
| 61 | 61 | ||
| 62 | def find_or_create_draft current_user | 62 | def find_or_create_draft current_user |
| 63 | self.wipe_draft! | ||
| 63 | if draft && self.lock_owner == current_user | 64 | if draft && self.lock_owner == current_user |
| 64 | draft | 65 | draft |
| 65 | elsif draft && self.lock_owner.nil? | 66 | elsif draft && self.lock_owner.nil? |
| @@ -72,7 +73,7 @@ class Node < ActiveRecord::Base | |||
| 72 | raise( | 73 | raise( |
| 73 | LockedByAnotherUser, | 74 | LockedByAnotherUser, |
| 74 | "Page is locked by another user who is working on it! " \ | 75 | "Page is locked by another user who is working on it! " \ |
| 75 | "Last modification: #{draft.updated_at.to_s(:db)}" | 76 | "Last modification: #{draft.updated_at.to_fs(:db)}" |
| 76 | ) | 77 | ) |
| 77 | else | 78 | else |
| 78 | lock_for! current_user | 79 | lock_for! current_user |
| @@ -94,25 +95,55 @@ class Node < ActiveRecord::Base | |||
| 94 | end | 95 | end |
| 95 | 96 | ||
| 96 | def publish_draft! | 97 | def publish_draft! |
| 98 | # Return nil if nothing to publish and no staged changes | ||
| 99 | return nil unless self.draft || staged_slug || staged_parent_id | ||
| 100 | |||
| 97 | if self.draft | 101 | if self.draft |
| 98 | self.head = self.draft | 102 | self.head = self.draft |
| 99 | self.head.published_at ||= Time.now | 103 | self.head.published_at ||= Time.now |
| 100 | self.head.save! | 104 | self.head.save! |
| 101 | |||
| 102 | self.draft = nil | 105 | self.draft = nil |
| 106 | end | ||
| 103 | 107 | ||
| 104 | if staged_slug && (staged_slug != slug) | 108 | if staged_slug && (staged_slug != slug) |
| 105 | self.slug = staged_slug | 109 | self.slug = staged_slug |
| 106 | end | 110 | self.staged_slug = nil |
| 111 | end | ||
| 107 | 112 | ||
| 108 | if staged_parent_id && (staged_parent_id != parent_id) | 113 | if staged_parent_id && (staged_parent_id != parent_id) |
| 109 | self.parent_id = staged_parent_id | 114 | new_parent = Node.find(staged_parent_id) |
| 115 | self.staged_parent_id = nil | ||
| 116 | self.save! | ||
| 117 | self.move_to_child_of(new_parent) | ||
| 118 | else | ||
| 119 | unless self.save | ||
| 120 | raise ActiveRecord::RecordInvalid.new(self) | ||
| 110 | end | 121 | end |
| 122 | end | ||
| 111 | 123 | ||
| 112 | self.save! | 124 | self.reload |
| 125 | self.update_unique_name | ||
| 126 | self.send(:update_unique_names_of_children) | ||
| 127 | self.unlock! | ||
| 128 | self | ||
| 129 | end | ||
| 130 | |||
| 131 | # removes a draft and the lock if it is older than a day and still | ||
| 132 | # identical to head | ||
| 133 | def wipe_draft! | ||
| 134 | unless self.draft | ||
| 113 | self.unlock! | 135 | self.unlock! |
| 114 | self | 136 | return |
| 115 | end | 137 | end |
| 138 | return unless self.head | ||
| 139 | return unless self.draft.updated_at < 1.day.ago | ||
| 140 | return if self.head.has_changes_to? self.draft | ||
| 141 | |||
| 142 | self.draft.destroy | ||
| 143 | self.draft_id = nil | ||
| 144 | self.unlock! | ||
| 145 | self.save! | ||
| 146 | self.reload | ||
| 116 | end | 147 | end |
| 117 | 148 | ||
| 118 | def restore_revision! revision | 149 | def restore_revision! revision |
| @@ -126,7 +157,7 @@ class Node < ActiveRecord::Base | |||
| 126 | 157 | ||
| 127 | # returns an array with all parts of a unique_name rather than a string | 158 | # returns an array with all parts of a unique_name rather than a string |
| 128 | def unique_path | 159 | def unique_path |
| 129 | unique_name.split("/") rescue [unique_name] | 160 | unique_name.to_s.split("/") |
| 130 | end | 161 | end |
| 131 | 162 | ||
| 132 | # returns array with pages up to root excluding root | 163 | # returns array with pages up to root excluding root |
| @@ -180,6 +211,15 @@ class Node < ActiveRecord::Base | |||
| 180 | self.created_at < new_id_format_date ? unique_path : id | 211 | self.created_at < new_id_format_date ? unique_path : id |
| 181 | end | 212 | end |
| 182 | 213 | ||
| 214 | # Full-text search across all locale translations using PostgreSQL tsvector. | ||
| 215 | # Uses 'simple' dictionary (no stemming, no stopwords) so queries work | ||
| 216 | # across German and English content without language detection. | ||
| 217 | def self.search(term, _ = {}) | ||
| 218 | joins(head: :translations) | ||
| 219 | .where("page_translations.search_vector @@ plainto_tsquery('simple', ?)", term) | ||
| 220 | .distinct | ||
| 221 | end | ||
| 222 | |||
| 183 | protected | 223 | protected |
| 184 | def lock_for! current_user | 224 | def lock_for! current_user |
| 185 | self.lock_owner = current_user | 225 | self.lock_owner = current_user |
| @@ -208,8 +248,12 @@ class Node < ActiveRecord::Base | |||
| 208 | # Hopefully until no childrens occur | 248 | # Hopefully until no childrens occur |
| 209 | def update_unique_names_of_children | 249 | def update_unique_names_of_children |
| 210 | unless root? | 250 | unless root? |
| 211 | self.descendants.each do |descendant| | 251 | # Use parent_id-based traversal instead of lft/rgt descendants |
| 212 | descendant.update_unique_name | 252 | # due to awesome_nested_set not refreshing parent lft/rgt in memory |
| 253 | Node.where(:parent_id => self.id).each do |child| | ||
| 254 | child.reload | ||
| 255 | child.update_unique_name | ||
| 256 | child.send(:update_unique_names_of_children) | ||
| 213 | end | 257 | end |
| 214 | end | 258 | end |
| 215 | end | 259 | end |
| @@ -220,7 +264,3 @@ class Node < ActiveRecord::Base | |||
| 220 | end | 264 | end |
| 221 | end | 265 | end |
| 222 | end | 266 | end |
| 223 | |||
| 224 | class LockedByAnotherUser < StandardError; end | ||
| 225 | |||
| 226 | |||
