FEATURE - group modetators visual indicator (#10310)

FEATURE - group modetators visual indicator (#10310)

diff --git a/app/assets/javascripts/discourse/app/lib/transform-post.js b/app/assets/javascripts/discourse/app/lib/transform-post.js
index e2dd44b..efc1ac2 100644
--- a/app/assets/javascripts/discourse/app/lib/transform-post.js
+++ b/app/assets/javascripts/discourse/app/lib/transform-post.js
@@ -44,6 +44,7 @@ export function transformBasicPost(post) {
     staff: post.staff,
     admin: post.admin,
     moderator: post.moderator,
+    groupModerator: post.group_moderator,
     new_user: post.trust_level === 0,
     name: post.name,
     user_title: post.user_title,
diff --git a/app/assets/javascripts/discourse/app/widgets/post.js b/app/assets/javascripts/discourse/app/widgets/post.js
index 679f072..ae87f5c 100644
--- a/app/assets/javascripts/discourse/app/widgets/post.js
+++ b/app/assets/javascripts/discourse/app/widgets/post.js
@@ -675,6 +675,9 @@ export default createWidget("post", {
     if (attrs.topicOwner) {
       classNames.push("topic-owner");
     }
+    if (attrs.groupModerator) {
+      classNames.push("category-moderator");
+    }
     if (attrs.hidden) {
       classNames.push("post-hidden");
     }
diff --git a/app/assets/javascripts/discourse/app/widgets/poster-name.js b/app/assets/javascripts/discourse/app/widgets/poster-name.js
index 0445753..ebc12a8 100644
--- a/app/assets/javascripts/discourse/app/widgets/poster-name.js
+++ b/app/assets/javascripts/discourse/app/widgets/poster-name.js
@@ -44,7 +44,7 @@ export default createWidget("poster-name", {
 
   // TODO: Allow extensibility
   posterGlyph(attrs) {
-    if (attrs.moderator) {
+    if (attrs.moderator || attrs.groupModerator) {
       return iconNode("shield-alt", {
         title: I18n.t("user.moderator_tooltip")
       });
@@ -83,6 +83,9 @@ export default createWidget("poster-name", {
     if (attrs.moderator) {
       classNames.push("moderator");
     }
+    if (attrs.groupModerator) {
+      classNames.push("category-moderator");
+    }
     if (attrs.new_user) {
       classNames.push("new-user");
     }
diff --git a/app/serializers/post_serializer.rb b/app/serializers/post_serializer.rb
index 56ac4fe..12ca90e 100644
--- a/app/serializers/post_serializer.rb
+++ b/app/serializers/post_serializer.rb
@@ -60,6 +60,7 @@ class PostSerializer < BasicPostSerializer
              :moderator?,
              :admin?,
              :staff?,
+             :group_moderator,
              :user_id,
              :draft_sequence,
              :hidden,
@@ -140,6 +141,20 @@ class PostSerializer < BasicPostSerializer
     !!(object&.user&.staff?)
   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
     scope.user == object.user
   end
diff --git a/lib/guardian.rb b/lib/guardian.rb
index 9e31492..be5378e 100644
--- a/lib/guardian.rb
+++ b/lib/guardian.rb
@@ -93,6 +93,15 @@ class Guardian
     @user.moderator?
   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?
     @user.silenced?
   end
diff --git a/lib/guardian/topic_guardian.rb b/lib/guardian/topic_guardian.rb
index 5a0f9b5..60a7c37 100644
--- a/lib/guardian/topic_guardian.rb
+++ b/lib/guardian/topic_guardian.rb
@@ -17,10 +17,7 @@ module TopicGuardian
     return false if anonymous? || topic.nil?
     return true if is_staff?
 
-    SiteSetting.enable_category_group_moderation? &&
-      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?
+    is_category_group_moderator?(topic.category)
   end
 
   def can_create_shared_draft?
@@ -209,10 +206,7 @@ module TopicGuardian
     return true if is_staff?
     return true if @user.has_trust_level?(TrustLevel[4])
 
-    SiteSetting.enable_category_group_moderation? &&
-      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?
+    is_category_group_moderator?(topic.category)
   end
   alias :can_archive_topic? :can_perform_action_available_to_group_moderators?
   alias :can_close_topic? :can_perform_action_available_to_group_moderators?
diff --git a/lib/topic_view.rb b/lib/topic_view.rb
index 0601a59..0568e07 100644
--- a/lib/topic_view.rb
+++ b/lib/topic_view.rb
@@ -429,6 +429,19 @@ class TopicView
     @group_allowed_user_ids = Set.new(GroupUser.where(group_id: group_ids).pluck('distinct user_id'))
   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
     @all_post_actions ||= PostAction.counts_for(@posts, @user)
   end
diff --git a/spec/serializers/post_serializer_spec.rb b/spec/serializers/post_serializer_spec.rb
index 06ca8d4..b62c32e 100644
--- a/spec/serializers/post_serializer_spec.rb
+++ b/spec/serializers/post_serializer_spec.rb
@@ -258,6 +258,27 @@ describe PostSerializer do
     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)
     s = PostSerializer.new(post, scope: Guardian.new(u), root: false)
     s.add_raw = true
diff --git a/test/javascripts/acceptance/topic-test.js b/test/javascripts/acceptance/topic-test.js
index 4e4cb08..27c5e3b 100644
--- a/test/javascripts/acceptance/topic-test.js
+++ b/test/javascripts/acceptance/topic-test.js
@@ -228,6 +228,13 @@ QUnit.skip("Deleting a topic", async assert => {
   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", {
   loggedIn: true,
   settings: {
diff --git a/test/javascripts/fixtures/topic.js b/test/javascripts/fixtures/topic.js
index e4917aa..6b91b71 100644
--- a/test/javascripts/fixtures/topic.js
+++ b/test/javascripts/fixtures/topic.js
@@ -5075,5 +5075,343 @@ export default {
     message_bus_last_id: 7,
     participant_count: 2,
     pm_with_non_human_user: false
+  },
+  "/t/2480/1.json": {
+    "post_stream": {
+      "posts": [
+        {
+          "id": 41,
+          "name": "",
+          "username": "group_moderator",
+          "avatar_template": "/images/avatar.png",

[... diff too long, it was truncated ...]

GitHub sha: 74ab4f3b

This commit appears in #10310 which was approved by eviltrout. It was merged by jbrw.