mirror of
https://github.com/discourse/discourse.git
synced 2025-02-17 01:12:45 +08:00
FEATURE: Add post edits count to user activity (#13495)
This commit is contained in:
parent
7b56325f89
commit
e7b8e75583
|
@ -121,6 +121,11 @@ export default Controller.extend(CanCheckEmails, {
|
|||
}
|
||||
},
|
||||
|
||||
@discourseComputed("model.username")
|
||||
postEditsByEditorFilter(username) {
|
||||
return { editor: username };
|
||||
},
|
||||
|
||||
groupAdded(added) {
|
||||
this.model
|
||||
.groupAdded(added)
|
||||
|
|
|
@ -641,6 +641,20 @@
|
|||
<div class="field">{{i18n "user.invited.days_visited"}}</div>
|
||||
<div class="value">{{html-safe model.days_visited}}</div>
|
||||
</div>
|
||||
<div class="display-row post-edits-count">
|
||||
<div class="field">{{i18n "admin.user.post_edits_count" }}</div>
|
||||
<div class="value">
|
||||
{{if (gt model.post_edits_count 0) model.post_edits_count "0"}}
|
||||
</div>
|
||||
<div class="controls">
|
||||
{{#if (gt model.post_edits_count 0) }}
|
||||
{{#link-to "adminReports.show" "post_edits" (query-params filters=postEditsByEditorFilter) class="btn btn-icon"}}
|
||||
{{d-icon "far-eye"}}
|
||||
{{i18n "admin.user.view_edits"}}
|
||||
{{/link-to}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{#if model.single_sign_on_record}}
|
||||
|
|
|
@ -3,7 +3,7 @@ import {
|
|||
exists,
|
||||
queryAll,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
import { click, fillIn, visit } from "@ember/test-helpers";
|
||||
import { click, currentURL, fillIn, visit } from "@ember/test-helpers";
|
||||
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
||||
import { test } from "qunit";
|
||||
|
||||
|
@ -63,6 +63,40 @@ acceptance("Admin - User Index", function (needs) {
|
|||
);
|
||||
});
|
||||
|
||||
test("shows the number of post edits", async function (assert) {
|
||||
await visit("/admin/users/1/eviltrout");
|
||||
|
||||
assert.equal(queryAll(".post-edits-count .value").text().trim(), "6");
|
||||
|
||||
assert.ok(
|
||||
exists(".post-edits-count .controls .btn.btn-icon"),
|
||||
"View edits button exists"
|
||||
);
|
||||
});
|
||||
|
||||
test("a link to view post edits report exists", async function (assert) {
|
||||
await visit("/admin/users/1/eviltrout");
|
||||
|
||||
let filter = encodeURIComponent('{"editor":"eviltrout"}');
|
||||
|
||||
await click(".post-edits-count .controls .btn.btn-icon");
|
||||
|
||||
assert.equal(
|
||||
currentURL(),
|
||||
`/admin/reports/post_edits?filters=${filter}`,
|
||||
"it redirects to the right admin report"
|
||||
);
|
||||
});
|
||||
|
||||
test("hides the 'view Edits' button if the count is zero", async function (assert) {
|
||||
await visit("/admin/users/2/sam");
|
||||
|
||||
assert.ok(
|
||||
!exists(".post-edits-count .controls .btn.btn-icon"),
|
||||
"View Edits button not present"
|
||||
);
|
||||
});
|
||||
|
||||
test("will clear unsaved groups when switching user", async function (assert) {
|
||||
await visit("/admin/users/2/sam");
|
||||
|
||||
|
|
|
@ -746,6 +746,7 @@ export function applyDefaultHandlers(pretender) {
|
|||
username: "eviltrout",
|
||||
email: "eviltrout@example.com",
|
||||
admin: true,
|
||||
post_edits_count: 6,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -166,6 +166,17 @@ discourseModule("Integration | Component | admin-report", function (hooks) {
|
|||
},
|
||||
});
|
||||
|
||||
componentTest("post edits", {
|
||||
template: hbs`{{admin-report dataSourceName='post_edits'}}`,
|
||||
|
||||
test(assert) {
|
||||
assert.ok(
|
||||
exists(".admin-report.post-edits"),
|
||||
"it displays the post edits report"
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
componentTest("not found", {
|
||||
template: hbs`{{admin-report dataSourceName='not_found'}}`,
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Customise area
|
||||
// Customize area
|
||||
|
||||
.email-template {
|
||||
input {
|
||||
|
|
|
@ -6,6 +6,7 @@ module Reports::PostEdits
|
|||
class_methods do
|
||||
def report_post_edits(report)
|
||||
category_id, include_subcategories = report.add_category_filter
|
||||
editor_username = report.filters['editor']
|
||||
|
||||
report.modes = [:table]
|
||||
|
||||
|
@ -89,7 +90,12 @@ module Reports::PostEdits
|
|||
end
|
||||
end
|
||||
|
||||
builder.where("editor.id > 0 AND editor.id != author.id")
|
||||
if editor_username
|
||||
builder.where("editor.username = ?", editor_username)
|
||||
else
|
||||
builder.where("editor.id > 0 AND editor.id != author.id")
|
||||
end
|
||||
|
||||
builder.where("pr.created_at >= :start_date", start_date: report.start_date)
|
||||
builder.where("pr.created_at <= :end_date", end_date: report.end_date)
|
||||
|
||||
|
|
|
@ -59,4 +59,4 @@ end
|
|||
#
|
||||
# index_post_revisions_on_post_id (post_id)
|
||||
# index_post_revisions_on_post_id_and_number (post_id,number)
|
||||
#
|
||||
# index_post_revisions_on_user_id (user_id)
|
||||
|
|
|
@ -433,7 +433,6 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def created_topic_count
|
||||
stat = user_stat || create_user_stat
|
||||
stat.topic_count
|
||||
end
|
||||
|
||||
|
@ -894,10 +893,17 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def post_count
|
||||
stat = user_stat || create_user_stat
|
||||
stat.post_count
|
||||
end
|
||||
|
||||
def post_edits_count
|
||||
stat.post_edits_count
|
||||
end
|
||||
|
||||
def increment_post_edits_count
|
||||
stat.increment!(:post_edits_count)
|
||||
end
|
||||
|
||||
def flags_given_count
|
||||
PostAction.where(user_id: id, post_action_type_id: PostActionType.flag_types_without_custom.values).count
|
||||
end
|
||||
|
@ -1468,9 +1474,7 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def create_user_stat
|
||||
stat = UserStat.new(new_since: Time.now)
|
||||
stat.user_id = id
|
||||
stat.save!
|
||||
UserStat.create!(new_since: Time.zone.now, user_id: id)
|
||||
end
|
||||
|
||||
def create_user_option
|
||||
|
@ -1659,6 +1663,10 @@ class User < ActiveRecord::Base
|
|||
|
||||
private
|
||||
|
||||
def stat
|
||||
user_stat || create_user_stat
|
||||
end
|
||||
|
||||
def trigger_user_automatic_group_refresh
|
||||
if !staged
|
||||
Group.user_trust_level_change!(id, trust_level)
|
||||
|
|
|
@ -321,4 +321,4 @@ end
|
|||
# first_unread_pm_at :datetime not null
|
||||
# digest_attempted_at :datetime
|
||||
# draft_count :integer default(0), not null
|
||||
#
|
||||
# post_edits_count :integer
|
||||
|
|
|
@ -12,6 +12,7 @@ class AdminDetailedUserSerializer < AdminUserSerializer
|
|||
:like_given_count,
|
||||
:post_count,
|
||||
:topic_count,
|
||||
:post_edits_count,
|
||||
:flags_given_count,
|
||||
:flags_received_count,
|
||||
:private_topics_count,
|
||||
|
|
|
@ -14,6 +14,8 @@ group:
|
|||
post:
|
||||
include_images: false
|
||||
max_likes_count: 10
|
||||
post_revisions:
|
||||
count: 50
|
||||
tag:
|
||||
count: 30
|
||||
topic:
|
||||
|
|
|
@ -4855,6 +4855,8 @@ en:
|
|||
delete_all_posts: "Delete all posts"
|
||||
delete_posts_progress: "Deleting posts..."
|
||||
delete_posts_failed: "There was a problem deleting the posts."
|
||||
post_edits: "Post Edits"
|
||||
view_edits: "View Edits"
|
||||
penalty_post_actions: "What would you like to do with the associated post?"
|
||||
penalty_post_delete: "Delete the post"
|
||||
penalty_post_delete_replies: "Delete the post + any replies"
|
||||
|
@ -4913,6 +4915,7 @@ en:
|
|||
approve_success: "User approved and email sent with activation instructions."
|
||||
approve_bulk_success: "Success! All selected users have been approved and notified."
|
||||
time_read: "Read Time"
|
||||
post_edits_count: "Post Edits"
|
||||
anonymize: "Anonymize User"
|
||||
anonymize_confirm: "Are you SURE you want to anonymize this account? This will change the username and email, and reset all profile information."
|
||||
anonymize_yes: "Yes, anonymize this account"
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddUserIdIndexToPostRevisions < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
add_index :post_revisions, :user_id
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPostEditsCountToUserStats < ActiveRecord::Migration[6.1]
|
||||
disable_ddl_transaction!
|
||||
BATCH_SIZE = 30_000
|
||||
|
||||
def up
|
||||
add_column :user_stats, :post_edits_count, :integer
|
||||
|
||||
loop do
|
||||
count = DB.exec(<<~SQL, batch_size: BATCH_SIZE)
|
||||
UPDATE user_stats us
|
||||
SET post_edits_count = editor.edits_count
|
||||
FROM (
|
||||
SELECT COUNT(editor.id) AS edits_count, editor.id AS id
|
||||
FROM post_revisions pr JOIN users editor ON editor.id = pr.user_id
|
||||
JOIN user_stats us ON us.user_id = editor.id
|
||||
WHERE us.post_edits_count IS NULL AND pr.user_id IS NOT NULL
|
||||
GROUP BY editor.id
|
||||
LIMIT :batch_size
|
||||
) editor
|
||||
WHERE editor.id = us.user_id;
|
||||
SQL
|
||||
break if count == 0
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
raise ActiveRecord::IrreversibleMigration
|
||||
end
|
||||
end
|
|
@ -106,5 +106,9 @@ module DiscourseDev
|
|||
puts "Done!"
|
||||
end
|
||||
|
||||
def self.random
|
||||
super(::Post)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
23
lib/discourse_dev/post_revision.rb
Normal file
23
lib/discourse_dev/post_revision.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'discourse_dev/record'
|
||||
require 'faker'
|
||||
|
||||
module DiscourseDev
|
||||
class PostRevision < Record
|
||||
|
||||
def initialize
|
||||
super(::PostRevision, DiscourseDev.config.post_revisions[:count])
|
||||
end
|
||||
|
||||
def create!
|
||||
data = { raw: Faker::DiscourseMarkdown.sandwich(sentences: 5) }
|
||||
|
||||
::PostRevisor.new(Post.random).revise!(User.random, data)
|
||||
end
|
||||
|
||||
def populate!
|
||||
@count.times { create! }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -415,6 +415,8 @@ class PostRevisor
|
|||
@post.link_post_uploads
|
||||
@post.save_reply_relationships
|
||||
|
||||
@editor.increment_post_edits_count if @post_successfully_saved
|
||||
|
||||
# post owner changed
|
||||
if prev_owner && new_owner && prev_owner != new_owner
|
||||
likes = UserAction.where(target_post_id: @post.id)
|
||||
|
|
|
@ -25,6 +25,11 @@ task 'topics:populate' => ['db:load_config'] do |_, args|
|
|||
DiscourseDev::Topic.populate!
|
||||
end
|
||||
|
||||
desc 'Create post revisions'
|
||||
task 'post_revisions:populate' => ['db:load_config'] do |_, args|
|
||||
DiscourseDev::PostRevision.populate!
|
||||
end
|
||||
|
||||
desc 'Add replies to a topic'
|
||||
task 'replies:populate', [:topic_id, :count] => ['db:load_config'] do |_, args|
|
||||
DiscourseDev::Post.add_replies!(args)
|
||||
|
|
|
@ -606,6 +606,12 @@ describe PostRevisor do
|
|||
expect(post.topic.word_count).to eq(5)
|
||||
end
|
||||
|
||||
it 'increases the post_edits stat count' do
|
||||
expect do
|
||||
subject.revise!(post.user, { raw: "This is a new revision" })
|
||||
end.to change { post.user.user_stat.post_edits_count.to_i }.by(1)
|
||||
end
|
||||
|
||||
context 'second poster posts again quickly' do
|
||||
|
||||
it 'is a grace period edit, because the second poster posted again quickly' do
|
||||
|
|
|
@ -578,6 +578,37 @@ describe Report do
|
|||
expect(row[:topic_id]).to eq(post.topic.id)
|
||||
end
|
||||
end
|
||||
|
||||
context "with editor filter" do
|
||||
fab!(:posts) { Fabricate.times(3, :post) }
|
||||
|
||||
fab!(:editor_with_two_edits) do
|
||||
Fabricate(:user).tap do |user|
|
||||
2.times do |i|
|
||||
posts[i].revise(user, { raw: "edit #{i + 1}" })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fab!(:editor_with_one_edit) do
|
||||
Fabricate(:user).tap do |user|
|
||||
posts.last.revise(user, { raw: "edit 3" })
|
||||
end
|
||||
end
|
||||
|
||||
let(:report_with_one_edit) do
|
||||
Report.find('post_edits', { filters: { 'editor' => editor_with_one_edit.username } })
|
||||
end
|
||||
|
||||
let(:report_with_two_edits) do
|
||||
Report.find('post_edits', { filters: { 'editor' => editor_with_two_edits.username } })
|
||||
end
|
||||
|
||||
it "returns a report for a given editor" do
|
||||
expect(report_with_one_edit.data.count).to be(1)
|
||||
expect(report_with_two_edits.data.count).to be(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'moderator activity' do
|
||||
|
|
|
@ -147,6 +147,12 @@
|
|||
"silence_reason": {
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"post_edits_count": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"primary_group_id": {
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue
Block a user