From cef5d1a685f324a4779ea6d18da0b0bbf6cfb7b1 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Thu, 23 Jul 2026 19:46:29 +0200 Subject: Zoom the action log on assets Asset names in summaries and publish deltas link to assets#show with an inline Chronik beside each, suppressed inside that asset's own zoom, per the node convention. assets#show gains a history button. Also renames the details summary (no longer only translations) and moves View Diff onto its own line. --- app/controllers/node_actions_controller.rb | 5 +++ app/helpers/node_actions_helper.rb | 54 ++++++++++++++++++++---- app/views/assets/show.html.erb | 3 ++ app/views/node_actions/_change_details.html.erb | 2 + config/locales/de.yml | 3 +- config/locales/en.yml | 3 +- test/controllers/node_actions_controller_test.rb | 15 +++++++ 7 files changed, 74 insertions(+), 11 deletions(-) diff --git a/app/controllers/node_actions_controller.rb b/app/controllers/node_actions_controller.rb index 9b97b450..0f547732 100644 --- a/app/controllers/node_actions_controller.rb +++ b/app/controllers/node_actions_controller.rb @@ -11,6 +11,11 @@ class NodeActionsController < ApplicationController .where(:action_participants => { :subject_type => "Node", :subject_id => params[:node_id] }) end + if params[:asset_id].present? + @actions = @actions.joins(:action_participants) + .where(:action_participants => { :subject_type => "Asset", + :subject_id => params[:asset_id] }) + end @actions = @actions.where(:user_id => params[:user_id]) if params[:user_id].present? @actions = @actions.includes(:node, :user) .paginate(:page => params[:page], :per_page => 50) diff --git a/app/helpers/node_actions_helper.rb b/app/helpers/node_actions_helper.rb index 4041ffa9..a4d13d22 100644 --- a/app/helpers/node_actions_helper.rb +++ b/app/helpers/node_actions_helper.rb @@ -44,7 +44,8 @@ module NodeActionsHelper :action => h(action.action), :subject => subject_ref(action)).html_safe end - + # Plain strings by design, safe_join in the template escapes them. + # Exception: the asset items carry zoom links and arrive pre-escaped. def action_details? action m = action.metadata return true if m["translation_diff"].present? @@ -53,7 +54,6 @@ module NodeActionsHelper abstract_changed body_changed].any? { |key| m[key].present? } end - # Plain strings by design -- safe_join in the template escapes them. def default_locale_changes action m = action.metadata items = [] @@ -70,10 +70,14 @@ module NodeActionsHelper items << t("node_actions.body_changed") if m["body_changed"] items << t("node_actions.template_changed") if m["template_changed"] if m["assets"] - items << t("node_actions.detail_assets_added", - :names => Array(m.dig("assets", "added")).join(", ")) if m.dig("assets", "added") - items << t("node_actions.detail_assets_removed", - :names => Array(m.dig("assets", "removed")).join(", ")) if m.dig("assets", "removed") + if (names = m.dig("assets", "added")) + items << t("node_actions.detail_assets_added", + :names => linked_asset_names(action, names)).html_safe + end + if (names = m.dig("assets", "removed")) + items << t("node_actions.detail_assets_removed", + :names => linked_asset_names(action, names)).html_safe + end end items << t("node_actions.assets_reordered") if m["assets_reordered"] items << t("node_actions.assets_changed") if m["assets_changed"] @@ -138,6 +142,38 @@ module NodeActionsHelper : h(action.subject_name) end + def asset_ref action + asset = action.action_participants.detect { |p| p.subject_type == "Asset" }&.subject + name = action.metadata["asset_name"].presence || action.metadata["path"] + return h(name) unless asset + + parts = [link_to(h(name), asset_path(asset))] + unless params[:asset_id].to_s == asset.id.to_s + parts << link_to(t("node_actions.asset_history"), + admin_log_path(:asset_id => asset.id), + :class => "node_action_zoom") + end + safe_join(parts, " ") + end + + def linked_asset_names action, names + by_name = action.action_participants.includes(:subject) + .select { |p| p.subject_type == "Asset" } + .filter_map(&:subject).index_by(&:name) + safe_join(names.map { |n| + asset = by_name[n] + next h(n) unless asset + + parts = [link_to(h(n), asset_path(asset))] + unless params[:asset_id].to_s == asset.id.to_s + parts << link_to(t("node_actions.asset_history"), + admin_log_path(:asset_id => asset.id), + :class => "node_action_zoom") + end + safe_join(parts, " ") + }, ", ") + end + def summarize_publish action if action.metadata["via"] == "revision" t("node_actions.publish_rollback", @@ -193,20 +229,20 @@ module NodeActionsHelper 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 + :asset => asset_ref(action)).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 + :asset => asset_ref(action)).html_safe end def summarize_asset_destroy action m = action.metadata parts = [t("node_actions.asset_destroy", :actor => actor_ref(action), - :asset => h(m["asset_name"].presence || m["path"]))] + :asset => asset_ref(action))] parts << t("node_actions.asset_destroy_detached", :paths => h(Array(m["detached_from"]).join(", "))) if m["detached_from"].present? parts << t("node_actions.asset_destroy_headlines", diff --git a/app/views/assets/show.html.erb b/app/views/assets/show.html.erb index 27238211..122b44e4 100644 --- a/app/views/assets/show.html.erb +++ b/app/views/assets/show.html.erb @@ -13,6 +13,9 @@
<%= link_to 'Back', assets_path %>
+ <%= link_to admin_log_path(:asset_id => @asset.id), :class => "action_button" do %> + <%= icon("history", library: "tabler", "aria-hidden": true) %> <%= t("node_actions.asset_history") %> + <% end %> diff --git a/app/views/node_actions/_change_details.html.erb b/app/views/node_actions/_change_details.html.erb index 066d0f30..19e2fe29 100644 --- a/app/views/node_actions/_change_details.html.erb +++ b/app/views/node_actions/_change_details.html.erb @@ -6,6 +6,7 @@ <%= I18n.default_locale.to_s.upcase %> <%= safe_join(default_items, tag.br) %> +
<% if action_entry.page && action_entry.node %> <% if (diff_params = action_entry.diff_link_params) %> <%= link_to t("node_actions.view_diff"), diff_node_revisions_path(action_entry.node, diff_params) %> @@ -21,6 +22,7 @@ <%= locale.upcase %> <%= safe_join(translation_changes(diff), tag.br) %> +
<% if action_entry.page && action_entry.node %> <% if (diff_params = action_entry.diff_link_params) %> <%= link_to t("node_actions.view_diff"), diff_node_revisions_path(action_entry.node, diff_params.merge(:locale => locale)) %> diff --git a/config/locales/de.yml b/config/locales/de.yml index b1594f19..d5ba0706 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -87,7 +87,7 @@ de: heading: "Letzte Änderungen" show_all: "Alle Einträge zeigen" backfilled: "rekonstruiert" - show_changes: "Änderungen an Übersetzungen" + show_changes: "Änderungen im Detail" view_revision: "Diese Revision ansehen" unknown: "%{actor} hat %{action} auf %{subject} ausgeführt" publish: "%{actor} hat %{revision} von %{subject} veröffentlicht" @@ -116,6 +116,7 @@ de: revision_created: "angelegt am %{date} von %{actor}" revision_published: "veröffentlicht am %{date} von %{actor}" revision_restored: "wiederhergestellt am %{date} von %{actor}" + asset_history: "Chronik" asset_create: "%{actor} hat das Asset „%{asset}“ hochgeladen" asset_attach: "%{actor} hat „%{asset}“ an %{subject} angehängt" asset_attach_headline: "%{actor} hat „%{asset}“ als Aufmacher an %{subject} angehängt" diff --git a/config/locales/en.yml b/config/locales/en.yml index 649c8812..8b4e5db4 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -39,7 +39,7 @@ en: heading: "Action log" show_all: "Show all entries" backfilled: "backfilled" - show_changes: "translation changes" + show_changes: "Change details" view_revision: "view this revision" unknown: "%{actor} did %{action} on %{subject}" publish: "%{actor} published %{revision} of %{subject}" @@ -68,6 +68,7 @@ en: revision_created: "created %{date} by %{actor}" revision_published: "published %{date} by %{actor}" revision_restored: "restored %{date} by %{actor}" + asset_history: "History" asset_destroy: "%{actor} destroyed asset “%{asset}”" asset_destroy_detached: "— detached from %{paths}" asset_destroy_headlines: "(was the headline of %{paths})" diff --git a/test/controllers/node_actions_controller_test.rb b/test/controllers/node_actions_controller_test.rb index cfc5a31b..8f0104d3 100644 --- a/test/controllers/node_actions_controller_test.rb +++ b/test/controllers/node_actions_controller_test.rb @@ -58,4 +58,19 @@ class NodeActionsControllerTest < ActionController::TestCase assert_response :success assert_includes assigns(:actions).map(&:action), "trash" end + + test "zooming on an asset finds the publishes that changed it" do + node = Node.root.children.create!(:slug => "asset_zoom_node") + asset = Asset.create!(:name => "Zoomable", :upload_content_type => "image/png") + node.publish_draft!(users(:quentin)) + node.lock_for_editing!(users(:quentin)) + node.create_new_draft(users(:quentin)) + node.draft.related_assets.create!(:asset => asset) + node.publish_draft!(users(:quentin)) + + get :index, params: { :asset_id => asset.id } + assert_response :success + assert_equal 1, assigns(:actions).count + assert_equal "publish", assigns(:actions).first.action + end end -- cgit v1.3