FEATURE - group modetators visual indicator (#10310)

This commit is contained in:
jbrw 2020-07-28 17:15:04 -04:00 committed by GitHub
parent 01a3fa1ca8
commit 74ab4f3bff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 414 additions and 17 deletions

View File

@ -44,6 +44,7 @@ export function transformBasicPost(post) {
staff: post.staff, staff: post.staff,
admin: post.admin, admin: post.admin,
moderator: post.moderator, moderator: post.moderator,
groupModerator: post.group_moderator,
new_user: post.trust_level === 0, new_user: post.trust_level === 0,
name: post.name, name: post.name,
user_title: post.user_title, user_title: post.user_title,

View File

@ -675,6 +675,9 @@ export default createWidget("post", {
if (attrs.topicOwner) { if (attrs.topicOwner) {
classNames.push("topic-owner"); classNames.push("topic-owner");
} }
if (attrs.groupModerator) {
classNames.push("category-moderator");
}
if (attrs.hidden) { if (attrs.hidden) {
classNames.push("post-hidden"); classNames.push("post-hidden");
} }

View File

@ -44,7 +44,7 @@ export default createWidget("poster-name", {
// TODO: Allow extensibility // TODO: Allow extensibility
posterGlyph(attrs) { posterGlyph(attrs) {
if (attrs.moderator) { if (attrs.moderator || attrs.groupModerator) {
return iconNode("shield-alt", { return iconNode("shield-alt", {
title: I18n.t("user.moderator_tooltip") title: I18n.t("user.moderator_tooltip")
}); });
@ -83,6 +83,9 @@ export default createWidget("poster-name", {
if (attrs.moderator) { if (attrs.moderator) {
classNames.push("moderator"); classNames.push("moderator");
} }
if (attrs.groupModerator) {
classNames.push("category-moderator");
}
if (attrs.new_user) { if (attrs.new_user) {
classNames.push("new-user"); classNames.push("new-user");
} }

View File

@ -60,6 +60,7 @@ class PostSerializer < BasicPostSerializer
:moderator?, :moderator?,
:admin?, :admin?,
:staff?, :staff?,
:group_moderator,
:user_id, :user_id,
:draft_sequence, :draft_sequence,
:hidden, :hidden,
@ -140,6 +141,20 @@ class PostSerializer < BasicPostSerializer
!!(object&.user&.staff?) !!(object&.user&.staff?)
end end
def group_moderator
!!@group_moderator
end
def include_group_moderator?
@group_moderator ||= begin
if @topic_view
@topic_view.category_group_moderator_user_ids.include?(object.user_id)
else
object&.user&.guardian&.is_category_group_moderator?(object&.topic&.category)
end
end
end
def yours def yours
scope.user == object.user scope.user == object.user
end end

View File

@ -93,6 +93,15 @@ class Guardian
@user.moderator? @user.moderator?
end end
def is_category_group_moderator?(category)
@is_category_group_moderator ||= begin
SiteSetting.enable_category_group_moderation? &&
category.present? &&
category.reviewable_by_group_id.present? &&
GroupUser.where(group_id: category.reviewable_by_group_id, user_id: @user.id).exists?
end
end
def is_silenced? def is_silenced?
@user.silenced? @user.silenced?
end end

View File

@ -17,10 +17,7 @@ module TopicGuardian
return false if anonymous? || topic.nil? return false if anonymous? || topic.nil?
return true if is_staff? return true if is_staff?
SiteSetting.enable_category_group_moderation? && is_category_group_moderator?(topic.category)
topic.category.present? &&
topic.category.reviewable_by_group_id.present? &&
GroupUser.where(group_id: topic.category.reviewable_by_group_id, user_id: user.id).exists?
end end
def can_create_shared_draft? def can_create_shared_draft?
@ -209,10 +206,7 @@ module TopicGuardian
return true if is_staff? return true if is_staff?
return true if @user.has_trust_level?(TrustLevel[4]) return true if @user.has_trust_level?(TrustLevel[4])
SiteSetting.enable_category_group_moderation? && is_category_group_moderator?(topic.category)
topic.category.present? &&
topic.category.reviewable_by_group_id.present? &&
GroupUser.where(group_id: topic.category.reviewable_by_group_id, user_id: @user.id).exists?
end end
alias :can_archive_topic? :can_perform_action_available_to_group_moderators? alias :can_archive_topic? :can_perform_action_available_to_group_moderators?
alias :can_close_topic? :can_perform_action_available_to_group_moderators? alias :can_close_topic? :can_perform_action_available_to_group_moderators?

View File

@ -429,6 +429,19 @@ class TopicView
@group_allowed_user_ids = Set.new(GroupUser.where(group_id: group_ids).pluck('distinct user_id')) @group_allowed_user_ids = Set.new(GroupUser.where(group_id: group_ids).pluck('distinct user_id'))
end end
def category_group_moderator_user_ids
@category_group_moderator_user_ids ||= begin
if SiteSetting.enable_category_group_moderation? && @topic.category.reviewable_by_group.present?
posts_user_ids = Set.new(@posts.map(&:user_id))
Set.new(
@topic.category.reviewable_by_group.group_users.where(user_id: posts_user_ids).pluck('distinct user_id')
)
else
Set.new
end
end
end
def all_post_actions def all_post_actions
@all_post_actions ||= PostAction.counts_for(@posts, @user) @all_post_actions ||= PostAction.counts_for(@posts, @user)
end end

View File

@ -258,6 +258,27 @@ describe PostSerializer do
end end
end end
context "posts when group moderation is enabled" do
fab!(:topic) { Fabricate(:topic) }
fab!(:group_user) { Fabricate(:group_user) }
fab!(:post) { Fabricate(:post, topic: topic) }
before do
SiteSetting.enable_category_group_moderation = true
topic.category.update!(reviewable_by_group_id: group_user.group.id)
end
it "does nothing for regular users" do
expect(serialized_post_for_user(nil)[:group_moderator]).to eq(nil)
end
it "returns a group_moderator attribute for category group moderators" do
post.update!(user: group_user.user)
expect(serialized_post_for_user(nil)[:group_moderator]).to eq(true)
end
end
def serialized_post(u) def serialized_post(u)
s = PostSerializer.new(post, scope: Guardian.new(u), root: false) s = PostSerializer.new(post, scope: Guardian.new(u), root: false)
s.add_raw = true s.add_raw = true

View File

@ -228,6 +228,13 @@ QUnit.skip("Deleting a topic", async assert => {
assert.ok(exists(".widget-button.recover"), "it shows the recover button"); assert.ok(exists(".widget-button.recover"), "it shows the recover button");
}); });
QUnit.test("Group category moderator posts", async assert => {
await visit("/t/topic-for-group-moderators/2480");
assert.ok(exists(".category-moderator"), "it has a class applied");
assert.ok(exists(".d-icon-shield-alt"), "it shows an icon");
});
acceptance("Topic featured links", { acceptance("Topic featured links", {
loggedIn: true, loggedIn: true,
settings: { settings: {

View File

@ -5075,5 +5075,343 @@ export default {
message_bus_last_id: 7, message_bus_last_id: 7,
participant_count: 2, participant_count: 2,
pm_with_non_human_user: false pm_with_non_human_user: false
},
"/t/2480/1.json": {
"post_stream": {
"posts": [
{
"id": 41,
"name": "",
"username": "group_moderator",
"avatar_template": "/images/avatar.png",
"created_at": "2020-07-24T17:48:55.419Z",
"cooked": "<p>Here is my new topic. I am a group category moderator!</p>",
"post_number": 1,
"post_type": 1,
"updated_at": "2020-07-24T17:48:55.419Z",
"reply_count": 0,
"reply_to_post_number": null,
"quote_count": 0,
"incoming_link_count": 0,
"reads": 2,
"readers_count": 1,
"score": 0,
"yours": true,
"topic_id": 2480,
"topic_slug": "a-topic-with-group-category-moderators",
"display_username": "",
"primary_group_name": "group_moderators",
"primary_group_flair_url": "cheese",
"primary_group_flair_bg_color": "ff0",
"primary_group_flair_color": "",
"version": 1,
"can_edit": true,
"can_delete": false,
"can_recover": false,
"can_wiki": false,
"read": true,
"user_title": "a title",
"title_is_group": false,
"bookmarked": false,
"actions_summary": [
{
"id": 3,
"can_act": true
},
{
"id": 4,
"can_act": true
},
{
"id": 8,
"can_act": true
},
{
"id": 7,
"can_act": true
}
],
"moderator": false,
"admin": false,
"staff": false,
"group_moderator": true,
"user_id": 3,
"hidden": false,
"trust_level": 1,
"deleted_at": null,
"user_deleted": false,
"edit_reason": null,
"can_view_edit_history": true,
"wiki": false,
"reviewable_id": 0,
"reviewable_score_count": 0,
"reviewable_score_pending_count": 0
},
{
"id": 42,
"name": "",
"username": "normal_user",
"avatar_template": "/images/avatar.png",
"created_at": "2020-07-24T17:50:01.263Z",
"cooked": "<p>A fascinating topic worthy of discussion.</p>",
"post_number": 2,
"post_type": 1,
"updated_at": "2020-07-24T17:50:01.263Z",
"reply_count": 0,
"reply_to_post_number": null,
"quote_count": 0,
"incoming_link_count": 0,
"reads": 2,
"readers_count": 1,
"score": 0,
"yours": false,
"topic_id": 2480,
"topic_slug": "a-topic-with-group-category-moderators",
"display_username": "",
"primary_group_name": null,
"primary_group_flair_url": null,
"primary_group_flair_bg_color": null,
"primary_group_flair_color": null,
"version": 1,
"can_edit": false,
"can_delete": false,
"can_recover": false,
"can_wiki": false,
"read": true,
"user_title": null,
"bookmarked": false,
"actions_summary": [
{
"id": 2,
"can_act": true
},
{
"id": 3,
"can_act": true
},
{
"id": 4,
"can_act": true
},
{
"id": 8,
"can_act": true
},
{
"id": 6,
"can_act": true
},
{
"id": 7,
"can_act": true
}
],
"moderator": false,
"admin": false,
"staff": false,
"user_id": 2,
"hidden": false,
"trust_level": 1,
"deleted_at": null,
"user_deleted": false,
"edit_reason": null,
"can_view_edit_history": true,
"wiki": false,
"reviewable_id": 0,
"reviewable_score_count": 0,
"reviewable_score_pending_count": 0
},
{
"id": 43,
"name": "",
"username": "group_moderator",
"avatar_template": "/images/avatar.png",
"created_at": "2020-07-24T17:50:17.274Z",
"cooked": "<p>Thank you for your reply!</p>",
"post_number": 3,
"post_type": 1,
"updated_at": "2020-07-24T17:50:17.274Z",
"reply_count": 0,
"reply_to_post_number": null,
"quote_count": 0,
"incoming_link_count": 0,
"reads": 2,
"readers_count": 1,
"score": 0,
"yours": true,
"topic_id": 2480,
"topic_slug": "a-topic-with-group-category-moderators",
"display_username": "",
"primary_group_name": "group_moderators",
"primary_group_flair_url": "cheese",
"primary_group_flair_bg_color": "ff0",
"primary_group_flair_color": "",
"version": 1,
"can_edit": true,
"can_delete": true,
"can_recover": false,
"can_wiki": false,
"read": true,
"user_title": "a title",
"title_is_group": false,
"bookmarked": false,
"actions_summary": [
{
"id": 3,
"can_act": true
},
{
"id": 4,
"can_act": true
},
{
"id": 8,
"can_act": true
},
{
"id": 7,
"can_act": true
}
],
"moderator": false,
"admin": false,
"staff": false,
"group_moderator": true,
"user_id": 3,
"hidden": false,
"trust_level": 1,
"deleted_at": null,
"user_deleted": false,
"edit_reason": null,
"can_view_edit_history": true,
"wiki": false,
"reviewable_id": 0,
"reviewable_score_count": 0,
"reviewable_score_pending_count": 0
}
],
"stream": [
41,
42,
43
]
},
"timeline_lookup": [
[
1,
0
]
],
"id": 2480,
"title": "A Topic with Group Category Moderators",
"fancy_title": "A Topic with Group Category Moderators",
"posts_count": 3,
"created_at": "2020-07-24T17:48:54.986Z",
"views": 2,
"reply_count": 0,
"like_count": 0,
"last_posted_at": "2020-07-24T17:50:17.274Z",
"visible": true,
"closed": false,
"archived": false,
"has_summary": false,
"archetype": "regular",
"slug": "a-topic-with-group-category-moderators",
"category_id": 5,
"word_count": 22,
"deleted_at": null,
"user_id": 3,
"featured_link": null,
"pinned_globally": false,
"pinned_at": null,
"pinned_until": null,
"image_url": null,
"draft": null,
"draft_key": "topic_2480",
"draft_sequence": 1,
"posted": true,
"unpinned": null,
"pinned": false,
"current_post_number": 3,
"highest_post_number": 3,
"last_read_post_number": 3,
"last_read_post_id": 43,
"deleted_by": null,
"actions_summary": [
{
"id": 4,
"count": 0,
"hidden": false,
"can_act": true
},
{
"id": 8,
"count": 0,
"hidden": false,
"can_act": true
},
{
"id": 7,
"count": 0,
"hidden": false,
"can_act": true
}
],
"chunk_size": 20,
"bookmarked": false,
"topic_timer": null,
"private_topic_timer": null,
"message_bus_last_id": 4,
"participant_count": 2,
"show_read_indicator": false,
"thumbnails": null,
"details": {
"notification_level": 3,
"notifications_reason_id": 1,
"can_edit": true,
"can_create_post": true,
"can_reply_as_new_topic": true,
"can_flag_topic": true,
"can_review_topic": true,
"can_close_topic": true,
"can_archive_topic": true,
"can_edit_staff_notes": true,
"participants": [
{
"id": 3,
"username": "group_moderator",
"name": "",
"avatar_template": "/images/avatar.png",
"post_count": 2,
"primary_group_name": "group_moderators",
"primary_group_flair_url": "cheese",
"primary_group_flair_color": "",
"primary_group_flair_bg_color": "ff0"
},
{
"id": 2,
"username": "normal_user",
"name": "",
"avatar_template": "/images/avatar.png",
"post_count": 1,
"primary_group_name": null,
"primary_group_flair_url": null,
"primary_group_flair_color": null,
"primary_group_flair_bg_color": null
}
],
"created_by": {
"id": 3,
"username": "group_moderator",
"name": "",
"avatar_template": "/images/avatar.png"
},
"last_poster": {
"id": 3,
"username": "group_moderator",
"name": "",
"avatar_template": "/images/avatar.png"
}
}
} }
}; };

View File

@ -255,14 +255,7 @@ export function applyDefaultHandlers(pretender) {
pretender.get("/t/12.json", () => response(fixturesByUrl["/t/12/1.json"])); pretender.get("/t/12.json", () => response(fixturesByUrl["/t/12/1.json"]));
pretender.put("/t/1234/re-pin", success); pretender.put("/t/1234/re-pin", success);
pretender.get("/t/2480.json", () => { pretender.get("/t/2480.json", () => response(fixturesByUrl["/t/2480/1.json"]));
const json = fixturesByUrl["/t/34/1.json"];
json.details.can_archive_topic = true;
json.details.can_close_topic = true;
json.details.can_edit_staff_notes = true;
return response(json);
});
pretender.get("/t/id_for/:slug", () => { pretender.get("/t/id_for/:slug", () => {
return response({ return response({