From 932d4a4be40587fa6d489eca16a1ef6d6f2936d1 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Thu, 23 Jul 2026 12:24:52 +0200 Subject: Witness asset uploads and out-of-band attaches in the action log --- app/controllers/assets_controller.rb | 5 +++++ app/helpers/node_actions_helper.rb | 15 +++++++++++++++ app/models/node.rb | 8 ++++++++ app/models/node_action.rb | 11 +++++++++++ 4 files changed, 39 insertions(+) (limited to 'app') diff --git a/app/controllers/assets_controller.rb b/app/controllers/assets_controller.rb index 8df4c94d..7edd9c05 100644 --- a/app/controllers/assets_controller.rb +++ b/app/controllers/assets_controller.rb @@ -50,6 +50,11 @@ class AssetsController < ApplicationController respond_to do |format| if @asset.save flash[:notice] = 'Asset was successfully created.' + NodeAction.record!(:participants => [@asset], :user => current_user, + :action => "asset_create", + :asset_name => @asset.name, + :content_type => @asset.upload_content_type, + :path => @asset.upload.url.sub(/\?\d+$/, "")) attach_to(attach_node) if attach_node format.html { redirect_to(@asset) } format.xml { render :xml => @asset, :status => :created, :location => @asset } diff --git a/app/helpers/node_actions_helper.rb b/app/helpers/node_actions_helper.rb index 02d1ba83..ed8d0407 100644 --- a/app/helpers/node_actions_helper.rb +++ b/app/helpers/node_actions_helper.rb @@ -13,6 +13,9 @@ module NodeActionsHelper "destroy" => "trash-x", "discard_autosave" => "eraser", "destroy_draft" => "eraser", + "asset_create" => "upload", + "asset_attach" => "paperclip", + "asset_destroy" => "file-x" }.freeze def verb_icon action @@ -181,6 +184,18 @@ module NodeActionsHelper :path => h(action.metadata["path"])).html_safe end + def summarize_asset_create action + t("node_actions.asset_create", :actor => actor_ref(action), + :asset => h(action.metadata["asset_name"].presence || action.metadata["path"])).html_safe + end + + def summarize_asset_attach action + m = action.metadata + key = m["headline"] ? "node_actions.asset_attach_headline" : "node_actions.asset_attach" + t(key, :actor => actor_ref(action), :subject => subject_ref(action), + :asset => h(m["asset_name"].presence || m["path"])).html_safe + end + def summarize_asset_destroy action m = action.metadata parts = [t("node_actions.asset_destroy", :actor => actor_ref(action), diff --git a/app/models/node.rb b/app/models/node.rb index 7a93e799..0a9cd2d1 100644 --- a/app/models/node.rb +++ b/app/models/node.rb @@ -501,6 +501,14 @@ class Node < ApplicationRecord to_attach.each do |row| row.related_assets.create!(:asset => asset, :headline => headline_state == :set) end + + if to_attach.any? + metadata = { :asset_name => asset.name, + :path => asset.upload.url.sub(/\?\d+$/, "") } + metadata[:headline] = true if headline_state == :set + NodeAction.record!(:node => self, :participants => [self, asset], + :user => user, :action => "asset_attach", **metadata) + end end { :attached => to_attach.size, diff --git a/app/models/node_action.rb b/app/models/node_action.rb index 9ed0b628..aa52f489 100644 --- a/app/models/node_action.rb +++ b/app/models/node_action.rb @@ -69,6 +69,17 @@ class NodeAction < ApplicationRecord # "path" -- final path, flat string (create-symmetric) # "destroyed_descendants" -- integer, only when positive; one entry # at the root, per the subtree rule. + # + # "asset_create" (witnessed upload; participants: the asset alone): + # "asset_name", "content_type", "path" -- flat strings + # + # "asset_attach" (out-of-band attach via Node#attach_asset!; written + # only when at least one new join was created, per the tandem rule -- + # in-editor curation stays draft-scoped and surfaces at publish. + # participants: the node (primary) and the asset): + # "asset_name", "path" -- flat strings + # "headline" -- boolean, only when set by this attach + # # "asset_destroy" (witnessed asset deletion; always written, even for # unattached assets -- the files were publicly reachable; node column # nil, subjects via participants: the asset plus every then-attached -- cgit v1.3