From 190daaa8a0bb79e750894b83e31b9d99f7d900a4 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Thu, 16 Jul 2026 03:30:01 +0200 Subject: Add a reader for the action log at admin/log NodeActionsController#index lists entries newest-first, filterable by node_id or user_id -- the two zoom shapes the log was designed around. Rendering goes through NodeActionsHelper.action_summary, which builds one sentence per entry from metadata alone, so entries referencing deleted users or nodes render from their snapshots; live associations only upgrade names to links. Unknown verbs degrade to a generic sentence rather than an error, since the log outlives its vocabulary. The helper is the escaping boundary: every metadata value passes through h() before assembly. Actor names link to the log's own user zoom rather than the unused users page -- inspecting a suspicious user's other actions is the intended workflow. Publish entries with a translation_diff expose a collapsed per-locale change table linking out to the revision itself. Sentences live in en.yml/de.yml following the existing widget-string convention. nodes#show links to its node's zoomed log. --- app/controllers/node_actions_controller.rb | 14 +++++ app/helpers/node_actions_helper.rb | 68 +++++++++++++++++++++++++ app/views/node_actions/_change_details.html.erb | 27 ++++++++++ app/views/node_actions/index.html.erb | 22 ++++++++ app/views/nodes/show.html.erb | 1 + 5 files changed, 132 insertions(+) create mode 100644 app/controllers/node_actions_controller.rb create mode 100644 app/helpers/node_actions_helper.rb create mode 100644 app/views/node_actions/_change_details.html.erb create mode 100644 app/views/node_actions/index.html.erb (limited to 'app') diff --git a/app/controllers/node_actions_controller.rb b/app/controllers/node_actions_controller.rb new file mode 100644 index 0000000..6e46719 --- /dev/null +++ b/app/controllers/node_actions_controller.rb @@ -0,0 +1,14 @@ +class NodeActionsController < ApplicationController + + before_action :login_required + + layout 'admin' + + def index + @actions = NodeAction.order(:occurred_at => :desc, :id => :desc) + @actions = @actions.where(:node_id => params[:node_id]) if params[:node_id].present? + @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) + end +end diff --git a/app/helpers/node_actions_helper.rb b/app/helpers/node_actions_helper.rb new file mode 100644 index 0000000..ef42b75 --- /dev/null +++ b/app/helpers/node_actions_helper.rb @@ -0,0 +1,68 @@ +module NodeActionsHelper + include ERB::Util + + # One sentence per entry, rendered from metadata alone, so entries + # stay renderable after the rows they reference are gone. Live + # associations only upgrade plain names to links. Every metadata + # value passes through h() here -- this helper is the escaping + # boundary; the locale sentences are trusted, the data never is. + def action_summary action + renderer = "summarize_#{action.action}" + return send(renderer, action) if respond_to?(renderer, true) + + # Unknown-verb fallback: the log outlives the vocabulary. A renamed + # or future verb degrades to an ugly sentence, never to a 500. + t("node_actions.unknown", :actor => actor_ref(action), + :action => h(action.action), :subject => subject_ref(action)).html_safe + end + + private + + def actor_ref action + if action.user && respond_to?(:current_user) && action.user == current_user + return t("node_actions.actor_self") + end + action.user ? link_to(h(action.actor_name), admin_log_path(:user_id => action.user_id)) + : h(action.actor_name) + end + + def subject_ref action + action.node ? link_to(h(action.subject_name), node_path(action.node)) + : h(action.subject_name) + end + + def summarize_publish action + key = if action.metadata["via"] == "revision" + "node_actions.publish_rollback" + elsif action.metadata.dig("title", "from").nil? + "node_actions.publish_first" + else + "node_actions.publish" + end + + t(key, :actor => actor_ref(action), :subject => subject_ref(action), + :from => h(action.metadata.dig("title", "from")), + :to => h(action.metadata.dig("title", "to"))).html_safe + end + + def summarize_move action + t("node_actions.move", :actor => actor_ref(action), :subject => subject_ref(action), + :from => h(action.metadata.dig("path", "from")), + :to => h(action.metadata.dig("path", "to"))).html_safe + end + + def summarize_create action + t("node_actions.create", :actor => actor_ref(action), :subject => subject_ref(action), + :path => h(action.metadata["path"])).html_safe + end + + def summarize_discard_autosave action + t("node_actions.discard_autosave", + :actor => actor_ref(action), :subject => subject_ref(action)).html_safe + end + + def summarize_destroy_draft action + t("node_actions.destroy_draft", + :actor => actor_ref(action), :subject => subject_ref(action)).html_safe + end +end diff --git a/app/views/node_actions/_change_details.html.erb b/app/views/node_actions/_change_details.html.erb new file mode 100644 index 0000000..383f53a --- /dev/null +++ b/app/views/node_actions/_change_details.html.erb @@ -0,0 +1,27 @@ +
+ <%= t("node_actions.show_changes") %> + + <% action_entry.metadata["translation_diff"].each do |locale, diff| %> + + + + + <% end %> +
<%= locale.upcase %> + <% case diff["status"] %> + <% when "added" %> + <%= t("node_actions.locale_added", :title => diff.dig("title", "to")) %> + <% when "removed" %> + <%= t("node_actions.locale_removed", :title => diff.dig("title", "from")) %> + <% else %> + <% if diff["title"] %> + <%= diff.dig("title", "from") %> → <%= diff.dig("title", "to") %> + <% end %> + <%= t("node_actions.abstract_changed") if diff["abstract_changed"] %> + <%= t("node_actions.body_changed") if diff["body_changed"] %> + <% end %> +
+ <% if action_entry.page && action_entry.node %> + <%= link_to t("node_actions.view_revision"), node_revision_path(action_entry.node, action_entry.page) %> + <% end %> +
diff --git a/app/views/node_actions/index.html.erb b/app/views/node_actions/index.html.erb new file mode 100644 index 0000000..92740cd --- /dev/null +++ b/app/views/node_actions/index.html.erb @@ -0,0 +1,22 @@ +

<%= t("node_actions.heading") %>

+ +<% if params[:node_id].present? || params[:user_id].present? %> +

<%= link_to t("node_actions.show_all"), admin_log_path %>

+<% end %> + +<%= will_paginate @actions %> + + + +<%= will_paginate @actions %> diff --git a/app/views/nodes/show.html.erb b/app/views/nodes/show.html.erb index 66f04f9..d0eff60 100644 --- a/app/views/nodes/show.html.erb +++ b/app/views/nodes/show.html.erb @@ -183,6 +183,7 @@ +

<%= link_to t("node_actions.heading"), admin_log_path(:node_id => @node.id) %>

-- cgit v1.3