post events: show post-related mod actions on post event page.
Show the following actions on the post events page: * Post bans and unbans * Post deletions and undeletions * Thumbnail regenerations and IQDB regenerations * Favorites moves * Rating locks and unlocks * Note locks and unlocks Fixes #3825: Events/Moderation page for each post should show eventual ban actions
This commit is contained in:
@@ -6,16 +6,40 @@ class PostEvent < ApplicationRecord
|
||||
belongs_to :post
|
||||
|
||||
def self.model_types
|
||||
%w[Post PostAppeal PostApproval PostDisapproval PostFlag PostReplacement]
|
||||
%w[Post PostAppeal PostApproval PostDisapproval PostFlag PostReplacement ModAction]
|
||||
end
|
||||
|
||||
def self.categories
|
||||
# model_types.excluding("ModAction") + ModAction.categories.keys.grep(/\Apost_(?!permanent_delete|vote)/).map(&:camelize)
|
||||
%w[Upload Flag Appeal Approval Disapproval Delete Undelete Ban Unban Replacement Regenerate RegenerateIqdb MoveFavorites NoteLockCreate NoteLockDelete RatingLockCreate RatingLockDelete]
|
||||
end
|
||||
|
||||
def self.visible(user)
|
||||
all
|
||||
end
|
||||
|
||||
def self.category_matches(category)
|
||||
category = category.squish.titleize.delete(" ")
|
||||
|
||||
case category
|
||||
when "Upload"
|
||||
where(model_type: "Post")
|
||||
when "Flag", "Appeal", "Approval", "Disapproval", "Replacement"
|
||||
where(model_type: "Post" + category)
|
||||
when *categories
|
||||
where(model: ModAction.where(category: "post_" + category.underscore))
|
||||
else
|
||||
none
|
||||
end
|
||||
end
|
||||
|
||||
def self.search(params, current_user)
|
||||
q = search_attributes(params, [:model, :post, :creator, :event_at], current_user: current_user)
|
||||
|
||||
if params[:category]
|
||||
q = q.category_matches(params[:category])
|
||||
end
|
||||
|
||||
case params[:order]
|
||||
when "event_at_asc"
|
||||
q = q.order(event_at: :asc, model_id: :asc)
|
||||
@@ -34,6 +58,20 @@ class PostEvent < ApplicationRecord
|
||||
[:post, :model] # XXX creator isn't included because it leaks flagger/disapprover names
|
||||
end
|
||||
|
||||
def category
|
||||
if model_type == "Post"
|
||||
"Upload"
|
||||
elsif model_type == "ModAction"
|
||||
model.category.camelize.delete_prefix("Post")
|
||||
else
|
||||
model_type.delete_prefix("Post")
|
||||
end
|
||||
end
|
||||
|
||||
def pretty_category
|
||||
category.titleize.delete_prefix("Post ")
|
||||
end
|
||||
|
||||
def readonly?
|
||||
true
|
||||
end
|
||||
|
||||
@@ -24,8 +24,13 @@ class PostEventPolicy < ApplicationPolicy
|
||||
attr = attr.to_s.gsub("creator", "uploader").to_sym if type == "Post"
|
||||
attr = attr.to_s.gsub("creator", "user").to_sym if type in "PostApproval" | "PostDisapproval"
|
||||
|
||||
if type == "ModAction"
|
||||
# XXX don't apply visible_for_search to mod actions because it's slow and we know all mod actions are visible
|
||||
events.where(model_type: "ModAction")
|
||||
else
|
||||
# XXX ordering by created_at desc is a query planner hack to make Postgres use the right indexes.
|
||||
events.where(model: type.constantize.visible_for_search(attr, user).order(created_at: :desc))
|
||||
end
|
||||
end.reduce(:or)
|
||||
else
|
||||
events
|
||||
|
||||
@@ -14,4 +14,5 @@
|
||||
<%= subnav_link_to "Disapprovals", post_disapprovals_path %>
|
||||
<%= subnav_link_to "Flags", post_flags_path %>
|
||||
<%= subnav_link_to "Replacements", post_replacements_path %>
|
||||
<%= subnav_link_to "Mod Actions", mod_actions_path(search: { subject_type: "Post" }) %>
|
||||
<% end %>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<%= search_form_for(post_events_path) do |f| %>
|
||||
<%= f.input :creator_name, label: "User", input_html: { value: params[:search][:creator_name], "data-autocomplete": "user" } %>
|
||||
<%= f.input :post_tags_match, label: "Tags", input_html: { value: params[:search][:post_tags_match], "data-autocomplete": "tag-query" } %>
|
||||
<%= f.input :model_type, label: "Category", collection: PostEvent.model_types.map { |type| [type.titleize.delete_prefix("Post "), type] }, include_blank: true, selected: params[:search][:model_type] %>
|
||||
<%= f.input :category, label: "Category", collection: PostEvent.categories.map { |category| [category.titleize, category] }, include_blank: true, selected: params[:search][:category] %>
|
||||
<%= f.input :order, collection: [%w[Newest event_at], %w[Oldest event_at_asc]], include_blank: true, selected: params[:search][:order] %>
|
||||
<%= f.submit "Search" %>
|
||||
<% end %>
|
||||
@@ -60,11 +60,38 @@
|
||||
(<%= external_link_to model.original_url.presence || "none", Source::URL.site_name(model.original_url) || model.original_url %> ->
|
||||
<%= external_link_to model.replacement_url, Source::URL.site_name(model.replacement_url) || model.replacement_url %>).
|
||||
<% end %>
|
||||
<% when "ModAction" %>
|
||||
<% case model.category %>
|
||||
<% when "post_ban" %>
|
||||
<%= link_to post.dtext_shortlink, post %> was banned by <%= link_to_user creator %>.
|
||||
<% when "post_unban" %>
|
||||
<%= link_to post.dtext_shortlink, post %> was unbanned by <%= link_to_user creator %>.
|
||||
<% when "post_delete" %>
|
||||
<%= link_to post.dtext_shortlink, post %> was deleted by <%= link_to_user creator %>.
|
||||
<% when "post_undelete" %>
|
||||
<%= link_to post.dtext_shortlink, post %> was undeleted by <%= link_to_user creator %>.
|
||||
<% when "post_regenerate" %>
|
||||
<%= link_to post.dtext_shortlink, post %> had its thumbnails regenerated by <%= link_to_user creator %>.
|
||||
<% when "post_regenerate_iqdb" %>
|
||||
<%= link_to post.dtext_shortlink, post %> was reindexed in IQDB by <%= link_to_user creator %>.
|
||||
<% when "post_rating_lock_create" %>
|
||||
<%= link_to post.dtext_shortlink, post %> was rating locked by <%= link_to_user creator %>.
|
||||
<% when "post_rating_lock_delete" %>
|
||||
<%= link_to post.dtext_shortlink, post %> was rating unlocked by <%= link_to_user creator %>.
|
||||
<% when "post_note_lock_create" %>
|
||||
<%= link_to post.dtext_shortlink, post %> was note locked by <%= link_to_user creator %>.
|
||||
<% when "post_note_lock_delete" %>
|
||||
<%= link_to post.dtext_shortlink, post %> was note unlocked by <%= link_to_user creator %>.
|
||||
<% else %>
|
||||
<div class="prose">
|
||||
<%= link_to_user creator %> <%= format_text(model.description.chomp(".").strip, inline: true) %>.
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% t.column "Category" do |event| %>
|
||||
<%= link_to event.model_type.titleize.delete_prefix("Post "), post_events_path(search: { model_type: event.model_type, **search_params }) %>
|
||||
<%= link_to event.pretty_category, post_events_path(search: { category: event.category, **search_params }) %>
|
||||
<% end %>
|
||||
|
||||
<% t.column "User" do |event| %>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class UpdatePostEventsToVersion2 < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
replace_view :post_events, version: 2, revert_to_version: 1
|
||||
end
|
||||
end
|
||||
@@ -1513,7 +1513,16 @@ UNION ALL
|
||||
post_replacements.post_id,
|
||||
post_replacements.creator_id,
|
||||
post_replacements.created_at AS event_at
|
||||
FROM public.post_replacements;
|
||||
FROM public.post_replacements
|
||||
UNION ALL
|
||||
( SELECT 'ModAction'::character varying AS model_type,
|
||||
mod_actions.id AS model_id,
|
||||
mod_actions.subject_id AS post_id,
|
||||
mod_actions.creator_id,
|
||||
mod_actions.created_at AS event_at
|
||||
FROM public.mod_actions
|
||||
WHERE ((mod_actions.subject_type)::text = 'Post'::text)
|
||||
ORDER BY mod_actions.created_at DESC);
|
||||
|
||||
|
||||
--
|
||||
@@ -6739,6 +6748,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20220922014326'),
|
||||
('20220923010905'),
|
||||
('20220924092056'),
|
||||
('20220925045236');
|
||||
('20220925045236'),
|
||||
('20220926050108');
|
||||
|
||||
|
||||
|
||||
23
db/views/post_events_v02.sql
Normal file
23
db/views/post_events_v02.sql
Normal file
@@ -0,0 +1,23 @@
|
||||
SELECT 'Post'::character varying AS model_type, id AS model_id, id AS post_id, uploader_id AS creator_id, created_at AS event_at
|
||||
FROM posts
|
||||
UNION ALL
|
||||
SELECT 'PostAppeal'::character varying, id, post_id, creator_id, created_at
|
||||
FROM post_appeals
|
||||
UNION ALL
|
||||
SELECT 'PostApproval'::character varying, id, post_id, user_id, created_at
|
||||
FROM post_approvals
|
||||
UNION ALL
|
||||
SELECT 'PostDisapproval'::character varying, id, post_id, user_id, created_at
|
||||
FROM post_disapprovals
|
||||
UNION ALL
|
||||
SELECT 'PostFlag'::character varying, id, post_id, creator_id, created_at
|
||||
FROM post_flags
|
||||
UNION ALL
|
||||
SELECT 'PostReplacement'::character varying, id, post_id, creator_id, created_at
|
||||
FROM post_replacements
|
||||
UNION ALL (
|
||||
SELECT 'ModAction'::character varying, id, subject_id, creator_id, created_at
|
||||
FROM mod_actions
|
||||
WHERE mod_actions.subject_type = 'Post'
|
||||
ORDER BY created_at DESC
|
||||
)
|
||||
@@ -13,6 +13,18 @@ class PostEventsControllerTest < ActionDispatch::IntegrationTest
|
||||
@appeal = create(:post_appeal, post: @post, creator: @user)
|
||||
@disapproval = create(:post_disapproval, post: @post, user: @user)
|
||||
@replacement = create(:post_replacement, post: @post, creator: @user)
|
||||
|
||||
create(:mod_action, category: :post_delete, description: "deleted post ##{@post.id}", subject: @post)
|
||||
create(:mod_action, category: :post_undelete, description: "undeleted post ##{@post.id}", subject: @post)
|
||||
create(:mod_action, category: :post_ban, description: "banned post ##{@post.id}", subject: @post)
|
||||
create(:mod_action, category: :post_unban, description: "unbanned post ##{@post.id}", subject: @post)
|
||||
create(:mod_action, category: :post_move_favorites, description: "moved favorites from post ##{@post.id} to post #1234", subject: @post)
|
||||
create(:mod_action, category: :post_regenerate, description: "regenerated post ##{@post.id}", subject: @post)
|
||||
create(:mod_action, category: :post_regenerate_iqdb, description: "regenerated IQDB for post ##{@post.id}", subject: @post)
|
||||
create(:mod_action, category: :post_note_lock_create, description: "locked notes for post ##{@post.id}", subject: @post)
|
||||
create(:mod_action, category: :post_note_lock_delete, description: "unlocked notes for post ##{@post.id}", subject: @post)
|
||||
create(:mod_action, category: :post_rating_lock_create, description: "locked rating for post ##{@post.id}", subject: @post)
|
||||
create(:mod_action, category: :post_rating_lock_delete, description: "unlocked ratineg for post ##{@post.id}", subject: @post)
|
||||
end
|
||||
|
||||
should "render for a global listing" do
|
||||
@@ -54,6 +66,11 @@ class PostEventsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_response :success
|
||||
assert_equal(@flag.creator_id, response.parsed_body.find { |event| event["model_type"] == "PostFlag" }["creator_id"])
|
||||
end
|
||||
|
||||
should respond_to_search(category: "Upload").with { PostEvent.find_by!(model: @post) }
|
||||
should respond_to_search(category: "Flag").with { PostEvent.find_by!(model: @flag) }
|
||||
should respond_to_search(category: "Delete").with { PostEvent.find_by!(model: ModAction.post_delete.first) }
|
||||
should respond_to_search(category: "Blah").with { [] }
|
||||
end
|
||||
|
||||
context "for a non-moderator" do
|
||||
@@ -72,6 +89,11 @@ class PostEventsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_response :success
|
||||
assert_nil(response.parsed_body.find { |event| event["model_type"] == "PostFlag" }["creator_id"])
|
||||
end
|
||||
|
||||
should respond_to_search(category: "Upload").with { PostEvent.find_by!(model: @post) }
|
||||
should respond_to_search(category: "Flag").with { PostEvent.find_by!(model: @flag) }
|
||||
should respond_to_search(category: "Delete").with { PostEvent.find_by!(model: ModAction.post_delete.first) }
|
||||
should respond_to_search(category: "Blah").with { [] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user