summaryrefslogtreecommitdiff
path: root/app/models/node.rb
diff options
context:
space:
mode:
authorerdgeist <erdgeist@erdgeist.org>2026-08-19 16:05:40 +0200
committererdgeist <erdgeist@erdgeist.org>2026-08-19 16:05:40 +0200
commit4df88b601b1900c287051d827eaff46f498f60d0 (patch)
treeb6db47fbb256dc68601f2aa31430d8636018f115 /app/models/node.rb
parentedf0d64aa2c837b4bb28787483f98cb4815601c2 (diff)
Redirect a page to a node or an external URL
Page#redirect_target resolves the precedence and returns nil for a destination that is restricted or has no head, so a page with a broken target renders itself rather than linking nowhere. The banner partial will call the same method, so the redirect and the link cannot drift. One hop, no exceptions, checked in publish_draft! rather than as a validation: two nodes publishing concurrently could each pass a save-time check and still produce a chain. "Live" means heads only, a draft redirect that has not published is not yet a link anyone can follow. Node.search excludes redirecting pages. editor_search does not: an editor looking for one searches by title, and only the body is worth hiding.
Diffstat (limited to 'app/models/node.rb')
-rw-r--r--app/models/node.rb27
1 files changed, 27 insertions, 0 deletions
diff --git a/app/models/node.rb b/app/models/node.rb
index 6fc6c3dc..52e06d2d 100644
--- a/app/models/node.rb
+++ b/app/models/node.rb
@@ -236,6 +236,32 @@ class Node < ApplicationRecord
236 raise ActiveRecord::RecordInvalid.new(self) 236 raise ActiveRecord::RecordInvalid.new(self)
237 end 237 end
238 238
239 if self.draft.redirect.present?
240 if self.draft.redirect_node_id == self.id
241 errors.add(:base, :redirect_to_self)
242 raise ActiveRecord::RecordInvalid.new(self)
243 end
244
245 if self.draft.redirect_node_id.present?
246 target = Node.find_by(:id => self.draft.redirect_node_id)
247
248 unless target
249 errors.add(:base, :redirect_target_missing)
250 raise ActiveRecord::RecordInvalid.new(self)
251 end
252
253 if target.head&.redirect.present?
254 errors.add(:base, :redirect_to_redirect)
255 raise ActiveRecord::RecordInvalid.new(self)
256 end
257 end
258
259 if Page.redirecting_to(self.id).exists?
260 errors.add(:base, :redirect_would_chain)
261 raise ActiveRecord::RecordInvalid.new(self)
262 end
263 end
264
239 path_before = self.unique_name 265 path_before = self.unique_name
240 266
241 ActiveRecord::Base.transaction do 267 ActiveRecord::Base.transaction do
@@ -602,6 +628,7 @@ class Node < ApplicationRecord
602 def self.search(term, _ = {}) 628 def self.search(term, _ = {})
603 joins(head: :translations) 629 joins(head: :translations)
604 .where("page_translations.search_vector @@ plainto_tsquery('simple', ?)", term) 630 .where("page_translations.search_vector @@ plainto_tsquery('simple', ?)", term)
631 .where(:pages => { :redirect => nil })
605 .distinct 632 .distinct
606 end 633 end
607 634