summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorerdgeist <erdgeist@erdgeist.org>2026-07-12 14:26:30 +0200
committererdgeist <erdgeist@erdgeist.org>2026-07-12 14:26:30 +0200
commit5803c59192b7fb05840d0b452eb64d9f997f3d8f (patch)
tree09ce87657484ce8508dd6fca12570c0d044f3819
parent19e0ee821d5b2b6d3397a81411f4f3a4cbd2fa83 (diff)
Extract drafts/recent query logic from controller into Node
Node.drafts_and_autosaves and Node.recently_changed replace inline query logic in NodesController#drafts/#recent -- pure refactor, no behavior change for either action. The real reason: the dashboard's upcoming abridged widgets need the exact same queries the full pages already use, just limited and (for drafts) sorted with the current user's own locked nodes first. Better to share one method than let a widget and a full page quietly drift onto two versions of "what counts as a current draft." current_user_id: is an explicit, optional argument rather than a separate method -- ordering by lock ownership only applies when a user is given; omitted entirely, it's pure recency, exactly today's behavior. Needed Arel.sql wrapping a sanitized CASE expression before .order would accept it -- sanitize_sql_array only vouches for the values inside the string, a separate Rails safety check still refuses any string shaped like more than a plain column reference unless it's explicitly marked as already-vetted.
-rw-r--r--app/controllers/nodes_controller.rb9
-rw-r--r--app/models/node.rb16
-rw-r--r--test/models/node_test.rb24
3 files changed, 42 insertions, 7 deletions
diff --git a/app/controllers/nodes_controller.rb b/app/controllers/nodes_controller.rb
index 772bf4b..c1468be 100644
--- a/app/controllers/nodes_controller.rb
+++ b/app/controllers/nodes_controller.rb
@@ -185,16 +185,11 @@ class NodesController < ApplicationController
185 185
186 # Filter functions for admin views 186 # Filter functions for admin views
187 def drafts 187 def drafts
188 base = Node.where("draft_id IS NOT NULL OR autosave_id IS NOT NULL") 188 @nodes = index_matching(Node.drafts_and_autosaves)
189 @nodes = index_matching(base)
190 end 189 end
191 190
192 def recent 191 def recent
193 base = Node.where( 192 @nodes = index_matching(Node.recently_changed)
194 "nodes.updated_at < ? AND nodes.updated_at > ? AND nodes.parent_id IS NOT NULL",
195 Time.now, Time.now - 14.days
196 )
197 @nodes = index_matching(base)
198 end 193 end
199 194
200 def mine 195 def mine
diff --git a/app/models/node.rb b/app/models/node.rb
index 24f3cd0..aa2f7f3 100644
--- a/app/models/node.rb
+++ b/app/models/node.rb
@@ -367,6 +367,22 @@ class Node < ApplicationRecord
367 .distinct 367 .distinct
368 end 368 end
369 369
370 def self.drafts_and_autosaves(current_user_id: nil)
371 scope = where("draft_id IS NOT NULL OR autosave_id IS NOT NULL")
372 return scope.order("updated_at DESC") unless current_user_id
373
374 scope.order(
375 Arel.sql(sanitize_sql_array(["CASE WHEN locking_user_id = ? THEN 0 ELSE 1 END, updated_at DESC", current_user_id]))
376 )
377 end
378
379 def self.recently_changed
380 where(
381 "nodes.updated_at < ? AND nodes.updated_at > ? AND nodes.parent_id IS NOT NULL",
382 Time.now, Time.now - 14.days
383 )
384 end
385
370 protected 386 protected
371 def lock_for! current_user 387 def lock_for! current_user
372 self.lock_owner = current_user 388 self.lock_owner = current_user
diff --git a/test/models/node_test.rb b/test/models/node_test.rb
index 15e908b..de540f8 100644
--- a/test/models/node_test.rb
+++ b/test/models/node_test.rb
@@ -537,4 +537,28 @@ class NodeTest < ActiveSupport::TestCase
537 assert_includes Node.editor_search("Backspace Spiegelgraben"), node 537 assert_includes Node.editor_search("Backspace Spiegelgraben"), node
538 assert_equal 0, Node.editor_search("Backspace Nonexistentstreet").count 538 assert_equal 0, Node.editor_search("Backspace Nonexistentstreet").count
539 end 539 end
540
541 test "drafts_and_autosaves without a user sorts by recency only" do
542 older = Node.root.children.create!(:slug => "drafts_order_older")
543 older.find_or_create_draft(@user1)
544 newer = Node.root.children.create!(:slug => "drafts_order_newer")
545 newer.find_or_create_draft(@user1)
546
547 result = Node.drafts_and_autosaves.to_a
548 assert result.index(newer) < result.index(older)
549 end
550
551 test "drafts_and_autosaves with a user puts their own locked nodes first, regardless of recency" do
552 mine = Node.root.children.create!(:slug => "drafts_order_mine")
553 mine.lock_for_editing!(@user1)
554 mine.autosave!({:title => "mine"}, @user1)
555
556 someone_elses_newer = Node.root.children.create!(:slug => "drafts_order_theirs")
557 other_user = User.find_by_login("quentin")
558 someone_elses_newer.lock_for_editing!(other_user)
559 someone_elses_newer.autosave!({:title => "theirs"}, other_user)
560
561 result = Node.drafts_and_autosaves(current_user_id: @user1.id).to_a
562 assert result.index(mine) < result.index(someone_elses_newer)
563 end
540end 564end