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.rb89
1 files changed, 74 insertions, 15 deletions
diff --git a/app/models/node.rb b/app/models/node.rb
index a440c2f..7a93e79 100644
--- a/app/models/node.rb
+++ b/app/models/node.rb
@@ -4,7 +4,14 @@ class Node < ApplicationRecord
4 4
5 # Associations 5 # Associations
6 has_many :pages, -> { order("revision ASC") }, :dependent => :destroy 6 has_many :pages, -> { order("revision ASC") }, :dependent => :destroy
7
8 # Entries where this node is the primary subject (fast-path column).
9 # For a *complete* history -- including subtree trash/destroy entries
10 # recorded at an ancestor -- use participated_actions instead.
7 has_many :node_actions, :dependent => :nullify 11 has_many :node_actions, :dependent => :nullify
12 has_many :action_participations, :class_name => "ActionParticipant", :as => :subject
13 has_many :participated_actions, :through => :action_participations, :source => :node_action
14
8 belongs_to :head, :class_name => "Page", :foreign_key => :head_id, optional: true 15 belongs_to :head, :class_name => "Page", :foreign_key => :head_id, optional: true
9 belongs_to :draft, :class_name => "Page", :foreign_key => :draft_id, optional: true 16 belongs_to :draft, :class_name => "Page", :foreign_key => :draft_id, optional: true
10 # Autosave pages carry no node_id, so has_many :pages does not cover 17 # Autosave pages carry no node_id, so has_many :pages does not cover
@@ -312,14 +319,14 @@ class Node < ApplicationRecord
312 was_published = head_id.present? 319 was_published = head_id.present?
313 final_published_at = head&.published_at 320 final_published_at = head&.published_at
314 321
315 demoted = 0 322 subtree = [self] + descendants.to_a
316 ([self] + descendants.to_a).each do |node| 323 demoted_nodes = subtree.select do |node|
317 next unless node.head_id 324 next false unless node.head_id
318 former = node.head 325 former = node.head
319 node.head = nil 326 node.head = nil
320 node.draft_id = former.id if node.draft_id.nil? 327 node.draft_id = former.id if node.draft_id.nil?
321 node.save! 328 node.save!
322 demoted += 1 329 true
323 end 330 end
324 331
325 self.reload 332 self.reload
@@ -332,9 +339,10 @@ class Node < ApplicationRecord
332 metadata = { :path => { "from" => path_before, "to" => unique_name } } 339 metadata = { :path => { "from" => path_before, "to" => unique_name } }
333 metadata[:was_published] = true if was_published 340 metadata[:was_published] = true if was_published
334 metadata[:final_published_at] = final_published_at.iso8601 if final_published_at 341 metadata[:final_published_at] = final_published_at.iso8601 if final_published_at
335 metadata[:demoted_heads] = demoted if demoted > 0 342 metadata[:demoted_heads] = demoted_nodes.size if demoted_nodes.any?
336 343
337 NodeAction.record!(:node => self, :user => current_user, :action => "trash", **metadata) 344 NodeAction.record!(:participants => subtree, :user => current_user,
345 :action => "trash", **metadata)
338 self 346 self
339 end 347 end
340 end 348 end
@@ -382,7 +390,8 @@ class Node < ApplicationRecord
382 metadata = { :path => unique_name } 390 metadata = { :path => unique_name }
383 metadata[:destroyed_descendants] = doomed.size - 1 if doomed.size > 1 391 metadata[:destroyed_descendants] = doomed.size - 1 if doomed.size > 1
384 392
385 NodeAction.record!(:node => self, :user => current_user, :action => "destroy", **metadata) 393 NodeAction.record!(:participants => doomed, :user => current_user,
394 :action => "destroy", **metadata)
386 doomed.each(&:destroy!) 395 doomed.each(&:destroy!)
387 end 396 end
388 end 397 end
@@ -442,8 +451,65 @@ class Node < ApplicationRecord
442 end 451 end
443 end 452 end
444 453
454 # Attaches an asset to every current lifecycle row -- head, draft and
455 # autosave -- that does not already carry it. Attachments are page-
456 # scoped content (RelatedAsset belongs_to :page; drafts and autosaves
457 # are wholesale clones), so attaching to a single layer is how an
458 # attachment gets silently lost when another layer replaces it at
459 # publish or save. This is the out-of-band counterpart to the
460 # in-editor attach UI; it refuses when someone else holds the editing
461 # lock. Attaching to a head row changes the public page immediately,
462 # by design -- same reasoning as formalizing an already-existing
463 # editorial link.
464 #
465 # headline is a node-level decision: the flag is set on the newly
466 # created joins only when no current row has a headline yet and the
467 # asset is eligible; otherwise the asset is attached plain and the
468 # result says why, so the caller can point the editor at the star in
469 # the editor instead.
470 #
471 # Returns { :attached => n, :already => n,
472 # :headline => nil | :set | :kept_existing | :not_eligible }
473 def attach_asset! asset, user:, headline: false
474 if in_trash? || trash_node?
475 raise ActiveRecord::RecordInvalid.new(self), "Cannot attach assets to a node in the Trash"
476 end
477
478 if lock_owner && lock_owner != user
479 raise(
480 LockedByAnotherUser,
481 "Page is locked by another user who is working on it! " \
482 "Last modification: #{(self.autosave || self.draft || self.head).updated_at.to_fs(:db)}"
483 )
484 end
485
486 rows = [head, draft, autosave].compact
487 to_attach = rows.reject { |row| row.related_assets.exists?(:asset_id => asset.id) }
488
489 headline_state =
490 if headline && to_attach.any?
491 if !(asset.image? || asset.pdf?)
492 :not_eligible
493 elsif rows.any? { |row| row.headline_asset.present? }
494 :kept_existing
495 else
496 :set
497 end
498 end
499
500 ActiveRecord::Base.transaction do
501 to_attach.each do |row|
502 row.related_assets.create!(:asset => asset, :headline => headline_state == :set)
503 end
504 end
505
506 { :attached => to_attach.size,
507 :already => rows.size - to_attach.size,
508 :headline => headline_state }
509 end
510
445 def title 511 def title
446 head ? head.title : draft.title 512 editable_page&.title
447 end 513 end
448 514
449 def update_unique_names? 515 def update_unique_names?
@@ -533,13 +599,6 @@ class Node < ApplicationRecord
533 throw :abort 599 throw :abort
534 end 600 end
535 601
536 def self.recently_changed
537 includes(:head).where(
538 "pages.updated_at < ? AND pages.updated_at > ? AND nodes.parent_id IS NOT NULL",
539 Time.now, Time.now - 14.days
540 ).order("pages.updated_at desc").references(:head)
541 end
542
543 protected 602 protected
544 def lock_for! current_user 603 def lock_for! current_user
545 self.lock_owner = current_user 604 self.lock_owner = current_user