diff options
Diffstat (limited to 'app/models')
| -rw-r--r-- | app/models/asset.rb | 45 | ||||
| -rw-r--r-- | app/models/node_action.rb | 23 |
2 files changed, 19 insertions, 49 deletions
diff --git a/app/models/asset.rb b/app/models/asset.rb index 4d43b18f..08ef5a78 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb | |||
| @@ -41,44 +41,23 @@ 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 | 44 | # Witnessed destruction, refused while the asset is attached to any |
| 45 | # removes it from every live page at once, so a single restricted | 45 | # current row. Detaching stays an in-editor act, so nothing |
| 46 | # attachment makes the destruction a restricted act. | 46 | # destroyed here is carried by a page. The entry is still warranted: |
| 47 | def restricted? | 47 | # the original and its variants are reachable under /system/uploads |
| 48 | attached_nodes.any?(&:restricted?) | 48 | # until the row and its files die. |
| 49 | end | ||
| 50 | |||
| 51 | # Witnessed destruction. Destroying an asset is a public-facing act | ||
| 52 | # even when unattached. The original and its variants are publicly | ||
| 53 | # reachable under /system/uploads, so an entry is always written, | ||
| 54 | # before the row and its files die. Every currently-attached node | ||
| 55 | # participates so its zoomed history shows the loss; the asset itself | ||
| 56 | # participates as the first non-Node subject (its participant row | ||
| 57 | # dangles after destroy, by design, the name lives on in metadata). | ||
| 58 | def destroy_witnessed! user: | 49 | def destroy_witnessed! user: |
| 59 | if user && !user.may_change_live?(self) | 50 | if attached_nodes.any? |
| 60 | errors.add(:base, :not_permitted) | 51 | errors.add(:base, :destroy_while_attached) |
| 61 | raise ActiveRecord::RecordInvalid.new(self) | 52 | raise ActiveRecord::RecordInvalid.new(self) |
| 62 | end | 53 | end |
| 63 | 54 | ||
| 64 | ActiveRecord::Base.transaction do | 55 | ActiveRecord::Base.transaction do |
| 65 | affected = attached_nodes.to_a | 56 | NodeAction.record!(:participants => [self], :user => user, |
| 66 | headline_losses = affected.select do |node| | 57 | :action => "asset_destroy", |
| 67 | [node.head, node.draft, node.autosave].compact.any? do |row| | 58 | :asset_name => name, |
| 68 | row.related_assets.exists?(:asset_id => id, :headline => true) | 59 | :content_type => upload_content_type, |
| 69 | end | 60 | :path => upload.url.sub(/\?\d+$/, "")) |
| 70 | end | ||
| 71 | |||
| 72 | metadata = { | ||
| 73 | :asset_name => name, | ||
| 74 | :content_type => upload_content_type, | ||
| 75 | :path => upload.url.sub(/\?\d+$/, ""), | ||
| 76 | } | ||
| 77 | metadata[:detached_from] = affected.map(&:unique_name) if affected.any? | ||
| 78 | metadata[:headline_removed_from] = headline_losses.map(&:unique_name) if headline_losses.any? | ||
| 79 | |||
| 80 | NodeAction.record!(:participants => [self] + affected, :user => user, | ||
| 81 | :action => "asset_destroy", **metadata) | ||
| 82 | destroy! | 61 | destroy! |
| 83 | end | 62 | end |
| 84 | end | 63 | end |
diff --git a/app/models/node_action.rb b/app/models/node_action.rb index 1e64861e..aab1d238 100644 --- a/app/models/node_action.rb +++ b/app/models/node_action.rb | |||
| @@ -82,22 +82,13 @@ class NodeAction < ApplicationRecord | |||
| 82 | # "asset_create" (witnessed upload; participants: the asset alone): | 82 | # "asset_create" (witnessed upload; participants: the asset alone): |
| 83 | # "asset_name", "content_type", "path" -- flat strings | 83 | # "asset_name", "content_type", "path" -- flat strings |
| 84 | # | 84 | # |
| 85 | # "asset_attach" (out-of-band attach via Node#attach_asset!; written | 85 | # "asset_destroy" (witnessed asset deletion; the files were publicly |
| 86 | # only when at least one new join was created, per the tandem rule -- | 86 | # reachable, so an entry is always written. Destruction is refused |
| 87 | # in-editor curation stays draft-scoped and surfaces at publish. | 87 | # while the asset is attached to any current row, so no node is ever |
| 88 | # participants: the node (primary) and the asset): | 88 | # affected: node column nil, the asset the sole participant): |
| 89 | # "asset_name", "path" -- flat strings | 89 | # "asset_name" -- flat string |
| 90 | # "headline" -- boolean, only when set by this attach | 90 | # "content_type" -- flat string |
| 91 | # | 91 | # "path" -- public original path, flat string |
| 92 | # "asset_destroy" (witnessed asset deletion; always written, even for | ||
| 93 | # unattached assets -- the files were publicly reachable; node column | ||
| 94 | # nil, subjects via participants: the asset plus every then-attached | ||
| 95 | # node): | ||
| 96 | # "asset_name" -- flat string | ||
| 97 | # "content_type" -- flat string | ||
| 98 | # "path" -- public original path, flat string | ||
| 99 | # "detached_from" -- array of unique_names, only when any | ||
| 100 | # "headline_removed_from" -- array of unique_names, only when any | ||
| 101 | # | 92 | # |
| 102 | # "otp_enroll" / "otp_disable" / "otp_reset" (second-factor | 93 | # "otp_enroll" / "otp_disable" / "otp_reset" (second-factor |
| 103 | # lifecycle) and "user_create" / "user_deactivate" / | 94 | # lifecycle) and "user_create" / "user_deactivate" / |
