diff options
Diffstat (limited to 'app/models')
| -rw-r--r-- | app/models/asset.rb | 12 | ||||
| -rw-r--r-- | app/models/node.rb | 28 | ||||
| -rw-r--r-- | app/models/user.rb | 5 |
3 files changed, 45 insertions, 0 deletions
diff --git a/app/models/asset.rb b/app/models/asset.rb index 8cec4371..b256b929 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb | |||
| @@ -41,6 +41,13 @@ class Asset < ApplicationRecord | |||
| 41 | :ids => page_ids).distinct | 41 | :ids => page_ids).distinct |
| 42 | end | 42 | end |
| 43 | 43 | ||
| 44 | # An asset's reach is the reach of the pages carrying it: destroying one | ||
| 45 | # removes it from every live page at once, so a single restricted | ||
| 46 | # attachment makes the destruction a restricted act. | ||
| 47 | def restricted? | ||
| 48 | attached_nodes.any?(&:restricted?) | ||
| 49 | end | ||
| 50 | |||
| 44 | # Witnessed destruction. Destroying an asset is a public-facing act | 51 | # Witnessed destruction. Destroying an asset is a public-facing act |
| 45 | # even when unattached. The original and its variants are publicly | 52 | # even when unattached. The original and its variants are publicly |
| 46 | # reachable under /system/uploads, so an entry is always written, | 53 | # reachable under /system/uploads, so an entry is always written, |
| @@ -49,6 +56,11 @@ class Asset < ApplicationRecord | |||
| 49 | # participates as the first non-Node subject (its participant row | 56 | # participates as the first non-Node subject (its participant row |
| 50 | # dangles after destroy, by design, the name lives on in metadata). | 57 | # dangles after destroy, by design, the name lives on in metadata). |
| 51 | def destroy_witnessed! user: | 58 | def destroy_witnessed! user: |
| 59 | if user && !user.may_change_live?(self) | ||
| 60 | errors.add(:base, :not_permitted) | ||
| 61 | raise ActiveRecord::RecordInvalid.new(self) | ||
| 62 | end | ||
| 63 | |||
| 52 | ActiveRecord::Base.transaction do | 64 | ActiveRecord::Base.transaction do |
| 53 | affected = attached_nodes.to_a | 65 | affected = attached_nodes.to_a |
| 54 | headline_losses = affected.select do |node| | 66 | headline_losses = affected.select do |node| |
diff --git a/app/models/node.rb b/app/models/node.rb index 1823daa4..ac3a6160 100644 --- a/app/models/node.rb +++ b/app/models/node.rb | |||
| @@ -236,6 +236,8 @@ class Node < ApplicationRecord | |||
| 236 | # Return nil if nothing to publish and no staged changes | 236 | # Return nil if nothing to publish and no staged changes |
| 237 | return nil unless self.draft || staged_slug || staged_parent_id | 237 | return nil unless self.draft || staged_slug || staged_parent_id |
| 238 | 238 | ||
| 239 | guard_live_change!(current_user) | ||
| 240 | |||
| 239 | if in_trash? || trash_node? | 241 | if in_trash? || trash_node? |
| 240 | errors.add(:base, :publish_in_trash) | 242 | errors.add(:base, :publish_in_trash) |
| 241 | raise ActiveRecord::RecordInvalid.new(self) | 243 | raise ActiveRecord::RecordInvalid.new(self) |
| @@ -319,6 +321,9 @@ class Node < ApplicationRecord | |||
| 319 | # at the root, carrying the leaving-public-view snapshot. | 321 | # at the root, carrying the leaving-public-view snapshot. |
| 320 | def trash! current_user = nil | 322 | def trash! current_user = nil |
| 321 | return nil if in_trash? | 323 | return nil if in_trash? |
| 324 | |||
| 325 | guard_live_change!(current_user) | ||
| 326 | |||
| 322 | if trash_node? | 327 | if trash_node? |
| 323 | errors.add(:base, :trash_the_trash) | 328 | errors.add(:base, :trash_the_trash) |
| 324 | raise ActiveRecord::RecordInvalid.new(self) | 329 | raise ActiveRecord::RecordInvalid.new(self) |
| @@ -391,6 +396,8 @@ class Node < ApplicationRecord | |||
| 391 | # One log entry at the root, per the subtree rule, written before the | 396 | # One log entry at the root, per the subtree rule, written before the |
| 392 | # rows die. | 397 | # rows die. |
| 393 | def destroy_from_trash! current_user = nil | 398 | def destroy_from_trash! current_user = nil |
| 399 | guard_live_change!(current_user) | ||
| 400 | |||
| 394 | unless in_trash? | 401 | unless in_trash? |
| 395 | errors.add(:base, :destroy_outside_trash) | 402 | errors.add(:base, :destroy_outside_trash) |
| 396 | raise ActiveRecord::RecordInvalid.new(self) | 403 | raise ActiveRecord::RecordInvalid.new(self) |
| @@ -485,6 +492,8 @@ class Node < ApplicationRecord | |||
| 485 | # Returns { :attached => n, :already => n, | 492 | # Returns { :attached => n, :already => n, |
| 486 | # :headline => nil | :set | :kept_existing | :not_eligible } | 493 | # :headline => nil | :set | :kept_existing | :not_eligible } |
| 487 | def attach_asset! asset, user:, headline: false | 494 | def attach_asset! asset, user:, headline: false |
| 495 | guard_live_change!(user) | ||
| 496 | |||
| 488 | if in_trash? || trash_node? | 497 | if in_trash? || trash_node? |
| 489 | errors.add(:base, :attach_in_trash) | 498 | errors.add(:base, :attach_in_trash) |
| 490 | raise ActiveRecord::RecordInvalid.new(self) | 499 | raise ActiveRecord::RecordInvalid.new(self) |
| @@ -565,6 +574,17 @@ class Node < ApplicationRecord | |||
| 565 | false | 574 | false |
| 566 | end | 575 | end |
| 567 | 576 | ||
| 577 | def restricted? | ||
| 578 | return true if root? | ||
| 579 | |||
| 580 | name = unique_name.to_s | ||
| 581 | return false if name.empty? | ||
| 582 | |||
| 583 | CccConventions::RESTRICTED_SUBTREES.any? do |prefix| | ||
| 584 | name == prefix || name.start_with?("#{prefix}/") | ||
| 585 | end | ||
| 586 | end | ||
| 587 | |||
| 568 | # Returns immutable node id for all new nodes so that the atom feed entry ids | 588 | # Returns immutable node id for all new nodes so that the atom feed entry ids |
| 569 | # stay the same eventhough the slug or positions changes. | 589 | # stay the same eventhough the slug or positions changes. |
| 570 | # Can be removed after a year or so ;) | 590 | # Can be removed after a year or so ;) |
| @@ -677,6 +697,14 @@ class Node < ApplicationRecord | |||
| 677 | 697 | ||
| 678 | private | 698 | private |
| 679 | 699 | ||
| 700 | def guard_live_change! user | ||
| 701 | return if user.nil? | ||
| 702 | return if user.may_change_live?(self) | ||
| 703 | |||
| 704 | errors.add(:base, :not_permitted) | ||
| 705 | raise ActiveRecord::RecordInvalid.new(self) | ||
| 706 | end | ||
| 707 | |||
| 680 | def reserved_slug_stays_reserved | 708 | def reserved_slug_stays_reserved |
| 681 | if parent&.root? && !trash_node_already_me? | 709 | if parent&.root? && !trash_node_already_me? |
| 682 | errors.add(:slug, :reserved_for_trash) if slug == CccConventions::TRASH_SLUG | 710 | errors.add(:slug, :reserved_for_trash) if slug == CccConventions::TRASH_SLUG |
diff --git a/app/models/user.rb b/app/models/user.rb index 1728521a..e8c3b9bb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb | |||
| @@ -105,6 +105,11 @@ class User < ApplicationRecord | |||
| 105 | roles.map { |r| I18n.t("users.roles.#{r}", :default => r) } | 105 | roles.map { |r| I18n.t("users.roles.#{r}", :default => r) } |
| 106 | end | 106 | end |
| 107 | 107 | ||
| 108 | def may_change_live?(subject) | ||
| 109 | return true unless subject.restricted? | ||
| 110 | redaktion? | ||
| 111 | end | ||
| 112 | |||
| 108 | def deactivate!(actor:) | 113 | def deactivate!(actor:) |
| 109 | return false if alumni? | 114 | return false if alumni? |
| 110 | transaction do | 115 | transaction do |
