From 9bb5b81fc53a23e0001218794bdd6599ef1ea37b Mon Sep 17 00:00:00 2001 From: Anna Buianova Date: Fri, 14 Oct 2022 15:46:53 +0300 Subject: [PATCH] Display information about "unpublish all" actions in the member manager (#18576) * Query audit_log + target records for display in the member manager * Display information about logged unpublished articles and comments * Display information if the post was republished * Added specs for unpublish_log tab on user's page in the member manager * Fixed specs * Change the wording for displaying info about unpublish all actions --- app/controllers/admin/users_controller.rb | 9 ++++- app/lib/constants/user_details.rb | 1 + app/queries/audit_log/unpublish_alls_query.rb | 36 +++++++++++++++++++ app/views/admin/users/show.html.erb | 8 +++-- app/views/admin/users/show/_tabs.html.erb | 3 ++ .../users/show/unpublish_logs/_index.html.erb | 29 +++++++++++++++ .../audit_log/unpublish_alls_query_spec.rb | 32 +++++++++++++++++ spec/requests/admin/users_spec.rb | 32 +++++++++++++++-- 8 files changed, 144 insertions(+), 6 deletions(-) create mode 100644 app/queries/audit_log/unpublish_alls_query.rb create mode 100644 app/views/admin/users/show/unpublish_logs/_index.html.erb create mode 100644 spec/queries/audit_log/unpublish_alls_query_spec.rb diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 081047b89..148057aa6 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -73,6 +73,7 @@ module Admin def show @user = User.find(params[:id]) set_current_tab(params[:tab]) + set_unpublish_all_log set_banishable_user set_feedback_messages set_related_reactions @@ -392,7 +393,7 @@ module Admin end def set_current_tab(current_tab = "overview") - @current_tab = if current_tab.in? Constants::UserDetails::TAB_LIST.map(&:downcase) + @current_tab = if current_tab.in? Constants::UserDetails::TAB_LIST.map(&:underscore) current_tab else "overview" @@ -403,5 +404,11 @@ module Admin @banishable_user = (@user.comments.where("created_at < ?", 100.days.ago).empty? && @user.created_at < 100.days.ago) || current_user.super_admin? || current_user.support_admin? end + + def set_unpublish_all_log + # in theory, there could be multiple "unpublish all" actions + # but let's query and display the last one for now, that should be enough for most cases + @unpublish_all_data = AuditLog::UnpublishAllsQuery.call(@user.id) + end end end diff --git a/app/lib/constants/user_details.rb b/app/lib/constants/user_details.rb index 1a85cf486..a16cdc659 100644 --- a/app/lib/constants/user_details.rb +++ b/app/lib/constants/user_details.rb @@ -6,6 +6,7 @@ module Constants Emails Reports Flags + UnpublishLogs ].freeze end end diff --git a/app/queries/audit_log/unpublish_alls_query.rb b/app/queries/audit_log/unpublish_alls_query.rb new file mode 100644 index 000000000..e9f32f92a --- /dev/null +++ b/app/queries/audit_log/unpublish_alls_query.rb @@ -0,0 +1,36 @@ +class AuditLog + class UnpublishAllsQuery + Result = Struct.new(:exists?, :audit_log, :target_articles, :target_comments, keyword_init: true) + + def initialize(user_id) + @user_id = user_id + @target_articles = [] + @target_comments = [] + end + + def self.call(...) + new(...).call + end + + def call + audit_log = AuditLog.where(slug: %w[api_user_unpublish unpublish_all_articles]) + .where("data @> '{\"target_user_id\": ?}'", user_id) + .includes(:user) + .order("created_at DESC") + .first + if audit_log + target_articles = Article.where(id: audit_log.data["target_article_ids"], user_id: user_id) + target_comments = Comment.where(id: audit_log.data["target_comment_ids"], user_id: user_id) + end + Result.new( + exists?: audit_log.present?, + audit_log: audit_log, + target_articles: target_articles, + target_comments: target_comments, + ) + end + + attr_reader :user_id + attr_accessor :target_comments, :target_articles + end +end diff --git a/app/views/admin/users/show.html.erb b/app/views/admin/users/show.html.erb index bd0309f35..3a1c3c0ab 100644 --- a/app/views/admin/users/show.html.erb +++ b/app/views/admin/users/show.html.erb @@ -1,7 +1,7 @@ <%= render "admin/users/show/profile" %> <%= render "admin/users/show/tabs" %> -<% if @current_tab == "overview" %> +<% if @current_tab == "overview" %>
<%= render "admin/users/show/overview/stats" if @user.registered %> @@ -50,8 +50,12 @@
<%= render "admin/users/show/reports/index" %>
-<% else @current_tab == "flags" %> +<% elsif @current_tab == "flags" %>
<%= render "admin/users/show/flags/index" %>
+<% elsif @current_tab == "unpublish_logs" %> +
+ <%= render "admin/users/show/unpublish_logs/index" %> +
<% end %> diff --git a/app/views/admin/users/show/_tabs.html.erb b/app/views/admin/users/show/_tabs.html.erb index 6d5c23457..e343b673f 100644 --- a/app/views/admin/users/show/_tabs.html.erb +++ b/app/views/admin/users/show/_tabs.html.erb @@ -5,5 +5,8 @@
  • <%= link_to "Emails", admin_user_path(@user.id, tab: :emails), class: "crayons-navigation__item #{'crayons-navigation__item crayons-navigation__item--current' if @current_tab == 'emails'}", aria: @current_tab == "emails" ? { current: "page" } : {} %>
  • <%= link_to "Reports", admin_user_path(@user.id, tab: :reports), class: "crayons-navigation__item #{'crayons-navigation__item crayons-navigation__item--current' if @current_tab == 'reports'}", aria: @current_tab == "reports" ? { current: "page" } : {} %>
  • <%= link_to "Flags", admin_user_path(@user.id, tab: :flags), class: "crayons-navigation__item #{'crayons-navigation__item crayons-navigation__item--current' if @current_tab == 'flags'}", aria: @current_tab == "flags" ? { current: "page" } : {} %>
  • + <% if @unpublish_all_data.exists? %> +
  • <%= link_to "Unpublish All History", admin_user_path(@user.id, tab: :unpublish_logs), class: "crayons-navigation__item #{'crayons-navigation__item crayons-navigation__item--current' if @current_tab == 'unpublish_logs'}", aria: @current_tab == "flags" ? { current: "page" } : {} %>
  • + <% end %> diff --git a/app/views/admin/users/show/unpublish_logs/_index.html.erb b/app/views/admin/users/show/unpublish_logs/_index.html.erb new file mode 100644 index 000000000..7b6127526 --- /dev/null +++ b/app/views/admin/users/show/unpublish_logs/_index.html.erb @@ -0,0 +1,29 @@ +<% if @unpublish_all_data.exists? %> +

    Unpublished by <%= link_to @unpublish_all_data.audit_log.user.username, @unpublish_all_data.audit_log.user.path %> on <%= @unpublish_all_data.audit_log.created_at.strftime("%Y-%m-%d, %H:%M") %>

    +
    +

    Posts:

    + <% @unpublish_all_data.target_articles.each do |article| %> +
    + <%= link_to article.title, article.current_state_path %> + Edit + <% if article.published %> + (was republished) + <% end %> +
    + <% end %> +
    +
    +

    Comments:

    + <% @unpublish_all_data.target_comments.each do |comment| %> +
    +
    + <%= truncate(strip_tags(comment.processed_html), length: 250).html_safe %> +
    + <%= link_to "commentable", comment.commentable.current_state_path %> +

    + Created at: <%= comment.created_at.strftime("%Y-%m-%d, %H:%M") %> +

    +
    + <% end %> +
    +<% end %> diff --git a/spec/queries/audit_log/unpublish_alls_query_spec.rb b/spec/queries/audit_log/unpublish_alls_query_spec.rb new file mode 100644 index 000000000..afb72d9af --- /dev/null +++ b/spec/queries/audit_log/unpublish_alls_query_spec.rb @@ -0,0 +1,32 @@ +require "rails_helper" + +RSpec.describe AuditLog::UnpublishAllsQuery, type: :query do + let(:user) { create(:user) } + let!(:article) { create(:article, user: user) } + + describe "::call" do + context "when audit_log exists" do + let!(:audit_log) do + create(:audit_log, slug: "unpublish_all_articles", + data: { target_user_id: user.id, target_article_ids: [article.id] }) + end + + it "has articles and audit_log in the result" do + res = described_class.call(user.id) + expect(res.target_articles).to eq([article]) + expect(res.target_comments).to eq([]) + expect(res.audit_log).to eq(audit_log) + end + + it "exists?" do + res = described_class.call(user.id) + expect(res.exists?).to be true + end + end + + it "doesn't exist when there is no related audit_log" do + res = described_class.call(user.id) + expect(res.exists?).to be false + end + end +end diff --git a/spec/requests/admin/users_spec.rb b/spec/requests/admin/users_spec.rb index f0ad26fa5..9dd0c35eb 100644 --- a/spec/requests/admin/users_spec.rb +++ b/spec/requests/admin/users_spec.rb @@ -95,6 +95,30 @@ RSpec.describe "/admin/member_manager/users", type: :request do get admin_user_path(user.id) expect(response.body).to include(report.feedback_type) end + + it "displays unpublish all data from logs when it exists on unpublish_alls tab" do + article = create(:article, user: user, published: false) + create(:audit_log, user: admin, slug: "unpublish_all_articles", + data: { target_article_ids: [article.id], target_user_id: user.id }) + get "#{admin_user_path(user.id)}?tab=unpublish_logs" + expect(response.body).to include("Unpublished by") + expect(response.body).to include(CGI.escapeHTML(article.title)) + end + + it "displays a label if an unpublished post was republished" do + article = create(:article, user: user, published: true) + create(:audit_log, user: admin, slug: "unpublish_all_articles", + data: { target_article_ids: [article.id], target_user_id: user.id }) + get "#{admin_user_path(user.id)}?tab=unpublish_logs" + expect(response.body).to include(CGI.escapeHTML(article.title)) + expect(response.body).to include("(was republished)") + end + + it "displays nothing on unpublish_alls tab if it the log doesn't exist" do + get "#{admin_user_path(user.id)}?tab=unpublish_logs" + expect(response).to be_successful + expect(response.body).not_to include("Unpublished by") + end end describe "POST /admin/member_manager/users/:id/banish" do @@ -341,9 +365,11 @@ RSpec.describe "/admin/member_manager/users", type: :request do create(:article, user: target_user, published: false) create(:comment, user: target_user, deleted: true) - sidekiq_perform_enqueued_jobs(only: Moderator::UnpublishAllArticlesWorker) do - post unpublish_all_articles_admin_user_path(target_user.id) - end + expect do + sidekiq_perform_enqueued_jobs(only: Moderator::UnpublishAllArticlesWorker) do + post unpublish_all_articles_admin_user_path(target_user.id) + end + end.to change(AuditLog, :count).by(1) log = AuditLog.last expect(log.category).to eq(AuditLog::MODERATOR_AUDIT_LOG_CATEGORY)